trax_controller 0.0.3 → 0.0.4

Sign up to get free protection for your applications and to get access to all the features.
Files changed (36) hide show
  1. checksums.yaml +4 -4
  2. data/Gemfile +1 -1
  3. data/Rakefile +0 -1
  4. data/lib/trax/controller/actions.rb +1 -1
  5. data/lib/trax/controller/authorization/pundit/actions/create.rb +18 -0
  6. data/lib/trax/controller/authorization/pundit/actions/destroy.rb +18 -0
  7. data/lib/trax/controller/authorization/pundit/actions/index.rb +18 -0
  8. data/lib/trax/controller/authorization/pundit/actions/new.rb +18 -0
  9. data/lib/trax/controller/authorization/pundit/actions/read.rb +16 -0
  10. data/lib/trax/controller/authorization/pundit/actions/search.rb +18 -0
  11. data/lib/trax/controller/authorization/pundit/actions/show.rb +18 -0
  12. data/lib/trax/controller/authorization/pundit/actions/update.rb +19 -0
  13. data/lib/trax/controller/authorization/pundit/actions.rb +20 -0
  14. data/lib/trax/controller/authorization/pundit/adapter.rb +43 -0
  15. data/lib/trax/controller/authorization/pundit/base_policy.rb +18 -0
  16. data/lib/trax/controller/authorization/pundit.rb +13 -0
  17. data/lib/trax/controller/authorization.rb +9 -0
  18. data/lib/trax/controller/authorize.rb +24 -0
  19. data/lib/trax/controller/config.rb +13 -0
  20. data/lib/trax/controller/serialization/adapters/json.rb +14 -14
  21. data/lib/trax/controller.rb +14 -1
  22. data/lib/trax_controller/version.rb +1 -1
  23. data/spec/internal/app/controllers/widgets_controller.rb +22 -0
  24. data/spec/internal/app/models/category.rb +3 -0
  25. data/spec/internal/app/models/user.rb +3 -0
  26. data/spec/internal/app/policies/widget_policy.rb +26 -0
  27. data/spec/internal/config/routes.rb +1 -0
  28. data/spec/internal/db/combustion_test.sqlite +0 -0
  29. data/spec/internal/db/schema.rb +8 -1
  30. data/spec/spec_helper.rb +7 -0
  31. data/spec/trax/controller/authorization/pundit/base_policy_spec.rb +43 -0
  32. data/spec/trax/controller/authorization/pundit_spec.rb +179 -0
  33. data/spec/trax/controllers/products_controller_spec.rb +0 -2
  34. data/spec/trax/serialization/adapters/json_spec.rb +55 -0
  35. data/trax_controller.gemspec +2 -3
  36. metadata +44 -17
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA1:
3
- metadata.gz: bdbaf7390613351761a1a3babb39b491bcdf7b1d
4
- data.tar.gz: ea407ce95d7f6a417606e8ea8a022bc4f49ce392
3
+ metadata.gz: 0e4adb200e79b39ccad802b45128fa422f496406
4
+ data.tar.gz: fb0dcf577b599ad791f87fc840875c94a156d2a4
5
5
  SHA512:
6
- metadata.gz: 98358ac5bc8959c7abd30f3c8a80b1eeeeba40a25944997ee70e70a864940b4d6a35bd61b8304be3b3c730b740cbb7f6e7a209a04ed6313e9d63b1f040b3ff49
7
- data.tar.gz: 821754fbed325e38de212341e8b6b6bbd583a248debf91b75edf3278fef22d7754a2fc8824243b10de9dec1c165da376c748f8be44999269987b7c155d8f3653
6
+ metadata.gz: 33bc80d31708005d6e42b77a0a6b4805b986e147dfee728db9e63a6b80fd735cfcb64123d41876b6c38b7addb9aa87e716f695ee7bcf54532c701eeb90a8a04b
7
+ data.tar.gz: ae5aa8a93e9aa0408b6e6a6d2c10da94500123f209ddd9f40108427489bc649c2a344d53e338fdb984f3257fe74d6f10d2660a3185c84c537fdcfdff3e226da7
data/Gemfile CHANGED
@@ -7,5 +7,5 @@ gem 'active_model_serializers', :github => 'rails-api/active_model_serializers',
7
7
 
8
8
  group :test do
9
9
  gem 'activerecord'
10
- gem 'rspec-rails'
10
+ gem 'rspec-rails', '~> 3.1'
11
11
  end
data/Rakefile CHANGED
@@ -1,2 +1 @@
1
1
  require "bundler/gem_tasks"
2
-
@@ -99,7 +99,7 @@ module Trax
99
99
  end
100
100
 
101
101
  def render_errors(status = :unprocessable_entity, error_messages_hash:{}, **options)
102
- errors = error_messages_hash.merge(resource_error_messages(**options))
102
+ errors = error_messages_hash.merge(resource_error_messages(**options) || {})
103
103
  render json: { meta: { errors: errors } }, status: status, serializer: nil
104
104
  end
105
105
 
@@ -0,0 +1,18 @@
1
+ module Trax
2
+ module Controller
3
+ module Authorization
4
+ module Pundit
5
+ module Actions
6
+ module Create
7
+ extend ::ActiveSupport::Concern
8
+
9
+ def create_resource(object)
10
+ authorize(object)
11
+ super(object)
12
+ end
13
+ end
14
+ end
15
+ end
16
+ end
17
+ end
18
+ end
@@ -0,0 +1,18 @@
1
+ module Trax
2
+ module Controller
3
+ module Authorization
4
+ module Pundit
5
+ module Actions
6
+ module Destroy
7
+ extend ::ActiveSupport::Concern
8
+
9
+ def destroy_resource(object)
10
+ authorize(object)
11
+ super(object)
12
+ end
13
+ end
14
+ end
15
+ end
16
+ end
17
+ end
18
+ end
@@ -0,0 +1,18 @@
1
+ module Trax
2
+ module Controller
3
+ module Authorization
4
+ module Pundit
5
+ module Actions
6
+ module Index
7
+ extend ::ActiveSupport::Concern
8
+
9
+ def index(*args, **options)
10
+ authorize(collection)
11
+ super(*args, **options)
12
+ end
13
+ end
14
+ end
15
+ end
16
+ end
17
+ end
18
+ end
@@ -0,0 +1,18 @@
1
+ module Trax
2
+ module Controller
3
+ module Authorization
4
+ module Pundit
5
+ module Actions
6
+ module New
7
+ extend ::ActiveSupport::Concern
8
+
9
+ def new(*args, *options)
10
+ authorize(build_resource)
11
+ super(*args, *options)
12
+ end
13
+ end
14
+ end
15
+ end
16
+ end
17
+ end
18
+ end
@@ -0,0 +1,16 @@
1
+ module Trax
2
+ module Controller
3
+ module Authorization
4
+ module Pundit
5
+ module Actions
6
+ module Read
7
+ extend ::ActiveSupport::Concern
8
+
9
+ include ::Trax::Controller::Authorization::Pundit::Actions::Index
10
+ include ::Trax::Controller::Authorization::Pundit::Actions::Show
11
+ include ::Trax::Controller::Authorization::Pundit::Actions::Search
12
+ end
13
+ end
14
+ end
15
+ end
16
+ end
@@ -0,0 +1,18 @@
1
+ module Trax
2
+ module Controller
3
+ module Authorization
4
+ module Pundit
5
+ module Actions
6
+ module Search
7
+ extend ::ActiveSupport::Concern
8
+
9
+ def search(*args, **options)
10
+ authorize(collection)
11
+ super(*args, **options)
12
+ end
13
+ end
14
+ end
15
+ end
16
+ end
17
+ end
18
+ end
@@ -0,0 +1,18 @@
1
+ module Trax
2
+ module Controller
3
+ module Authorization
4
+ module Pundit
5
+ module Actions
6
+ module Show
7
+ extend ::ActiveSupport::Concern
8
+
9
+ def show(*args, **options)
10
+ authorize(resource)
11
+ super(*args, **options)
12
+ end
13
+ end
14
+ end
15
+ end
16
+ end
17
+ end
18
+ end
@@ -0,0 +1,19 @@
1
+ module Trax
2
+ module Controller
3
+ module Authorization
4
+ module Pundit
5
+ module Actions
6
+ module Update
7
+ extend ::ActiveSupport::Concern
8
+
9
+ def update_resource(object, attributes={})
10
+ object.assign_attributes(*attributes)
11
+ authorize(object)
12
+ super(object, attributes)
13
+ end
14
+ end
15
+ end
16
+ end
17
+ end
18
+ end
19
+ end
@@ -0,0 +1,20 @@
1
+ module Trax
2
+ module Controller
3
+ module Authorization
4
+ module Pundit
5
+ module Actions
6
+ extend ::ActiveSupport::Autoload
7
+
8
+ autoload :Create
9
+ autoload :Destroy
10
+ autoload :Index
11
+ autoload :New
12
+ autoload :Read
13
+ autoload :Search
14
+ autoload :Show
15
+ autoload :Update
16
+ end
17
+ end
18
+ end
19
+ end
20
+ end
@@ -0,0 +1,43 @@
1
+ require 'pundit'
2
+
3
+ module Trax
4
+ module Controller
5
+ module Authorization
6
+ module Pundit
7
+ module Adapter
8
+ extend ::ActiveSupport::Concern
9
+
10
+ include ::Pundit
11
+
12
+ included do
13
+ rescue_from ::Pundit::NotAuthorizedError, :with => :render_pundit_errors
14
+ class_attribute :_policy_class
15
+ end
16
+
17
+ def render_pundit_errors
18
+ render_errors(:forbidden, error_messages_hash: { :not_authorized => 'You are not authorized to perform this action' } )
19
+ end
20
+
21
+ def policy(record)
22
+ self.class._policy_class.new(current_user, record)
23
+ end
24
+
25
+ #not included into controller
26
+ def self.authorization_action_for(action_name)
27
+ "::Trax::Controller::Authorization::Pundit::Actions::#{action_name.to_s.camelize}".safe_constantize
28
+ end
29
+
30
+ module ClassMethods
31
+ def policy_class(klass)
32
+ self._policy_class = klass
33
+ end
34
+
35
+ def policy!(user, record)
36
+ _policy_class.new(user, record)
37
+ end
38
+ end
39
+ end
40
+ end
41
+ end
42
+ end
43
+ end
@@ -0,0 +1,18 @@
1
+ module Trax
2
+ module Controller
3
+ module Authorization
4
+ module Pundit
5
+ class BasePolicy
6
+ attr_accessor :user, :resource, :result
7
+
8
+ #Result is set to true by default because it makes chaining easy with &&=
9
+ def initialize(user, resource)
10
+ @user = user
11
+ @resource = resource
12
+ @result = true
13
+ end
14
+ end
15
+ end
16
+ end
17
+ end
18
+ end
@@ -0,0 +1,13 @@
1
+ module Trax
2
+ module Controller
3
+ module Authorization
4
+ module Pundit
5
+ extend ::ActiveSupport::Autoload
6
+
7
+ autoload :Actions
8
+ autoload :Adapter
9
+ autoload :BasePolicy
10
+ end
11
+ end
12
+ end
13
+ end
@@ -0,0 +1,9 @@
1
+ module Trax
2
+ module Controller
3
+ module Authorization
4
+ extend ::ActiveSupport::Autoload
5
+
6
+ autoload :Pundit
7
+ end
8
+ end
9
+ end
@@ -0,0 +1,24 @@
1
+ module Trax
2
+ module Controller
3
+ module Authorize
4
+ extend ::ActiveSupport::Concern
5
+
6
+ include ::Trax::Controller.config.authorization_adapter
7
+
8
+ STANDARD_ACTIONS = [ :index, :search, :show, :destroy, :update, :create, :new ]
9
+
10
+ module ClassMethods
11
+ def actions_to_authorize(*_actions, all:false, except:false, only:false)
12
+ _actions += (all || except) ? STANDARD_ACTIONS.dup : []
13
+ _actions += only if only
14
+ _actions.reject!{ |_action| except.include?(_action) } if except
15
+
16
+ _actions.each do |action|
17
+ action_module = ::Trax::Controller.config.authorization_adapter.authorization_action_for(action)
18
+ include action_module if action_module
19
+ end
20
+ end
21
+ end
22
+ end
23
+ end
24
+ end
@@ -0,0 +1,13 @@
1
+ require 'active_support/ordered_options'
2
+
3
+ module Trax
4
+ module Controller
5
+ class Config < ::ActiveSupport::InheritableOptions
6
+ def initialize(*args)
7
+ super(*args)
8
+
9
+ self[:authorization_adapter] = false
10
+ end
11
+ end
12
+ end
13
+ end
@@ -8,35 +8,33 @@ module Trax
8
8
  module Adapters
9
9
  class Json < ActiveModel::Serializer::Adapter::Json
10
10
  def serializable_hash(options={})
11
- is_collection? ? collection_serializable_hash(options) : resource_serializable_hash(options)
11
+ result = is_collection? ? collection_serializable_hash(options) : resource_serializable_hash(options)
12
+ @options[:nested] ? result : { root => result }
12
13
  end
13
14
 
14
15
  private
15
16
 
16
- def collection_result
17
- @collection_result ||= []
17
+ def get_hash(serializer, options={})
18
+ self.class.new(serializer, :nested => true).serializable_hash(options)
18
19
  end
19
20
 
20
21
  def collection_serializable_hash(options={})
21
- _result = collection_result + serializer.map { |s| FlattenJson.new(s).serializable_hash(options) }
22
- { root => _result }
22
+ serializer.map { |s| get_hash(s, options) }
23
23
  end
24
24
 
25
25
  def is_collection?
26
26
  serializer.respond_to?(:each)
27
27
  end
28
28
 
29
- def resource_result
30
- @resource_result ||= {}
31
- end
32
-
33
29
  def resource_serializable_hash(options={})
34
- cache_check(serializer){ resource_result.merge!(serializer.attributes(options)) }
35
- serialize_resource_associations
36
- { root => resource_result }
30
+ cached = cache_check(serializer){ serializer.attributes(options) }
31
+ resource_result = serialize_resource_associations
32
+ cached.merge(resource_result)
37
33
  end
38
34
 
39
35
  def serialize_resource_associations
36
+ resource_result = {}
37
+
40
38
  serializer.associations.each do |association|
41
39
  association_serializer = association.serializer
42
40
  association_options = association.options
@@ -45,16 +43,18 @@ module Trax
45
43
  array_serializer = association_serializer
46
44
 
47
45
  resource_result[association.key] = array_serializer.map do |item|
48
- cache_check(item) { FlattenJson.new(item).serializable_hash }
46
+ cache_check(item) { get_hash(item) }
49
47
  end
50
48
  else
51
49
  resource_result[association.key] = if association_serializer && association_serializer.object
52
- cache_check(association_serializer) { FlattenJson.new(association_serializer).serializable_hash }
50
+ cache_check(association_serializer) { get_hash(association_serializer) }
53
51
  elsif association_options[:virtual_value]
54
52
  association_options[:virtual_value]
55
53
  end
56
54
  end
57
55
  end
56
+
57
+ resource_result
58
58
  end
59
59
  end
60
60
  end
@@ -16,19 +16,32 @@ module Trax
16
16
  end
