Skip to content

Instantly share code, notes, and snippets.

Show Gist options
  • Save kccheung/70921b9dbc610c1c7744 to your computer and use it in GitHub Desktop.
Save kccheung/70921b9dbc610c1c7744 to your computer and use it in GitHub Desktop.
Prerequisitie:
Ruby 2.0.0-p481
Rails 4.1.6
[Step 1]
- rails new wids2015
[Step 2] Add following lines to Gemfile
gem 'devise'
gem 'rails_admin'
gem 'grape', '~> 0.6.1'
gem 'grape-entity', '~> 0.4.0'
gem 'rack-contrib', '~> 1.1.0'
gem 'grape-swagger', '~> 0.7.2'
gem 'grape-swagger-rails', '~> 0.0.8'
[Step 3]
- bundle install
- rails g devise:install
- rails g devise User
- rails g model Watch name:string user:references
[Step 4] Edit app/models/user.rb
has_many :watches
[Step 5]
- rake db:migrate
- rails g rails_admin:install
[Step 6] Uncomment config/initializer/rails_admin.rb
config.authenticate_with do
warden.authenticate! scope: :user
end
config.current_user_method(&:current_user)
[Step 7]
- rails s
------------------------------------------------------------------------------
You will be able to work with rails_admin and devise now.
http://localhost:3000/admin
------------------------------------------------------------------------------
[Step 8]
- rails g model Token secret_id:string hashed_secret:string expires_at:datetime user:references
[Step 9] add after create table db/migrate/[date]_create_tokens
- add_index :tokens, :secret_id, name: "index_tokens_secret_id", unique: true
[Step 10]
- rake db:migrate
[Step 11] Edit app/models/token.rb
before_create :generate_secret
attr_accessor :secret
def expired?
DateTime.now >= self.expires_at
end
def self.find_authenticated secret_id, secret
token = where(secret_id: secret_id).first
token if token && token.has_secret?(secret)
end
def has_secret? secret
BCrypt::Password.new(self.hashed_secret) == secret
end
private
def generate_secret
begin
self.secret_id = SecureRandom.hex 8
end while Token.exists?(secret_id: self.secret_id)
@secret = SecureRandom.urlsafe_base64 32
self.hashed_secret = BCrypt::Password.create secret, cost: 10
self.expires_at = DateTime.now + 30.days
end
[Step 12] Edit app/models/user.rb
def generate_authentication_token
Token.create(user: self)
end
[Step 13] Add lib/api/entities.rb
module API
module Entities
class EntityToken < Grape::Entity
expose :secret_id
expose :secret
expose :created_at
expose :updated_at
end
end
end
[Step 14] Add lib/api/entities.rb
module API
module Entities
class EntityWatch < Grape::Entity
expose :id
expose :name
expose :created_at
expose :updated_at
end
end
end
[Step 15] Add lib/api/v1/users.rb
module API
module V1
class Users < Grape::API
version 'v1'
format :json
resource :login do
desc "Return logged in user with access token"
params do
requires :email, type: String, desc: "Email"
requires :password, type: String, desc: "Password"
end
get do
user = User.find_by_email(params[:email].downcase)
if user && user.valid_password?(params[:password])
token = user.generate_authentication_token
user.sign_in_count += 1
if user.save
present :token, token, with: API::Entities::EntityToken
end
else
error!(I18n.t("devise.failure.invalid", :authentication_keys => Devise.authentication_keys.join(", ")), 403)
end
end
end
resource :watches do
desc "Return list of watches"
params do
requires :secret_id, type: String, desc: "Secret id"
requires :secret, type: String, desc: "Secret"
end
get do
authenticate!
present :watches, @user.watches, with: API::Entities::EntityWatch, size: params[:size]
end
desc "Add a watch"
params do
requires :secret_id, type: String, desc: "Secret id"
requires :secret, type: String, desc: "Secret"
optional :name, type: String, desc: "Name of watch"
end
get 'add' do
authenticate!
watch = Watch.create!(name: params[:name], user: @user)
watch.save!
present :watch, watch, with: API::Entities::EntityWatch, size: params[:size]
end
end
end
end
end
[Step 16] Add lib/v1/root.rb
module API
module V1
class Root < Grape::API
mount API::V1::Users
end
end
end
[Step 17] Add lib/api/root.rb
require 'grape-swagger'
module API
class Root < Grape::API
helpers do
def authenticate!
error!({message: I18n.t("devise.errors.messages.invalid_token"), code: -90001}, 403) unless current_user
end
def current_user
@user || find_user_by_secret
end
private
def find_user_by_secret
secret_id = params[:secret_id].presence
secret = params[:secret].presence
token = secret_id && secret && Token.find_authenticated(secret_id, secret)
@user = token.user if token
return @user
end
end
mount API::V1::Root
end
end
[Step 18] Edit config/locales/devise.en.yml for devise.errors.messages.invalid_token
invalid_token: "Invalid Token."
[Step 19] Edit config/initializer/rails_admin.rb
Rails.application.eager_load!
config.included_models = ActiveRecord::Base.descendants.map!(&:name)
[Step 20] Edit config/application.rb
config.autoload_paths += %W(#{config.root}/lib)
config.autoload_paths += Dir["#{config.root}/lib/**/"]
[Step 21] Edit config/routes.rb
mount API::Root => '/api', as: 'api'
------------------------------------------------------------------------------
You will be able to access the api now.
http://localhost:3000/api/v1/login.json?email=[your email]&password=[your password]
------------------------------------------------------------------------------
[Step 22] Add config/initializer/grape_swagger_rails.rb
require 'grape-swagger-rails'
GrapeSwaggerRails.options.url = "/api/swagger_doc.json"
GrapeSwaggerRails.options.app_name = 'wids2015'
GrapeSwaggerRails.options.app_url = '/'
[Step 23] Edit config/routes.rb
mount GrapeSwaggerRails::Engine => '/apidoc'
[Step 24] Edit lib/api/root.rb
add_swagger_documentation(
base_path: "/api",
api_version: "v1",
hide_documentation_path: true
)
------------------------------------------------------------------------------
You will be able to access the api doc now.
http://localhost:3000/apidoc
------------------------------------------------------------------------------
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment