trax_controller 0.0.3 → 0.0.4

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
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