17
17
 
18
18
  autoload :Actions
19
+ autoload :Authorize
20
+ autoload :Authorization
21
+ autoload :Config
19
22
  autoload :Collection
20
23
  autoload :Engine
21
24
  autoload :Resource
22
25
  autoload :InheritResources
23
26
  autoload :ActionTypes
24
- autoload :Engine
25
27
  autoload :Serialization
26
28
 
29
+ @configuration ||= ::Trax::Controller::Config.new
30
+
31
+ def self.configure(&block)
32
+ block.call(config)
33
+ end
34
+
27
35
  include ::Trax::Controller::InheritResources
28
36
  include ::Trax::Controller::Collection::Base
29
37
  include ::Trax::Controller::Resource::Base
30
38
  include ::Trax::Controller::Actions
31
39
  include ::Trax::Controller::ActionTypes
40
+
41
+ class << self
42
+ attr_accessor :configuration
43
+ alias_method :config, :configuration
44
+ end
32
45
  end
33
46
  end
34
47
 
@@ -1,3 +1,3 @@
1
1
  module TraxController
2
- VERSION = "0.0.3"
2
+ VERSION = "0.0.4"
3
3
  end
@@ -0,0 +1,22 @@
1
+ class WidgetsController < ::ApplicationController
2
+ include ::Trax::Controller
3
+ include ::Trax::Controller::Collection::Pageable
4
+
5
+ defaults :resource_class => ::Widget
6
+
7
+ has_scope :by_id
8
+
9
+ actions :index, :show, :create, :update, :destroy
10
+
11
+ def widget_params
12
+ params.require(:widget).permit(:name, :quantity)
13
+ end
14
+
15
+ def user_email
16
+ params[:user_email]
17
+ end
18
+
19
+ def current_user
20
+ @current_user ||= ::User.find_by(:email => user_email)
21
+ end
22
+ end
@@ -2,4 +2,7 @@ class Category < ::ActiveRecord::Base
2
2
  validates :name, :length => { :minimum => 5 }
3
3
 
4
4
  has_many :products
5
+
6
+ belongs_to :parent, :class_name => self.name, :foreign_key => :parent_id
7
+ has_many :subcategories, :class_name => self.name, :foreign_key => :parent_id
5
8
  end
@@ -0,0 +1,3 @@
1
+ class User < ::ActiveRecord::Base
2
+
3
+ end
@@ -0,0 +1,26 @@
1
+ class WidgetPolicy < Trax::Controller::Authorization::Pundit::BasePolicy
2
+ def initialize(*args)
3
+ super(*args)
4
+ end
5
+
6
+ def index?
7
+ true
8
+ end
9
+
10
+ def create?
11
+ @result &&= user.can_create_widgets
12
+ end
13
+
14
+ def show?
15
+ @result &&= user.can_read_widgets
16
+ end
17
+
18
+ def update?
19
+ @result &&= user.can_update_widgets
20
+ end
21
+
22
+ def destroy?
23
+ @result &&= user.can_destroy_widgets
24
+ @result &&= user.is_admin
25
+ end
26
+ end
@@ -1,5 +1,6 @@
1
1
  Rails.application.routes.draw do
2
2
  resources :products, :defaults => { :format => :json }
3
+ resources :widgets, :defaults => { :format => :json }
3
4
  resources :categories, :defaults => { :format => :json } do
4
5
  collection do
5
6
  post 'create_with_modified_response_codes'
@@ -13,6 +13,7 @@
13
13
 
14
14
  create_table "categories", :force => true do |t|
15
15
  t.string "name"
16
+ t.integer "parent_id"
16
17
  t.datetime "created_at", :null => false
17
18
  t.datetime "updated_at", :null => false
18
19
  end
@@ -21,6 +22,11 @@
21
22
  t.string "first_name"
22
23
  t.string "last_name"
23
24
  t.string "email"
25
+ t.boolean "can_destroy_widgets"
26
+ t.boolean "can_create_widgets"
27
+ t.boolean "can_read_widgets"
28
+ t.boolean "can_update_widgets"
29
+ t.boolean "is_admin"
24
30
  t.datetime "created_at", :null => false
25
31
  t.datetime "updated_at", :null => false
26
32
  end
@@ -31,7 +37,8 @@
31
37
  t.string "subdomain"
32
38
  t.string "website"
33
39
  t.integer "status"
34
- t.integer "quantity"
40
+ t.integer "quantity"
41
+ t.boolean "is_read_only"
35
42
  t.datetime "created_at", :null => false
36
43
  t.datetime "updated_at", :null => false
37
44
  end
data/spec/spec_helper.rb CHANGED
@@ -18,6 +18,13 @@ end
18
18
 
19
19
  ::RSpec.configure do |config|
20
20
  config.include RSpec::Rails::RequestExampleGroup, type: :feature
21
+
22
+ config.before(:all) do
23
+ ::Trax::Controller.config.authorization_adapter = ::Trax::Controller::Authorization::Pundit::Adapter
24
+ end
25
+
26
+ config.filter_run :focus => true
27
+ config.run_all_when_everything_filtered = true
21
28
  end
22
29
 
23
30
  ::Bundler.require(:default, :development, :test)
