Skip to content

Instantly share code, notes, and snippets.

@jeffrwells
Last active August 29, 2015 14:07
Show Gist options
  • Save jeffrwells/6d45cb050a0f650eca85 to your computer and use it in GitHub Desktop.
Save jeffrwells/6d45cb050a0f650eca85 to your computer and use it in GitHub Desktop.
Incoming API setup
class Api::V1::BaseController < ApplicationController
respond_to :json
before_action :authenticate! #make this the default, then you could use skip_before_action on an action you don't need it
def authenticate!
load_account || access_denied
end
def access_denied
head :unauthorized and return
end
def not_found
raise ActionController::RoutingError.new('Not Found') # this will trigger 404 - acts as if no route matched
end
def api_account
@api_account ||= find_api_account!
end
def find_api_account!
User.find_by!(api_key: (params[:api_key] || request.headers['api_key']) )
# find_by! will trigger 404 also if not found
end
end
class Api::V1::UsersController < Api::V1::BaseController
# GET /api/v1/user/3
def show
@user = api_account.users.find(params[:id])
render json: @user.as_json(root: true, only: [:has_iphone, :and, :other, :allowable, :attributes]
end
def create
@user = Api::User.create!(api_account, params.require(:user)) #delegate this to lib, don't do it in controller
render json @user.as_json #etc...
# you can start to see why active_model_serializer will keep your code dry.
# All you need to do is define the serializer and then call render json: @user, root: true (I think)
end
#...
end
module Api::User
#note that it isn't a class
def create!(account, user_params)
user = account.find_or_initialize_by(email: user_params['email']) #email or whatever your pniqueness is
# or just User.new, up to you
#note scoped to account ^. this will autofill account_id on the new user
user.approved_attr = user_params['approved_attr']
#etc etc
user.data = user_params
user.save! #would use bang here to bubble up to response throwing a 500 if it fails
return user
end
end
require 'rails_helper'
describe Api::User do
let (:subject) { Api::User }
describe '.create!' do
let(:account} { Fabricate :account }
let(:params) { {name: 'bob', availability: {monday: true}} }
it 'returns a user' do
expect(subject.create!(account, params)).to be_a(User)
end
it 'saves approved attributes to the user' do
user = subject.create!(account, params)
expect(user.name).to eq('bob')
end
it 'stores arbitrary attributes to the data column' do
user = subject.create!(account, params)
expect(user.data).to eq(params)
end
it 'throws an error if the user doesn't save' do
user_with_missing_necessary_attribute = User.new
allow(account).to receieve(:find_or_initialize_by).and_return(user_with_missing_necessary_attribute)
expect {
user = subject.create!(account, params)
}.to raise_error
end
end
end
@kibaekr
Copy link

kibaekr commented Oct 2, 2014

In the lib/api/user.rb,

If the email already exists, then we would just want to break out instead of saving right? Since any random user could submit a form with someone else's email, and their existing data could change?

@kibaekr
Copy link

kibaekr commented Oct 2, 2014

@jeffrwells is it fine that the controller file name is "class Api::V1::BaseController < ApplicationController"
instead of

module Api
module V1
class BaseController < ApplicationController
end
end
end

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment