snfoil 0.1.0

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.
@@ -0,0 +1,147 @@
1
+ # frozen_string_literal: true
2
+
3
+ require 'active_support/concern'
4
+ require_relative './setup_context_concern'
5
+ require_relative './change_context_concern'
6
+
7
+ module SnFoil
8
+ module Contexts
9
+ module CreateContextConcern # rubocop:disable Metrics/ModuleLength
10
+ extend ActiveSupport::Concern
11
+
12
+ included do
13
+ include SetupContextConcern
14
+ include ChangeContextConcern
15
+ end
16
+
17
+ class_methods do
18
+ attr_reader :i_before_create_hooks, :i_after_create_hooks, :i_after_create_success_hooks, :i_after_create_failure_hooks
19
+ def create(params:, user: nil, **options)
20
+ new(user).create(**options, params: params)
21
+ end
22
+
23
+ def before_create(method = nil, **options, &block)
24
+ raise ArgumentError, '#on_create requires either a method name or a block' if method.nil? && block.nil?
25
+
26
+ (@i_before_create_hooks ||= []) << { method: method, block: block, if: options[:if], unless: options[:unless] }
27
+ end
28
+
29
+ def after_create(method = nil, **options, &block)
30
+ raise ArgumentError, '#after_create requires either a method name or a block' if method.nil? && block.nil?
31
+
32
+ (@i_after_create_hooks ||= []) << { method: method, block: block, if: options[:if], unless: options[:unless] }
33
+ end
34
+
35
+ def after_create_success(method = nil, **options, &block)
36
+ raise ArgumentError, '#after_create_success requires either a method name or a block' if method.nil? && block.nil?
37
+
38
+ (@i_after_create_success_hooks ||= []) << { method: method, block: block, if: options[:if], unless: options[:unless] }
39
+ end
40
+
41
+ def after_create_failure(method = nil, **options, &block)
42
+ raise ArgumentError, '#after_create_failure requires either a method name or a block' if method.nil? && block.nil?
43
+
44
+ (@i_after_create_failure_hooks ||= []) << { method: method, block: block, if: options[:if], unless: options[:unless] }
45
+ end
46
+ end
47
+
48
+ def setup_create_object(params: {}, object: nil, **options)
49
+ object = if object
50
+ wrap_object(object)
51
+ else
52
+ klass = options.fetch(:model) { model }
53
+ wrap_object(klass).new
54
+ end
55
+
56
+ object.attributes = params
57
+ options.merge! object: object
58
+ end
59
+
60
+ def create(**options)
61
+ options[:action] = :create
62
+ options = setup_change(setup_create(**options))
63
+ options = setup_create_object(**options)
64
+ authorize(options[:object], :create?, **options)
65
+ options = create_hooks(**options)
66
+ unwrap_object(options[:object])
67
+ end
68
+
69
+ def setup_create(**options)
70
+ options
71
+ end
72
+
73
+ def before_create(**options)
74
+ options
75
+ end
76
+
77
+ def after_create(**options)
78
+ options
79
+ end
80
+
81
+ def after_create_success(**options)
82
+ options
83
+ end
84
+
85
+ def after_create_failure(**options)
86
+ options
87
+ end
88
+
89
+ def before_create_hooks
90
+ self.class.i_before_create_hooks || []
91
+ end
92
+
93
+ def after_create_hooks
94
+ self.class.i_after_create_hooks || []
95
+ end
96
+
97
+ def after_create_success_hooks
98
+ self.class.i_after_create_success_hooks || []
99
+ end
100
+
101
+ def after_create_failure_hooks
102
+ self.class.i_after_create_failure_hooks || []
103
+ end
104
+
105
+ private
106
+
107
+ # This method is private to help protect the order of execution of hooks
108
+ def create_hooks(options)
109
+ options = before_create_save(**options)
110
+ options = if options[:object].save
111
+ after_create_save_success(**options)
112
+ else
113
+ after_create_save_failure(**options)
114
+ end
115
+ after_create_save(**options)
116
+ end
117
+
118
+ def before_create_save(**options)
119
+ options = before_create(**options)
120
+ options = before_create_hooks.reduce(options) { |opts, hook| run_hook(hook, opts) }
121
+ options = before_change(**options)
122
+ before_change_hooks.reduce(options) { |opts, hook| run_hook(hook, opts) }
123
+ end
124
+
125
+ def after_create_save(**options)
126
+ options = after_create(**options)
127
+ options = after_create_hooks.reduce(options) { |opts, hook| run_hook(hook, opts) }
128
+ options = after_change(**options)
129
+ after_change_hooks.reduce(options) { |opts, hook| run_hook(hook, opts) }
130
+ end
131
+
132
+ def after_create_save_success(**options)
133
+ options = after_create_success(**options)
134
+ options = after_create_success_hooks.reduce(options) { |opts, hook| run_hook(hook, opts) }
135
+ options = after_change_success(**options)
136
+ after_change_success_hooks.reduce(options) { |opts, hook| run_hook(hook, opts) }
137
+ end
138
+
139
+ def after_create_save_failure(**options)
140
+ options = after_create_failure(**options)
141
+ options = after_create_failure_hooks.reduce(options) { |opts, hook| run_hook(hook, opts) }
142
+ options = after_change_failure(**options)
143
+ after_change_failure_hooks.reduce(options) { |opts, hook| run_hook(hook, opts) }
144
+ end
145
+ end
146
+ end
147
+ end
@@ -0,0 +1,141 @@
1
+ # frozen_string_literal: true
2
+
3
+ require 'active_support/concern'
4
+ require_relative './setup_context_concern'
5
+ require_relative './change_context_concern'
6
+
7
+ module SnFoil
8
+ module Contexts
9
+ module DestroyContextConcern
10
+ extend ActiveSupport::Concern
11
+
12
+ included do
13
+ include SetupContextConcern
14
+ include ChangeContextConcern
15
+ end
16
+
17
+ class_methods do
18
+ attr_reader :i_before_destroy_hooks, :i_after_destroy_hooks, :i_after_destroy_success_hooks, :i_after_destroy_failure_hooks
19
+ def destroy(id:, user: nil, **options)
20
+ new(user).destroy(**options, id: id)
21
+ end
22
+
23
+ def before_destroy(method = nil, **options, &block)
24
+ raise ArgumentError, '#on_destroy requires either a method name or a block' if method.nil? && block.nil?
25
+
26
+ (@i_before_destroy_hooks ||= []) << { method: method, block: block, if: options[:if], unless: options[:unless] }
27
+ end
28
+
29
+ def after_destroy(method = nil, **options, &block)
30
+ raise ArgumentError, '#after_destroy requires either a method name or a block' if method.nil? && block.nil?
31
+
32
+ (@i_after_destroy_hooks ||= []) << { method: method, block: block, if: options[:if], unless: options[:unless] }
33
+ end
34
+
35
+ def after_destroy_success(method = nil, **options, &block)
36
+ raise ArgumentError, '#after_destroy_success requires either a method name or a block' if method.nil? && block.nil?
37
+
38
+ (@i_after_destroy_success_hooks ||= []) << { method: method, block: block, if: options[:if], unless: options[:unless] }
39
+ end
40
+
41
+ def after_destroy_failure(method = nil, **options, &block)
42
+ raise ArgumentError, '#after_destroy_failure requires either a method name or a block' if method.nil? && block.nil?
43
+
44
+ (@i_after_destroy_failure_hooks ||= []) << { method: method, block: block, if: options[:if], unless: options[:unless] }
45
+ end
46
+ end
47
+
48
+ def setup_destroy_object(id: nil, object: nil, **options)
49
+ raise ArgumentError, 'one of the following keywords is required: id, object' unless id || object
50
+
51
+ options.merge! object: wrap_object(object || scope.resolve.find(id))
52
+ end
53
+
54
+ def destroy(**options)
55
+ options[:action] = :destroy
56
+ options = setup_destroy(setup_change(**options))
57
+ options = setup_destroy_object(**options)
58
+ authorize(options[:object], :destroy?, **options)
59
+ options = destroy_hooks(**options)
60
+ unwrap_object(options[:object])
61
+ end
62
+
63
+ def setup_destroy(**options)
64
+ options
65
+ end
66
+
67
+ def before_destroy(**options)
68
+ options
69
+ end
70
+
71
+ def after_destroy(**options)
72
+ options
73
+ end
74
+
75
+ def after_destroy_success(**options)
76
+ options
77
+ end
78
+
79
+ def after_destroy_failure(**options)
80
+ options
81
+ end
82
+
83
+ def before_destroy_hooks
84
+ self.class.i_before_destroy_hooks || []
85
+ end
86
+
87
+ def after_destroy_hooks
88
+ self.class.i_after_destroy_hooks || []
89
+ end
90
+
91
+ def after_destroy_success_hooks
92
+ self.class.i_after_destroy_success_hooks || []
93
+ end
94
+
95
+ def after_destroy_failure_hooks
96
+ self.class.i_after_destroy_failure_hooks || []
97
+ end
98
+
99
+ private
100
+
101
+ # This method is private to help protect the order of execution of hooks
102
+ def destroy_hooks(options)
103
+ options = before_destroy_save(options)
104
+ options = if options[:object].destroy
105
+ after_destroy_save_success(options)
106
+ else
107
+ after_destroy_save_failure(options)
108
+ end
109
+ after_destroy_save(options)
110
+ end
111
+
112
+ def before_destroy_save(options)
113
+ options = before_destroy(**options)
114
+ options = before_destroy_hooks.reduce(options) { |opts, hook| run_hook(hook, **opts) }
115
+ options = before_change(**options)
116
+ before_change_hooks.reduce(options) { |opts, hook| run_hook(hook, **opts) }
117
+ end
118
+
119
+ def after_destroy_save(options)
120
+ options = after_destroy(**options)
121
+ options = after_destroy_hooks.reduce(options) { |opts, hook| run_hook(hook, **opts) }
122
+ options = after_change(**options)
123
+ after_change_hooks.reduce(options) { |opts, hook| run_hook(hook, **opts) }
124
+ end
125
+
126
+ def after_destroy_save_success(options)
127
+ options = after_destroy_success(**options)
128
+ options = after_destroy_success_hooks.reduce(options) { |opts, hook| run_hook(hook, **opts) }
129
+ options = after_change_success(**options)
130
+ after_change_success_hooks.reduce(options) { |opts, hook| run_hook(hook, **opts) }
131
+ end
132
+
133
+ def after_destroy_save_failure(options)
134
+ options = after_destroy_failure(**options)
135
+ options = after_destroy_failure_hooks.reduce(options) { |opts, hook| run_hook(hook, **opts) }
136
+ options = after_change_failure(**options)
137
+ after_change_failure_hooks.reduce(options) { |opts, hook| run_hook(hook, **opts) }
138
+ end
139
+ end
140
+ end
141
+ end
@@ -0,0 +1,45 @@
1
+ # frozen_string_literal: true
2
+
3
+ require 'active_support/concern'
4
+ require_relative './change_context_concern'
5
+
6
+ module SnFoil
7
+ module Contexts
8
+ module IndexContextConcern
9
+ extend ActiveSupport::Concern
10
+
11
+ included do
12
+ include SetupContextConcern
13
+ end
14
+
15
+ class_methods do
16
+ attr_reader :i_searcher
17
+
18
+ def index(params: {}, user: nil, **options)
19
+ new(user).index(**options, params: params)
20
+ end
21
+
22
+ def searcher(klass = nil)
23
+ @i_searcher = klass
24
+ end
25
+ end
26
+
27
+ def searcher
28
+ self.class.i_searcher
29
+ end
30
+
31
+ def index(params:, **options)
32
+ options[:action] = :index
33
+ options = setup_index(**options)
34
+ options.fetch(:searcher) { searcher }
35
+ .new(scope: scope.resolve)
36
+ .search(params: params)
37
+ end
38
+
39
+ # Param manipulation based on User should be done here
40
+ def setup_index(**options)
41
+ options
42
+ end
43
+ end
44
+ end
45
+ end
@@ -0,0 +1,80 @@
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 SetupContextConcern
9
+ extend ActiveSupport::Concern
10
+
11
+ class_methods do
12
+ attr_reader :i_model, :i_policy
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
+ end
22
+
23
+ def model
24
+ self.class.i_model
25
+ end
26
+
27
+ def policy
28
+ self.class.i_policy
29
+ end
30
+
31
+ attr_reader :user
32
+ def initialize(user = nil)
33
+ @user = user
34
+ end
35
+
36
+ def authorize(object, action, **options)
37
+ return unless user # Add logging
38
+
39
+ lookup_policy(object, options).send(action)
40
+ end
41
+
42
+ def scope(object_class = nil, **options)
43
+ object_class ||= model
44
+ policy_name = lookup_policy(object_class, options).class.name
45
+ "#{policy_name}::Scope".safe_constantize.new(wrap_object(object_class), user)
46
+ end
47
+
48
+ def wrap_object(object)
49
+ return object unless adapter
50
+
51
+ adapter.new(object)
52
+ end
53
+
54
+ def unwrap_object(object)
55
+ return object unless adapter
56
+
57
+ adapter?(object) ? object.__getobj__ : object
58
+ end
59
+
60
+ def adapter?(object)
61
+ return false unless adapter
62
+
63
+ object.instance_of? adapter
64
+ end
65
+
66
+ def adapter
67
+ @adapter ||= SnFoil.adapter
68
+ end
69
+
70
+ private
71
+
72
+ def lookup_policy(object, options)
73
+ return options[:policy].new(user, object) if options[:policy]
74
+ return policy.new(user, object) if policy
75
+
76
+ Pundit.policy!(user, object)
77
+ end
78
+ end
79
+ end
80
+ end
@@ -0,0 +1,35 @@
1
+ # frozen_string_literal: true
2
+
3
+ require 'active_support/concern'
4
+ require_relative './setup_context_concern'
5
+
6
+ module SnFoil
7
+ module Contexts
8
+ module ShowContextConcern
9
+ extend ActiveSupport::Concern
10
+
11
+ included do
12
+ include SetupContextConcern
13
+ end
14
+
15
+ class_methods do
16
+ def show(id:, user: nil, **options)
17
+ new(user).show(**options, id: id)
18
+ end
19
+ end
20
+
21
+ def setup_show_object(id: nil, object: nil, **options)
22
+ raise ArgumentError, 'one of the following keywords is required: id, object' unless id || object
23
+
24
+ options.merge! object: wrap_object(object || scope.resolve.find(id))
25
+ end
26
+
27
+ def show(**options)
28
+ options[:action] = :show
29
+ options = setup_show_object(**options)
30
+ authorize(options[:object], :show?, **options)
31
+ unwrap_object options[:object]
32
+ end
33
+ end
34
+ end
35
+ end
@@ -0,0 +1,144 @@
1
+ # frozen_string_literal: true
2
+
3
+ require 'active_support/concern'
4
+ require_relative './setup_context_concern'
5
+ require_relative './change_context_concern'
6
+
7
+ module SnFoil
8
+ module Contexts
9
+ module UpdateContextConcern # rubocop:disable Metrics/ModuleLength
10
+ extend ActiveSupport::Concern
11
+
12
+ included do
13
+ include SetupContextConcern
14
+ include ChangeContextConcern
15
+ end
16
+
17
+ class_methods do
18
+ attr_reader :i_before_update_hooks, :i_after_update_hooks, :i_after_update_success_hooks, :i_after_update_failure_hooks
19
+ def update(id:, params:, user: nil, **options)
20
+ new(user).update(**options, id: id, params: params)
21
+ end
22
+
23
+ def before_update(method = nil, **options, &block)
24
+ raise ArgumentError, '#on_update requires either a method name or a block' if method.nil? && block.nil?
25
+
26
+ (@i_before_update_hooks ||= []) << { method: method, block: block, if: options[:if], unless: options[:unless] }
27
+ end
28
+
29
+ def after_update(method = nil, **options, &block)
30
+ raise ArgumentError, '#after_update requires either a method name or a block' if method.nil? && block.nil?
31
+
32
+ (@i_after_update_hooks ||= []) << { method: method, block: block, if: options[:if], unless: options[:unless] }
33
+ end
34
+
35
+ def after_update_success(method = nil, **options, &block)
36
+ raise ArgumentError, '#after_update_success requires either a method name or a block' if method.nil? && block.nil?
37
+
38
+ (@i_after_update_success_hooks ||= []) << { method: method, block: block, if: options[:if], unless: options[:unless] }
39
+ end
40
+
41
+ def after_update_failure(method = nil, **options, &block)
42
+ raise ArgumentError, '#after_update_failure requires either a method name or a block' if method.nil? && block.nil?
43
+
44
+ (@i_after_update_failure_hooks ||= []) << { method: method, block: block, if: options[:if], unless: options[:unless] }
45
+ end
46
+ end
47
+
48
+ def setup_update_object(params: {}, id: nil, object: nil, **options)
49
+ raise ArgumentError, 'one of the following keywords is required: id, object' unless id || object
50
+
51
+ object = wrap_object(object || scope.resolve.find(id))
52
+ authorize(object, :update?, **options)
53
+ object.attributes = params
54
+ options.merge! object: object
55
+ end
56
+
57
+ def update(**options)
58
+ options[:action] = :update
59
+ options = setup_change(setup_update(**options))
60
+ options = setup_update_object(**options)
61
+ authorize(options[:object], :update?, **options)
62
+ options = update_hooks(**options)
63
+ unwrap_object(options[:object])
64
+ end
65
+
66
+ def setup_update(**options)
67
+ options
68
+ end
69
+
70
+ def before_update(**options)
71
+ options
72
+ end
73
+
74
+ def after_update(**options)
75
+ options
76
+ end
77
+
78
+ def after_update_success(**options)
79
+ options
80
+ end
81
+
82
+ def after_update_failure(**options)
83
+ options
84
+ end
85
+
86
+ def before_update_hooks
87
+ self.class.i_before_update_hooks || []
88
+ end
89
+
90
+ def after_update_hooks
91
+ self.class.i_after_update_hooks || []
92
+ end
93
+
94
+ def after_update_success_hooks
95
+ self.class.i_after_update_success_hooks || []
96
+ end
97
+
98
+ def after_update_failure_hooks
99
+ self.class.i_after_update_failure_hooks || []
100
+ end
101
+
102
+ private
103
+
104
+ # This method is private to help protect the order of execution of hooks
105
+ def update_hooks(options)
106
+ options = before_update_save(options)
107
+ options = if options[:object].save
108
+ after_update_save_success(options)
109
+ else
110
+ after_update_save_failure(options)
111
+ end
112
+ after_update_save(options)
113
+ end
114
+
115
+ def before_update_save(options)
116
+ options = before_update(**options)
117
+ options = before_update_hooks.reduce(options) { |opts, hook| run_hook(hook, **opts) }
118
+ options = before_change(**options)
119
+ before_change_hooks.reduce(options) { |opts, hook| run_hook(hook, **opts) }
120
+ end
121
+
122
+ def after_update_save(options)
123
+ options = after_update(**options)
124
+ options = after_update_hooks.reduce(options) { |opts, hook| run_hook(hook, **opts) }
125
+ options = after_change(**options)
126
+ after_change_hooks.reduce(options) { |opts, hook| run_hook(hook, **opts) }
127
+ end
128
+
129
+ def after_update_save_success(options)
130
+ options = after_update_success(**options)
131
+ options = after_update_success_hooks.reduce(options) { |opts, hook| run_hook(hook, **opts) }
132
+ options = after_change_success(**options)
133
+ after_change_success_hooks.reduce(options) { |opts, hook| run_hook(hook, **opts) }
134
+ end
135
+
136
+ def after_update_save_failure(options)
137
+ options = after_update_failure(**options)
138
+ options = after_update_failure_hooks.reduce(options) { |opts, hook| run_hook(hook, **opts) }
139
+ options = after_change_failure(**options)
140
+ after_change_failure_hooks.reduce(options) { |opts, hook| run_hook(hook, **opts) }
141
+ end
142
+ end
143
+ end
144
+ end
@@ -0,0 +1,48 @@
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
+ def initialize(record, entity = nil)
11
+ @record = record
12
+ @entity = entity
13
+ end
14
+
15
+ def index?
16
+ false
17
+ end
18
+
19
+ def show?
20
+ false
21
+ end
22
+
23
+ def create?
24
+ false
25
+ end
26
+
27
+ def update?
28
+ false
29
+ end
30
+
31
+ def destroy?
32
+ false
33
+ end
34
+
35
+ class Scope
36
+ attr_reader :scope, :entity
37
+
38
+ def initialize(scope, entity = nil)
39
+ @entity = entity
40
+ @scope = scope
41
+ end
42
+
43
+ def resolve
44
+ scope.all
45
+ end
46
+ end
47
+ end
48
+ end