@@ -0,0 +1,43 @@
1
+ require 'spec_helper'
2
+
3
+ ::RSpec.describe ::Trax::Controller::Authorization::Pundit::BasePolicy do
4
+ let(:widget_policy_class) { ::WidgetPolicy }
5
+
6
+ let!(:widget) { ::Widget.create(:name => "Haterade", :is_read_only => true) }
7
+ subject { WidgetPolicy.new(user, widget) }
8
+
9
+ context "user with permissions" do
10
+ let!(:user) {
11
+ ::User.create(
12
+ :can_read_widgets => true,
13
+ :can_update_widgets => true,
14
+ :can_create_widgets => true,
15
+ :can_destroy_widgets => true,
16
+ :is_admin => false
17
+ )
18
+ }
19
+
20
+ it { expect(subject.show?).to be true }
21
+ it { expect(subject.update?).to be true }
22
+ it { expect(subject.create?).to be true }
23
+ it { expect(subject.destroy?).to be false }
24
+ end
25
+
26
+ context "user without permissions" do
27
+ let!(:user) {
28
+ ::User.create(
29
+ :can_read_widgets => false,
30
+ :can_update_widgets => false,
31
+ :can_create_widgets => false,
32
+ :can_destroy_widgets => false,
33
+ :is_admin => false
34
+ )
35
+ }
36
+ subject { WidgetPolicy.new(user, widget) }
37
+
38
+ it { expect(subject.show?).to be false }
39
+ it { expect(subject.update?).to be false }
40
+ it { expect(subject.create?).to be false }
41
+ it { expect(subject.destroy?).to be false }
42
+ end
43
+ end
@@ -0,0 +1,179 @@
1
+ require 'spec_helper'
2
+
3
+ ::RSpec.describe WidgetsController do
4
+ before(:all) do
5
+ ::Trax::Controller.config.authorization_adapter = ::Trax::Controller::Authorization::Pundit::Adapter
6
+
7
+ ::WidgetsController.class_eval do
8
+ include ::Trax::Controller::Authorize
9
+ actions_to_authorize :index, :show, :create, :update, :destroy
10
+ policy_class ::WidgetPolicy
11
+ end
12
+ end
13
+
14
+ context "WidgetsController" do
15
+ context "Unit Test" do
16
+ context ".actions_to_authorize" do
17
+ {
18
+ :index => Trax::Controller::Authorization::Pundit::Actions::Index,
19
+ :create => Trax::Controller::Authorization::Pundit::Actions::Create,
20
+ :show => Trax::Controller::Authorization::Pundit::Actions::Show,
21
+ :destroy => Trax::Controller::Authorization::Pundit::Actions::Destroy,
22
+ :update => Trax::Controller::Authorization::Pundit::Actions::Update
23
+ }.each_pair do |k,v|
24
+ it "includes the pundit action for #{k}" do
25
+ expect(subject.class.ancestors).to include(v)
26
+ end
27
+ end
28
+ end
29
+ end
30
+
31
+ describe "WidgetsController", :type => :controller do
32
+ let!(:user1) {
33
+ ::User.create(
34
+ :email => 'kanyewest@theleatherjoggingpants.com',
35
+ :can_read_widgets => true,
36
+ :can_update_widgets => true,
37
+ :can_create_widgets => true,
38
+ :can_destroy_widgets => true,
39
+ :is_admin => false
40
+ )
41
+ }
42
+
43
+ let!(:user2) {
44
+ ::User.create(
45
+ :email => 'kanyewest@waterbottledesign.com',
46
+ :can_read_widgets => false,
47
+ :can_update_widgets => false,
48
+ :can_create_widgets => false,
49
+ :can_destroy_widgets => false,
50
+ :is_admin => true
51
+ )
52
+ }
53
+
54
+ let(:user_who_can) {
55
+ {
56
+ :read_widgets => user1,
57
+ :update_widgets => user1,
58
+ :create_widgets => user1,
59
+ :destroy_widgets => user1
60
+ }
61
+ }
62
+
63
+ let(:user_who_cannot) {
64
+ {
65
+ :read_widgets => user2,
66
+ :update_widgets => user2,
67
+ :create_widgets => user2,
68
+ :destroy_widgets => user2
69
+ }
70
+ }
71
+
72
+ let(:common_params) { {:user_email => current_user.email} }
73
+ let!(:widget) { ::Widget.create(:name => "Water Bottle", :is_read_only => true, :quantity => 5) }
74
+
75
+ let(:unauthorized_result_expectations) {
76
+ lambda { |json|
77
+ expect(json).to have_key("meta")
78
+ expect(json["meta"]).to have_key("errors")
79
+ expect(json["meta"]["errors"]).to have_key("not_authorized")
80
+ }
81
+ }
82
+
83
+ describe '#show' do
84
+ context 'user can read widgets' do
85
+ let(:current_user) { user_who_can[:read_widgets] }
86
+
87
+ it 'is authorized' do
88
+ get :show, common_params.merge!(id: widget.id)
89
+ expect(response).to be_ok
90
+ json = JSON.parse response.body
91
+ expect(json).to have_key('widget')
92
+ end
93
+ end
94
+
95
+ context 'user cannot read widgets' do
96
+ let(:current_user) { user_who_cannot[:read_widgets] }
97
+
98
+ it do
99
+ get :show, common_params.merge!(id: widget.id)
100
+ expect(response).to_not be_ok
101
+ json = JSON.parse response.body
102
+ unauthorized_result_expectations.call(json)
103
+ end
104
+ end
105
+ end
106
+
107
+ describe '#create' do
108
+ context "user can create widgets" do
109
+ let(:current_user) { user_who_can[:create_widgets] }
110
+ let(:widget_name) { 'Kanyes water bottle' }
111
+ let(:params){ {
112
+ :format => :json,
113
+ :widget => widget_params
114
+ }}
115
+ let(:widget_params) { { :name => 'Marble Conference Table', :quantity => 1 } }
116
+ before { post :create, common_params.merge(params) }
117
+
118
+ it { expect(response).to be_successful }
119
+ end
120
+
121
+ context "user cannot create widgets" do
122
+ let(:current_user) { user_who_cannot[:create_widgets] }
123
+ let(:params){ {
124
+ :format => :json,
125
+ :widget => widget_params
126
+ }}
127
+ let(:widget_params) { { :name => 'Marble Conference Table', :quantity => 1 } }
128
+ before { post :create, common_params.merge(params) }
129
+
130
+ it {
131
+ expect(response).to_not be_ok
132
+ json = JSON.parse response.body
133
+ unauthorized_result_expectations.call(json)
134
+ }
135
+ end
136
+ end
137
+
138
+ describe '#update' do
139
+ context "user cannot update widgets" do
140
+ let(:current_user) { user_who_cannot[:update_widgets] }
141
+ let(:updated_widget_name) { 'Kanyes water bottle' }
142
+ let(:params){ {
143
+ :format => :json,
144
+ :id => widget.id,
145
+ :widget => widget_params
146
+ }}
147
+ let(:widget_params) { { :name => updated_widget_name, :quantity => 1 } }
148
+
149
+ before { put :update, common_params.merge(params) }
150
+
151
+ it { expect(response).to_not be_successful }
152
+ it { expect(response.status).to eq 403 }
153
+ it {
154
+ expect(response).to_not be_ok
155
+ json = JSON.parse response.body
156
+ unauthorized_result_expectations.call(json)
157
+ }
158
+ end
159
+
160
+ context "user can update widgets" do
161
+ let(:current_user) { user_who_can[:update_widgets] }
162
+ let(:updated_widget_name) { 'Kanyes water bottle' }
163
+ let(:params){ {
164
+ :format => :json,
165
+ :id => widget.id,
166
+ :widget => widget_params
167
+ }}
168
+ let(:widget_params) { { :name => updated_widget_name, :quantity => 1 } }
169
+ let(:request_params) { common_params.merge(params) }
170
+
171
+ before { put :update, request_params }
172
+
173
+ it { expect(response).to be_successful }
174
+ it { expect(response).to be_ok }
175
+ end
176
+ end
177
+ end
178
+ end
179
+ end
@@ -23,8 +23,6 @@ require 'spec_helper'
23
23
  expect(json["meta"]["pagination"]).to have_key(k)
