snfoil 0.9.0 → 1.0.0

Sign up to get free protection for your applications and to get access to all the features.
Files changed (40) hide show
  1. checksums.yaml +4 -4
  2. data/CHANGELOG.md +5 -0
  3. data/Gemfile +6 -0
  4. data/LICENSE.txt +201 -0
  5. data/README.md +95 -505
  6. data/docs/build-context.md +56 -0
  7. data/docs/create-context.md +109 -0
  8. data/docs/destroy-context.md +102 -0
  9. data/docs/index-context.md +70 -0
  10. data/docs/show-context.md +71 -0
  11. data/docs/update-context.md +107 -0
  12. data/lib/{sn_foil → snfoil}/adapters/orms/active_record.rb +14 -0
  13. data/lib/{sn_foil → snfoil}/adapters/orms/base_adapter.rb +15 -1
  14. data/lib/snfoil/crud/build_context.rb +49 -0
  15. data/lib/snfoil/crud/change_context.rb +48 -0
  16. data/lib/snfoil/crud/context.rb +41 -0
  17. data/lib/snfoil/crud/create_context.rb +45 -0
  18. data/lib/snfoil/crud/destroy_context.rb +57 -0
  19. data/lib/snfoil/crud/index_context.rb +46 -0
  20. data/lib/snfoil/crud/setup_context.rb +90 -0
  21. data/lib/snfoil/crud/show_context.rb +52 -0
  22. data/lib/snfoil/crud/update_context.rb +60 -0
  23. data/lib/snfoil/version.rb +19 -0
  24. data/lib/snfoil.rb +69 -0
  25. data/snfoil.gemspec +47 -0
  26. metadata +100 -33
  27. data/Rakefile +0 -4
  28. data/lib/sn_foil/context.rb +0 -24
  29. data/lib/sn_foil/contexts/build_context.rb +0 -67
  30. data/lib/sn_foil/contexts/change_context.rb +0 -101
  31. data/lib/sn_foil/contexts/create_context.rb +0 -158
  32. data/lib/sn_foil/contexts/destroy_context.rb +0 -164
  33. data/lib/sn_foil/contexts/index_context.rb +0 -64
  34. data/lib/sn_foil/contexts/setup_context.rb +0 -119
  35. data/lib/sn_foil/contexts/show_context.rb +0 -61
  36. data/lib/sn_foil/contexts/update_context.rb +0 -168
  37. data/lib/sn_foil/policy.rb +0 -55
  38. data/lib/sn_foil/searcher.rb +0 -123
  39. data/lib/sn_foil/version.rb +0 -5
  40. data/lib/sn_foil.rb +0 -49