24
24
  }
25
25
  end
26
-
27
-
28
26
  end
29
27
  end
30
28
 
@@ -0,0 +1,55 @@
1
+ require 'spec_helper'
2
+
3
+ class NameCategorySerializer < ::ActiveModel::Serializer
4
+ attributes :name
5
+ has_many :subcategories, :serializer => NameCategorySerializer
6
+ end
7
+
8
+ class NameProductSerializer < ::ActiveModel::Serializer
9
+ attributes :name
10
+ has_one :category, :serializer => NameCategorySerializer
11
+ end
12
+
13
+ describe Trax::Controller::Serialization::Adapters::Json do
14
+ let(:category_flat_heads){ ::Category.new(:name => "flat heads") }
15
+ let(:category_screwdrivers){ ::Category.new(:name => "screwdrivers", :subcategories => [category_flat_heads]) }
16
+ let(:category_knives){ ::Category.new(:name => "knives") }
17
+ let(:category_tools){ ::Category.new(:name => "tools", :subcategories => [category_screwdrivers, category_knives]) }
18
+ let(:product_tool){ ::Product.new(:name => "Generics Multi-Purpose Tool", :category => category_tools) }
19
+ let(:root_key){ 'root_key' }
20
+ let(:serializer){ ::NameProductSerializer.new(product_tool, :root => root_key) }
21
+ let!(:expected_root_key){ root_key.to_sym }
22
+ let!(:expected_tool_hash){ {
23
+ :name => product_tool.name,
24
+ :category => {
25
+ :name => category_tools.name,
26
+ :subcategories => [
27
+ { :name => category_screwdrivers.name,
28
+ :subcategories => [{:name => category_flat_heads.name, :subcategories => []}]
29
+ },
30
+ {:name => category_knives.name, :subcategories => []}
31
+ ]
32
+ }
33
+ }}
34
+
35
+ subject(:serialized){ described_class.new(serializer).serializable_hash }
36
+
37
+ it { expect(serialized).to eq(expected_root_key => expected_tool_hash) }
38
+
39
+ context "collection" do
40
+ let(:category_cookware){ ::Category.new(:name => "cookware") }
41
+ let(:product_pot){ ::Product.new(:name => "Pot", :category => category_cookware) }
42
+ let(:collection){ [product_tool, product_pot] }
43
+ let!(:expected_root_key){ root_key.pluralize.to_sym }
44
+ let!(:expected_pot_hash){ {
45
+ :name => product_pot.name,
46
+ :category => {
47
+ :name => category_cookware.name,
48
+ :subcategories => []
49
+ }
50
+ }}
51
+ let(:serializer){ ::ActiveModel::Serializer::ArraySerializer.new(collection, :root => root_key, :serializer => ::NameProductSerializer) }
52
+
53
+ it { expect(serialized).to eq(expected_root_key => [expected_tool_hash, expected_pot_hash]) }
54
+ end
55
+ end
@@ -22,7 +22,7 @@ Gem::Specification.new do |spec|
22
22
  spec.add_dependency "will_paginate"
23
23
  spec.add_dependency "has_scope"
24
24
  spec.add_development_dependency "rails"
25
- spec.add_development_dependency "inherited_resources"
25
+ spec.add_development_dependency "inherited_resources", "~> 1.5.1"
26
26
  spec.add_development_dependency 'combustion', '~> 0.5.3'
27
27
  spec.add_development_dependency "bundler", "~> 1.6"
28
28
  spec.add_development_dependency "rake"
@@ -34,9 +34,8 @@ Gem::Specification.new do |spec|
34
34
  spec.add_development_dependency 'rspec-its', '~> 1'
35
35
  spec.add_development_dependency 'rspec-collection_matchers', '~> 1'
36
36
  spec.add_development_dependency 'guard', '~> 2'
37
- spec.add_development_dependency 'guard-rspec', '~> 4'
38
37
  spec.add_development_dependency 'guard-bundler', '~> 2'
39
38
  spec.add_development_dependency 'rb-fsevent'
40
39
  spec.add_development_dependency 'terminal-notifier-guard'
41
-
40
+ spec.add_development_dependency 'pundit'
42
41
  end
metadata CHANGED
@@ -1,14 +1,14 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: trax_controller
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.0.3
4
+ version: 0.0.4
5
5
  platform: ruby
6
6
  authors:
7
7
  - Jason Ayre
8
8
  autorequire:
9
9
  bindir: bin
10
10
  cert_chain: []
11
- date: 2015-10-28 00:00:00.000000000 Z
11
+ date: 2016-04-15 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: trax_core
@@ -70,16 +70,16 @@ dependencies:
70
70
  name: inherited_resources
71
71
  requirement: !ruby/object:Gem::Requirement
72
72
  requirements:
73
- - - ">="
73
+ - - "~>"
74
74
  - !ruby/object:Gem::Version
75
- version: '0'
75
+ version: 1.5.1
76
76
  type: :development
77
77
  prerelease: false
78
78
  version_requirements: !ruby/object:Gem::Requirement
79
79
  requirements:
80
- - - ">="
80
+ - - "~>"
81
81
  - !ruby/object:Gem::Version
82
- version: '0'
82
+ version: 1.5.1
83
83
  - !ruby/object:Gem::Dependency
84
84
  name: combustion
85
85
  requirement: !ruby/object:Gem::Requirement
@@ -235,35 +235,35 @@ dependencies:
235
235
  - !ruby/object:Gem::Version
236
236
  version: '2'
237
237
  - !ruby/object:Gem::Dependency
238
- name: guard-rspec
238
+ name: guard-bundler
239
239
  requirement: !ruby/object:Gem::Requirement
240
240
  requirements:
241
241
  - - "~>"
242
242
  - !ruby/object:Gem::Version
243
- version: '4'
243
+ version: '2'
244
244
  type: :development
245
245
  prerelease: false
246
246
  version_requirements: !ruby/object:Gem::Requirement
247
247
  requirements:
248
248
  - - "~>"
249
249
  - !ruby/object:Gem::Version
250
- version: '4'
250
+ version: '2'
251
251
  - !ruby/object:Gem::Dependency
252
- name: guard-bundler
252
+ name: rb-fsevent
253
253
  requirement: !ruby/object:Gem::Requirement
254
254
  requirements:
255
- - - "~>"
255
+ - - ">="
256
256
  - !ruby/object:Gem::Version
257
- version: '2'
257
+ version: '0'
258
258
  type: :development
259
259
  prerelease: false
260
260
  version_requirements: !ruby/object:Gem::Requirement
261
261
  requirements:
262
- - - "~>"
262
+ - - ">="
263
263
  - !ruby/object:Gem::Version
264
- version: '2'
264
+ version: '0'
265
265
  - !ruby/object:Gem::Dependency
266
- name: rb-fsevent
266
+ name: terminal-notifier-guard
267
267
  requirement: !ruby/object:Gem::Requirement
268
268
  requirements:
269
269
  - - ">="
@@ -277,7 +277,7 @@ dependencies:
277
277
  - !ruby/object:Gem::Version
278
278
  version: '0'
279
279
  - !ruby/object:Gem::Dependency