@@ -1,164 +0,0 @@
1
- # frozen_string_literal: true
2
-
3
- require 'active_support/concern'
4
- require_relative './setup_context'
5
- require_relative './change_context'
6
-
7
- module SnFoil
8
- module Contexts
9
- module DestroyContext # rubocop:disable Metrics/ModuleLength
10
- extend ActiveSupport::Concern
11
-
12
- included do
13
- include SetupContext
14
- include ChangeContext
15
- end
16
-
17
- class_methods do
18
- attr_reader :i_setup_destroy_hooks, :i_before_destroy_hooks, :i_after_destroy_hooks,
19
- :i_after_destroy_success_hooks, :i_after_destroy_failure_hooks
20
-
21
- def destroy(id:, entity: nil, **options)
22
- new(entity).destroy(**options, id: id)
23
- end
24
-
25
- def setup_destroy(method = nil, **options, &block)
26
- raise ArgumentError, '#setup_destroy requires either a method name or a block' if method.nil? && block.nil?
27
-
28
- (@i_setup_destroy_hooks ||= []) << { method: method, block: block, if: options[:if], unless: options[:unless] }
29
- end
30
-
31
- def before_destroy(method = nil, **options, &block)
32
- raise ArgumentError, '#before_destroy requires either a method name or a block' if method.nil? && block.nil?
33
-
34
- (@i_before_destroy_hooks ||= []) << { method: method, block: block, if: options[:if], unless: options[:unless] }
35
- end
36
-
37
- def after_destroy(method = nil, **options, &block)
38
- raise ArgumentError, '#after_destroy requires either a method name or a block' if method.nil? && block.nil?
39
-
40
- (@i_after_destroy_hooks ||= []) << { method: method, block: block, if: options[:if], unless: options[:unless] }
41
- end
42
-
43
- def after_destroy_success(method = nil, **options, &block)
44
- raise ArgumentError, '#after_destroy_success requires either a method name or a block' if method.nil? && block.nil?
45
-
46
- (@i_after_destroy_success_hooks ||= []) << { method: method, block: block, if: options[:if], unless: options[:unless] }
47
- end
48
-
49
- def after_destroy_failure(method = nil, **options, &block)
50
- raise ArgumentError, '#after_destroy_failure requires either a method name or a block' if method.nil? && block.nil?
51
-
52
- (@i_after_destroy_failure_hooks ||= []) << { method: method, block: block, if: options[:if], unless: options[:unless] }
53
- end
54
- end
55
-
56
- def setup_destroy_object(id: nil, object: nil, **options)
57
- raise ArgumentError, 'one of the following keywords is required: id, object' unless id || object
58
-
59
- options.merge! object: wrap_object(object || scope.resolve.find(id))
60
- end
61
-
62
- def destroy(**options)
63
- options[:action] = :destroy
64
- options = before_setup_destroy_object(**options)
65
- options = setup_destroy_object(**options)
66
- authorize(options[:object], options.fetch(:authorize, :destroy?), **options)
67
- options = destroy_hooks(**options)
68
- options[:object]
69
- end
70
-
71
- def setup_destroy(**options)
72
- options
73
- end
74
-
75
- def before_destroy(**options)
76
- options
77
- end
78
-
79
- def after_destroy(**options)
80
- options
81
- end
82
-
83
- def after_destroy_success(**options)
84
- options
85
- end
86
-
87
- def after_destroy_failure(**options)
88
- options
89
- end
90
-
91
- def setup_destroy_hooks
92
- self.class.i_setup_destroy_hooks || []
93
- end
94
-
95
- def before_destroy_hooks
96
- self.class.i_before_destroy_hooks || []
97
- end
98
-
99
- def after_destroy_hooks
100
- self.class.i_after_destroy_hooks || []
101
- end
102
-
103
- def after_destroy_success_hooks
104
- self.class.i_after_destroy_success_hooks || []
105
- end
106
-
107
- def after_destroy_failure_hooks
108
- self.class.i_after_destroy_failure_hooks || []
109
- end
110
-
111
- private
112
-
113
- def before_setup_destroy_object(**options)
114
- options = setup(**options)
115
- options = setup_hooks.reduce(options) { |opts, hook| run_hook(hook, **opts) }
116
- options = setup_change(**options)
117
- options = setup_change_hooks.reduce(options) { |opts, hook| run_hook(hook, **opts) }
118
- options = setup_destroy(**options)
119
- setup_destroy_hooks.reduce(options) { |opts, hook| run_hook(hook, **opts) }
120
- end
121
-
122
- # This method is private to help protect the order of execution of hooks
123
- def destroy_hooks(options)
124
- options = before_destroy_save(options)
125
- destroy_successful = options[:object].destroy
126
- options[:object] = unwrap_object(options[:object])
127
- options = if destroy_successful
128
- after_destroy_save_success(options)
129
- else
130
- after_destroy_save_failure(options)
131
- end
132
- after_destroy_save(options)
133
- end
134
-
135
- def before_destroy_save(options)
136
- options = before_change(**options)
137
- options = before_change_hooks.reduce(options) { |opts, hook| run_hook(hook, **opts) }
138
- options = before_destroy(**options)
139
- before_destroy_hooks.reduce(options) { |opts, hook| run_hook(hook, **opts) }
140
- end
141
-
142
- def after_destroy_save(options)
143
- options = after_change(**options)
144
- options = after_change_hooks.reduce(options) { |opts, hook| run_hook(hook, **opts) }
145
- options = after_destroy(**options)
146
- after_destroy_hooks.reduce(options) { |opts, hook| run_hook(hook, **opts) }
147
- end
148
-
149
- def after_destroy_save_success(options)
150
- options = after_change_success(**options)
151
- options = after_change_success_hooks.reduce(options) { |opts, hook| run_hook(hook, **opts) }
152
- options = after_destroy_success(**options)
153
- after_destroy_success_hooks.reduce(options) { |opts, hook| run_hook(hook, **opts) }
154
- end
155
-
156
- def after_destroy_save_failure(options)
157
- options = after_change_failure(**options)
158
- options = after_change_failure_hooks.reduce(options) { |opts, hook| run_hook(hook, **opts) }
159
- options = after_destroy_failure(**options)
160
- after_destroy_failure_hooks.reduce(options) { |opts, hook| run_hook(hook, **opts) }
161
- end
162
- end
163
- end
164
- end
@@ -1,64 +0,0 @@
1
- # frozen_string_literal: true
2
-
3
- require 'active_support/concern'
4
- require_relative './change_context'
5
-
6
- module SnFoil
7
- module Contexts
8
- module IndexContext
9
- extend ActiveSupport::Concern
10
-
11
- included do
12
- include SetupContext
13
- end
14
-
15
- class_methods do
16
- attr_reader :i_searcher, :i_setup_index_hooks
17
-
18
- def index(params: {}, entity: nil, **options)
19
- new(entity).index(**options, params: params)
20
- end
21
-
22
- def searcher(klass = nil)
23
- @i_searcher = klass
24
- end
25
-
26
- def setup_index(method = nil, **options, &block)
27
- raise ArgumentError, '#setup_index requires either a method name or a block' if method.nil? && block.nil?
28
-
29
- (@i_setup_index_hooks ||= []) << { method: method, block: block, if: options[:if], unless: options[:unless] }
30
- end
31
- end
32
-
33
- def searcher
34
- self.class.i_searcher
35
- end
36
-
37
- def setup_index_hooks
38
- self.class.i_setup_index_hooks || []
39
- end
40
-
41
- def index(**options)
42
- options[:action] = :index
43
- options = before_setup_index(**options)
44
- authorize(nil, :index?, **options)
45
- options.fetch(:searcher) { searcher }
46
- .new(scope: scope.resolve)
47
- .search(options.fetch(:params) { {} })
48
- end
49
-
50
- def setup_index(**options)
51
- options
52
- end
53
-
54
- private
55
-
56
- def before_setup_index(**options)
57
- options = setup(**options)
58
- options = setup_hooks.reduce(options) { |opts, hook| run_hook(hook, **opts) }
59
- options = setup_index(**options)
60
- setup_index_hooks.reduce(options) { |opts, hook| run_hook(hook, **opts) }
61
- end
62
- end
63
- end
64
- end
@@ -1,119 +0,0 @@
1
- # frozen_string_literal: true
2
-
3
- require 'active_support/concern'
4
- require 'active_support/core_ext/string/inflections'
5
-
6
- module SnFoil
7
- module Contexts
8
- module SetupContext
9
- extend ActiveSupport::Concern
10
-
11
- class_methods do
12
- attr_reader :i_model, :i_policy, :i_setup_hooks
13
-
14
- def model(klass = nil)
15
- @i_model = klass
16
- end
17
-
18
- def policy(klass = nil)
19
- @i_policy = klass
20
- end
21
-
22
- def setup(method = nil, **options, &block)
23
- raise ArgumentError, '#setup requires either a method name or a block' if method.nil? && block.nil?
24
-
25
- (@i_setup_hooks ||= []) << { method: method, block: block, if: options[:if], unless: options[:unless] }
26
- end
27
- end
28
-
29
- def model
30
- self.class.i_model
31
- end
32
-
33
- def policy
34
- self.class.i_policy
35
- end
36
-
37
- def setup(**options)
38
- options
39
- end
40
-
41
- def setup_hooks
42
- self.class.i_setup_hooks || []
43
- end
44
-
45
- attr_reader :entity
46
-
47
- def initialize(entity = nil)
48
- @entity = entity
49
- end
50
-
51
- def authorize(object, action, **options)
52
- return unless entity # Add logging
53
-
54
- policy = lookup_policy(object, options)
55
- raise Pundit::NotAuthorizedError, query: action, record: object, policy: policy unless policy.public_send(action)
56
-
57
- true
58
- end
59
-
60
- def scope(object_class = nil, **options)
61
- object_class ||= model
62
- policy_name = lookup_policy(object_class, options).class.name
63
- "#{policy_name}::Scope".safe_constantize.new(wrap_object(object_class), entity)
64
- end
65
-
66
- def wrap_object(object)
67
- return object unless adapter
68
-
69
- adapter.new(object)
70
- end
71
-
72
- def unwrap_object(object)
73
- return object unless adapter
74
-
75
- adapter?(object) ? object.__getobj__ : object
76
- end
77
-
78
- def adapter?(object)
79
- return false unless adapter
80
-
81
- object.instance_of? adapter
82
- end
83
-
84
- def adapter
85
- @adapter ||= SnFoil.adapter
86
- end
87
-
88
- def run_hook(hook, **options)
89
- return options unless hook_valid?(hook, **options)
90
-
91
- return send(hook[:method], **options) if hook[:method]
92
-
93
- instance_exec **options, &hook[:block]
94
- end
95
-
96
- def hook_valid?(hook, **options)
97
- return false if !hook[:if].nil? && hook[:if].call(options) == false
98
- return false if !hook[:unless].nil? && hook[:unless].call(options) == true
99
-
100
- true
101
- end
102
-
103
- private
104
-
105
- def lookup_policy(object, options)
106
- lookup = if options[:policy]
107
- options[:policy].new(entity, object)
108
- elsif policy
109
- policy.new(entity, object)
110
- else
111
- Pundit.policy!(entity, object)
112
- end
113
-
114
- lookup.options = options if lookup.respond_to? :options=
115
- lookup
116
- end
117
- end
118
- end
119
- end
@@ -1,61 +0,0 @@
1
- # frozen_string_literal: true
2
-
3
- require 'active_support/concern'
4
- require_relative './setup_context'
5
-
6
- module SnFoil
7
- module Contexts
8
- module ShowContext
9
- extend ActiveSupport::Concern
10
-
11
- included do
12
- include SetupContext
13
- end
14
-
15
- class_methods do
16
- attr_reader :i_setup_show_hooks
17
-
18
- def show(id:, entity: nil, **options)
19
- new(entity).show(**options, id: id)
20
- end
21
-
22
- def setup_show(method = nil, **options, &block)
23
- raise ArgumentError, '#setup_show requires either a method name or a block' if method.nil? && block.nil?
24
-
25
- (@i_setup_show_hooks ||= []) << { method: method, block: block, if: options[:if], unless: options[:unless] }
26
- end
27
- end
28
-
29
- def setup_show_object(id: nil, object: nil, **options)
30
- raise ArgumentError, 'one of the following keywords is required: id, object' unless id || object
31
-
32
- options.merge! object: wrap_object(object || scope.resolve.find(id))
33
- end
34
-
35
- def setup_show_hooks
36
- self.class.i_setup_show_hooks || []
37
- end
38
-
39
- def show(**options)
40
- options[:action] = :show
41
- options = before_setup_show(**options)
42
- options = setup_show_object(**options)
43
- authorize(options[:object], :show?, **options)
44
- unwrap_object options[:object]
45
- end
46
-
47
- def setup_show(**options)
48
- options
49
- end
50
-
51
- private
52
-
53
- def before_setup_show(**options)
54
- options = setup(**options)
55
- options = setup_hooks.reduce(options) { |opts, hook| run_hook(hook, **opts) }
56
- options = setup_show(**options)
57
- setup_show_hooks.reduce(options) { |opts, hook| run_hook(hook, **opts) }
58
- end
59
- end
60
- end
61
- end
@@ -1,168 +0,0 @@
1
- # frozen_string_literal: true
2
-
3
- require 'active_support/concern'
4
- require_relative './setup_context'
5
- require_relative './change_context'
6
-
7
- module SnFoil
8
- module Contexts
9
- module UpdateContext # rubocop:disable Metrics/ModuleLength
10
- extend ActiveSupport::Concern
11
-
12
- included do
13
- include SetupContext
14
- include ChangeContext
15
- end
16
-
17
- class_methods do
18
- attr_reader :i_setup_update_hooks, :i_before_update_hooks, :i_after_update_hooks,
19
- :i_after_update_success_hooks, :i_after_update_failure_hooks
20
-
21
- def update(id:, params:, entity: nil, **options)
22
- new(entity).update(id: id, **options, params: params)
23
- end
24
-
25
- def setup_update(method = nil, **options, &block)
26
- raise ArgumentError, '#setup_update requires either a method name or a block' if method.nil? && block.nil?
27
-
28
- (@i_setup_update_hooks ||= []) << { method: method, block: block, if: options[:if], unless: options[:unless] }
29
- end
30
-
31
- def before_update(method = nil, **options, &block)
32
- raise ArgumentError, '#before_update requires either a method name or a block' if method.nil? && block.nil?
33
-
34
- (@i_before_update_hooks ||= []) << { method: method, block: block, if: options[:if], unless: options[:unless] }
35
- end
36
-
37
- def after_update(method = nil, **options, &block)
38
- raise ArgumentError, '#after_update requires either a method name or a block' if method.nil? && block.nil?
39
-
40
- (@i_after_update_hooks ||= []) << { method: method, block: block, if: options[:if], unless: options[:unless] }
41
- end
42
-
43
- def after_update_success(method = nil, **options, &block)
44
- raise ArgumentError, '#after_update_success requires either a method name or a block' if method.nil? && block.nil?
45
-
46
- (@i_after_update_success_hooks ||= []) << { method: method, block: block, if: options[:if], unless: options[:unless] }
47
- end
48
-
49
- def after_update_failure(method = nil, **options, &block)
50
- raise ArgumentError, '#after_update_failure requires either a method name or a block' if method.nil? && block.nil?
51
-
52
- (@i_after_update_failure_hooks ||= []) << { method: method, block: block, if: options[:if], unless: options[:unless] }
53
- end
54
- end
55
-
56
- def setup_update_object(params: {}, id: nil, object: nil, **options)
57
- raise ArgumentError, 'one of the following keywords is required: id, object' unless id || object
58
-
59
- object = wrap_object(object || scope.resolve.find(id))
60
- authorize(object, options.fetch(:authorize, :update?), **options)
61
-
62
- object.attributes = params
63
- options.merge! object: object
64
- end
65
-
66
- def update(**options)
67
- options[:action] = :update
68
- options = before_setup_update_object(**options)
69
- options = setup_update_object(**options)
70
- authorize(options[:object], options.fetch(:authorize, :update?), **options)
71
- options = update_hooks(**options)
72
- options[:object]
73
- end
74
-
75
- def setup_update(**options)
76
- options
77
- end
78
-
79
- def before_update(**options)
80
- options
81
- end
82
-
83
- def after_update(**options)
84
- options
85
- end
86
-
87
- def after_update_success(**options)
88
- options
89
- end
90
-
91
- def after_update_failure(**options)
92
- options
93
- end
94
-
95
- def setup_update_hooks
96
- self.class.i_setup_update_hooks || []
97
- end
98
-
99
- def before_update_hooks
100
- self.class.i_before_update_hooks || []
101
- end
102
-
103
- def after_update_hooks
104
- self.class.i_after_update_hooks || []
105
- end
106
-
107
- def after_update_success_hooks
108
- self.class.i_after_update_success_hooks || []
109
- end
110
-
111
- def after_update_failure_hooks
112
- self.class.i_after_update_failure_hooks || []
113
- end
114
-
115
- private
116
-
117
- def before_setup_update_object(**options)
118
- options = setup(**options)
119
- options = setup_hooks.reduce(options) { |opts, hook| run_hook(hook, **opts) }
120
- options = setup_change(**options)
121
- options = setup_change_hooks.reduce(options) { |opts, hook| run_hook(hook, **opts) }
122
- options = setup_update(**options)
123
- setup_update_hooks.reduce(options) { |opts, hook| run_hook(hook, **opts) }
124
- end
125
-
126
- # This method is private to help protect the order of execution of hooks
127
- def update_hooks(options)
128
- options = before_update_save(options)
129
- update_successful = options[:object].save
130
- options[:object] = unwrap_object(options[:object])
131
- options = if update_successful
132
- after_update_save_success(options)
133
- else
134
- after_update_save_failure(options)
135
- end
136
- after_update_save(options)
137
- end
138
-
139
- def before_update_save(options)
140
- options = before_change(**options)
141
- options = before_change_hooks.reduce(options) { |opts, hook| run_hook(hook, **opts) }
142
- options = before_update(**options)
143
- before_update_hooks.reduce(options) { |opts, hook| run_hook(hook, **opts) }
144
- end
145
-
146
- def after_update_save(options)
147
- options = after_change(**options)
148
- options = after_change_hooks.reduce(options) { |opts, hook| run_hook(hook, **opts) }
149
- options = after_update(**options)
150
- after_update_hooks.reduce(options) { |opts, hook| run_hook(hook, **opts) }
151
- end
152
-
153
- def after_update_save_success(options)
154
- options = after_change_success(**options)
155
- options = after_change_success_hooks.reduce(options) { |opts, hook| run_hook(hook, **opts) }
156
- options = after_update_success(**options)
157
- after_update_success_hooks.reduce(options) { |opts, hook| run_hook(hook, **opts) }
158
- end
159
-
160
- def after_update_save_failure(options)
161
- options = after_change_failure(**options)
162
- options = after_change_failure_hooks.reduce(options) { |opts, hook| run_hook(hook, **opts) }
163
- options = after_update_failure(**options)
164
- after_update_failure_hooks.reduce(options) { |opts, hook| run_hook(hook, **opts) }
165
- end
166
- end
167
- end
168
- end
@@ -1,55 +0,0 @@
1
- # frozen_string_literal: true
2
-
3
- require 'active_support/concern'
4
-
5
- module SnFoil
6
- module Policy
7
- extend ActiveSupport::Concern
8
-
9
- attr_reader :record, :entity
10
- attr_accessor :options
11
-
12
- def initialize(entity, record, options = {})
13
- @record = record
14
- @entity = entity
15
- @options = options
16
- end
17
-
18
- def index?
19
- false
20
- end
21
-
22
- def show?
23
- false
24
- end
25
-
26
- def create?
27
- false
28
- end
29
-
30
- def update?
31
- false
32
- end
33
-
34
- def destroy?
35
- false
36
- end
37
-
38
- def associate?
39
- false
40
- end
41
-
42
- class Scope
43
- attr_reader :scope, :entity
44
-
45
- def initialize(scope, entity = nil)
46
- @entity = entity
47
- @scope = scope
48
- end
49
-
50
- def resolve
51
- scope.all
52
- end
53
- end
54
- end
55
- end