280
- name: terminal-notifier-guard
280
+ name: pundit
281
281
  requirement: !ruby/object:Gem::Requirement
282
282
  requirements:
283
283
  - - ">="
@@ -311,11 +311,26 @@ files:
311
311
  - lib/trax/controller.rb
312
312
  - lib/trax/controller/action_types.rb
313
313
  - lib/trax/controller/actions.rb
314
+ - lib/trax/controller/authorization.rb
315
+ - lib/trax/controller/authorization/pundit.rb
316
+ - lib/trax/controller/authorization/pundit/actions.rb
317
+ - lib/trax/controller/authorization/pundit/actions/create.rb
318
+ - lib/trax/controller/authorization/pundit/actions/destroy.rb
319
+ - lib/trax/controller/authorization/pundit/actions/index.rb
320
+ - lib/trax/controller/authorization/pundit/actions/new.rb
321
+ - lib/trax/controller/authorization/pundit/actions/read.rb
322
+ - lib/trax/controller/authorization/pundit/actions/search.rb
323
+ - lib/trax/controller/authorization/pundit/actions/show.rb
324
+ - lib/trax/controller/authorization/pundit/actions/update.rb
325
+ - lib/trax/controller/authorization/pundit/adapter.rb
326
+ - lib/trax/controller/authorization/pundit/base_policy.rb
327
+ - lib/trax/controller/authorize.rb
314
328
  - lib/trax/controller/collection.rb
315
329
  - lib/trax/controller/collection/base.rb
316
330
  - lib/trax/controller/collection/pageable.rb
317
331
  - lib/trax/controller/collection/response_meta.rb
318
332
  - lib/trax/controller/collection/searchable.rb
333
+ - lib/trax/controller/config.rb
319
334
  - lib/trax/controller/engine.rb
320
335
  - lib/trax/controller/inherit_resources.rb
321
336
  - lib/trax/controller/resource.rb
@@ -329,9 +344,12 @@ files:
329
344
  - spec/internal/app/controllers/application_controller.rb
330
345
  - spec/internal/app/controllers/categories_controller.rb
331
346
  - spec/internal/app/controllers/products_controller.rb
347
+ - spec/internal/app/controllers/widgets_controller.rb
332
348
  - spec/internal/app/models/category.rb
333
349
  - spec/internal/app/models/product.rb
350
+ - spec/internal/app/models/user.rb
334
351
  - spec/internal/app/models/widget.rb
352
+ - spec/internal/app/policies/widget_policy.rb
335
353
  - spec/internal/app/serializers/categories_serializer.rb
336
354
  - spec/internal/app/serializers/category_serializer.rb
337
355
  - spec/internal/app/serializers/product_serializer.rb
@@ -343,9 +361,12 @@ files:
343
361
  - spec/internal/log/.gitignore
344
362
  - spec/internal/public/favicon.ico
345
363
  - spec/spec_helper.rb
364
+ - spec/trax/controller/authorization/pundit/base_policy_spec.rb
365
+ - spec/trax/controller/authorization/pundit_spec.rb
346
366
  - spec/trax/controller_spec.rb
347
367
  - spec/trax/controllers/categories_controller_spec.rb
348
368
  - spec/trax/controllers/products_controller_spec.rb
369
+ - spec/trax/serialization/adapters/json_spec.rb
349
370
  - trax_controller.gemspec
350
371
  homepage: http://www.github.com/jasonayre
351
372
  licenses:
@@ -367,7 +388,7 @@ required_rubygems_version: !ruby/object:Gem::Requirement
367
388
  version: '0'
368
389
  requirements: []
369
390
  rubyforge_project:
370
- rubygems_version: 2.4.5
391
+ rubygems_version: 2.5.1
371
392
  signing_key:
372
393
  specification_version: 4
373
394
  summary: Resourceful, standardized controllers
@@ -375,9 +396,12 @@ test_files:
375
396
  - spec/internal/app/controllers/application_controller.rb
376
397
  - spec/internal/app/controllers/categories_controller.rb
377
398
  - spec/internal/app/controllers/products_controller.rb
399
+ - spec/internal/app/controllers/widgets_controller.rb
378
400
  - spec/internal/app/models/category.rb
379
401
  - spec/internal/app/models/product.rb
402
+ - spec/internal/app/models/user.rb
380
403
  - spec/internal/app/models/widget.rb
404
+ - spec/internal/app/policies/widget_policy.rb
381
405
  - spec/internal/app/serializers/categories_serializer.rb
382
406
  - spec/internal/app/serializers/category_serializer.rb
383
407
  - spec/internal/app/serializers/product_serializer.rb
@@ -389,6 +413,9 @@ test_files:
389
413
  - spec/internal/log/.gitignore
390
414
  - spec/internal/public/favicon.ico
391
415
  - spec/spec_helper.rb
416
+ - spec/trax/controller/authorization/pundit/base_policy_spec.rb
417
+ - spec/trax/controller/authorization/pundit_spec.rb
392
418
  - spec/trax/controller_spec.rb
393
419
  - spec/trax/controllers/categories_controller_spec.rb
394
420
  - spec/trax/controllers/products_controller_spec.rb
421
+ - spec/trax/serialization/adapters/json_spec.rb