granite 0.9.9 → 0.12.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.
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: c916196599f14f9af76a9a550ffb51e7e4db5fd97deee1cf7cf04135c4bfb217
4
- data.tar.gz: 6fc2f4a436734b34eb2096fa063a0d82e6fe6bf58afbb04af07b1a86d82d8727
3
+ metadata.gz: 8f01fbcec194a3031c22582899a7ff0210d2b621aa149a5ccf7814a3a70540ea
4
+ data.tar.gz: c763544b18d4feb43a5502df2e661dce313c304f47543978be095e4e72afc863
5
5
  SHA512:
6
- metadata.gz: 0bd7b860deb8221df8be1e1227df3c71462862a47ba9243ec7ebc3be1972aeaa95b5c14334ab45aa372f7fe45fc89ed7b3af79a9fb52c3ed5f3e3908981611af
7
- data.tar.gz: 6f164dc00ce5e56be2bff2bd01f22b49d37996decc05b74816a129f1598451d0dbbb184c1c7d02cac90e52eca80e947e8cba6932dba6842a030ce7801864ba0d
6
+ metadata.gz: '099c76956322fe11a1118dc25a1fffbc0553018d1abf660d7c6270256f40505a3d93be01e57df0d7502c767c09d961fcf3361a7281315296e6abb9c512a38a9f'
7
+ data.tar.gz: 9b1b66eca75f4ff623319d1828cbec2b987c36050fb70598a8a14ea7edd760da38eb61c89f0a4de3b706875b68a16dccefe22333c3ac8edbe5b80864e04f4aca
@@ -6,7 +6,8 @@ module Granite
6
6
  end
7
7
 
8
8
  def translate(*args, **options)
9
- super(*Granite::Translations.scope_translation_args(i18n_scopes, *args, **options))
9
+ key, options = Granite::Translations.scope_translation_args(i18n_scopes, *args, **options)
10
+ super(key, **options)
10
11
  end
11
12
 
12
13
  alias t translate
@@ -42,7 +42,7 @@ module Granite
42
42
  # @return [Object] result of execute_perform! method execution or false in case of errors
43
43
  def perform(context: nil, **options)
44
44
  transaction do
45
- valid?(context) && perform_action(options)
45
+ valid?(context) && perform_action(**options)
46
46
  end
47
47
  end
48
48
 
@@ -94,7 +94,7 @@ module Granite
94
94
  def perform_action(raise_errors: false, **options)
95
95
  result = run_callbacks(:execute_perform) do
96
96
  apply_association_changes!
97
- execute_perform!(options)
97
+ execute_perform!(**options)
98
98
  end
99
99
  @_action_performed = true
100
100
  result || true
@@ -55,17 +55,17 @@ module Granite
55
55
  end
56
56
  end
57
57
 
58
- def try_perform!(*)
58
+ def try_perform!(*, **)
59
59
  authorize!
60
60
  super
61
61
  end
62
62
 
63
- def perform(*)
63
+ def perform(*, **)
64
64
  authorize!
65
65
  super
66
66
  end
67
67
 
68
- def perform!(*)
68
+ def perform!(*, **)
69
69
  authorize!
70
70
  super
71
71
  end
@@ -9,7 +9,7 @@ module Granite
9
9
  end
10
10
 
11
11
  def execute!(context)
12
- _execute(context) if context.conditions_satisfied?(@options)
12
+ _execute(context) if context.conditions_satisfied?(**@options)
13
13
  end
14
14
 
15
15
  private
@@ -51,15 +51,7 @@ module Granite
51
51
  elsif args.first.is_a?(Class)
52
52
  add_precondition(ObjectPrecondition, *args, options)
53
53
  else
54
- common_options = options.extract!(:if, :unless, :desc, :description)
55
- args.each do |type|
56
- precondition common_options.merge(type => {})
57
- end
58
- options.each do |key, value|
59
- value = Array.wrap(value)
60
- precondition_options = value.extract_options!
61
- add_precondition(klass(key), *value, precondition_options.merge!(common_options))
62
- end
54
+ add_preconditions_hash(*args, **options)
63
55
  end
64
56
  end
65
57
 
@@ -72,6 +64,18 @@ module Granite
72
64
  end || fail(NameError, "No precondition class for #{key}Precondition")
73
65
  end
74
66
 
67
+ def add_preconditions_hash(*args, **options)
68
+ common_options = options.extract!(:if, :unless, :desc, :description)
69
+ args.each do |type|
70
+ precondition common_options.merge(type => {})
71
+ end
72
+ options.each do |key, value|
73
+ value = Array.wrap(value)
74
+ precondition_options = value.extract_options!
75
+ add_precondition(klass(key), *value, precondition_options.merge!(common_options))
76
+ end
77
+ end
78
+
75
79
  def add_precondition(klass, *args, &block)
76
80
  self._preconditions += klass.new(*args, &block)
77
81
  end
@@ -16,7 +16,8 @@ module Granite
16
16
  end
17
17
 
18
18
  def translate(*args, **options)
19
- I18n.translate(*Granite::Translations.scope_translation_args(self.class.i18n_scopes, *args, **options))
19
+ key, options = Granite::Translations.scope_translation_args(self.class.i18n_scopes, *args, **options)
20
+ I18n.translate(key, **options)
20
21
  end
21
22
  alias t translate
22
23
  end
@@ -50,20 +50,28 @@ module Granite
50
50
  prepend AssignAttributes
51
51
 
52
52
  handle_exception ActiveRecord::RecordInvalid do |e|
53
- errors.messages.deep_merge!(e.record.errors.messages) do |_, this, other|
54
- (this + other).uniq
55
- end
53
+ merge_errors(e.record.errors)
56
54
  end
57
55
 
58
56
  handle_exception ActiveData::ValidationError do |e|
59
- errors.messages.deep_merge!(e.model.errors.messages) do |_, this, other|
60
- (this + other).uniq
61
- end
57
+ merge_errors(e.model.errors)
62
58
  end
63
59
 
64
60
  handle_exception Granite::Action::ValidationError do |e|
65
- errors.messages.deep_merge!(e.action.errors.messages) do |_, this, other|
66
- (this + other).uniq
61
+ merge_errors(e.action.errors)
62
+ end
63
+
64
+ if ActiveModel.version < Gem::Version.new('6.1.0')
65
+ def merge_errors(other_errors)
66
+ errors.messages.deep_merge!(other_errors.messages) do |_, this, other|
67
+ (this + other).uniq
68
+ end
69
+ end
70
+ else
71
+ def merge_errors(other_errors)
72
+ other_errors.each do |error|
73
+ errors.import(error) unless errors.added?(error.attribute, error)
74
+ end
67
75
  end
68
76
  end
69
77
 
@@ -0,0 +1,40 @@
1
+ module Granite
2
+ module AssignData
3
+ DataAssignment = Struct.new(:method, :options)
4
+
5
+ extend ActiveSupport::Concern
6
+
7
+ included do
8
+ class_attribute :data_assignments
9
+ self.data_assignments = []
10
+
11
+ alias_method :only_run_validations!, :run_validations!
12
+ protected :only_run_validations! # rubocop:disable Style/AccessModifierDeclarations
13
+ end
14
+
15
+ module ClassMethods
16
+ # Defines a callback to call when assigning data from business action to model.
17
+ # @param methods [Array<Symbol>] list of methods to call
18
+ # @param block [Proc] a block to call
19
+ # @option options [Symbol, Proc, Object] :if call methods/block if this condition evaluates to true
20
+ # @option options [Symbol, Proc, Object] :unless call method/block unless this condition evaluates to true
21
+ def assign_data(*methods, **options, &block)
22
+ self.data_assignments += methods.map { |method| DataAssignment.new(method, options) }
23
+ self.data_assignments += [DataAssignment.new(block, options)] if block
24
+ end
25
+ end
26
+
27
+ protected
28
+
29
+ def assign_data
30
+ data_assignments.each { |assignment| evaluate(assignment.method) if conditions_satisfied?(**assignment.options) }
31
+ end
32
+
33
+ private
34
+
35
+ def run_validations!
36
+ assign_data
37
+ super
38
+ end
39
+ end
40
+ end
data/lib/granite/base.rb CHANGED
@@ -5,8 +5,11 @@ require 'active_data/model/associations'
5
5
 
6
6
  require 'granite/translations'
7
7
  require 'granite/represents'
8
+ require 'granite/assign_data'
8
9
 
9
10
  module Granite
11
+ # Base included in Granite::Action, but also used by ActiveData when building data objects (e.g. when using
12
+ # embeds_many)
10
13
  module Base
11
14
  extend ActiveSupport::Concern
12
15
 
@@ -19,6 +22,7 @@ module Granite
19
22
  include ActiveModel::Validations::Callbacks
20
23
 
21
24
  include Granite::Util
25
+ include Granite::AssignData
22
26
  include Granite::Represents
23
27
  end
24
28
  end
@@ -25,7 +25,7 @@ class Granite::Dispatcher
25
25
  end
26
26
 
27
27
  def controller(params, *_args)
28
- projector(params.slice(:granite_action, :granite_projector).symbolize_keys)&.controller_class
28
+ projector(*params.values_at(:granite_action, :granite_projector))&.controller_class
29
29
  end
30
30
 
31
31
  def prepare_params!(params, *_args)
@@ -39,19 +39,19 @@ class Granite::Dispatcher
39
39
  controller(req.params),
40
40
  action_name(
41
41
  req.request_method_symbol,
42
- req.params.slice(:granite_action, :granite_projector, :projector_action).symbolize_keys
42
+ *req.params.values_at(:granite_action, :granite_projector, :projector_action)
43
43
  )
44
44
  ]
45
45
  end
46
46
 
47
- memoize def action_name(request_method_symbol, granite_action:, granite_projector:, projector_action: '')
48
- projector = projector(granite_action: granite_action, granite_projector: granite_projector)
47
+ memoize def action_name(request_method_symbol, granite_action, granite_projector, projector_action)
48
+ projector = projector(granite_action, granite_projector)
49
49
  return unless projector
50
50
 
51
- projector.action_for(request_method_symbol, projector_action)
51
+ projector.action_for(request_method_symbol, projector_action.to_s)
52
52
  end
53
53
 
54
- memoize def projector(granite_action:, granite_projector:)
54
+ memoize def projector(granite_action, granite_projector)
55
55
  action = business_action(granite_action)
56
56
 
57
57
  action.public_send(granite_projector) if action.respond_to?(granite_projector)
@@ -23,11 +23,11 @@ module Granite
23
23
  controller_class.__send__(:define_method, name, &block)
24
24
  class_eval <<-METHOD, __FILE__, __LINE__ + 1
25
25
  def #{name}_url(options = {})
26
- action_url(:#{name}, options.symbolize_keys)
26
+ action_url(:#{name}, **options.symbolize_keys)
27
27
  end
28
28
 
29
29
  def #{name}_path(options = {})
30
- action_path(:#{name}, options.symbolize_keys)
30
+ action_path(:#{name}, **options.symbolize_keys)
31
31
  end
32
32
  METHOD
33
33
  else
@@ -8,7 +8,8 @@ module Granite
8
8
  end
9
9
 
10
10
  def translate(*args, **options)
11
- super(*Granite::Translations.scope_translation_args(i18n_scopes, *args, **options))
11
+ key, options = Granite::Translations.scope_translation_args(i18n_scopes, *args, **options)
12
+ super(key, **options)
12
13
  end
13
14
 
14
15
  alias t translate
@@ -34,7 +34,7 @@ module Granite
34
34
  end
35
35
 
36
36
  def changed?
37
- if reflection.options[:default].present?
37
+ if reflection.options.key?(:default)
38
38
  reference.public_send(reader) != read
39
39
  else
40
40
  owner.public_send("#{name}_changed?")
@@ -13,10 +13,7 @@ module Granite
13
13
  fields.each do |field|
14
14
  add_attribute Granite::Represents::Reflection, field, options, &block
15
15
 
16
- before_validation do
17
- attribute(field).sync if attribute(field).changed?
18
- true
19
- end
16
+ assign_data { attribute(field).sync if attribute(field).changed? }
20
17
  end
21
18
  end
22
19
  end
data/lib/granite/util.rb CHANGED
@@ -26,7 +26,7 @@ module Granite
26
26
  # @option options :if method name or callable
27
27
  # @option options :unless method name or callable
28
28
  # @return [Boolean] whether conditions are satisfied
29
- def conditions_satisfied?(options)
29
+ def conditions_satisfied?(**options)
30
30
  fail ArgumentError, 'You cannot specify both if and unless' if options.key?(:if) && options.key?(:unless)
31
31
 
32
32
  if options.key?(:if)
@@ -1,3 +1,3 @@
1
1
  module Granite
2
- VERSION = '0.9.9'.freeze
2
+ VERSION = '0.12.0'.freeze
3
3
  end
metadata CHANGED
@@ -1,14 +1,14 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: granite
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.9.9
4
+ version: 0.12.0
5
5
  platform: ruby
6
6
  authors:
7
- - Arkadiy Zabazhanov & friends
8
- autorequire:
7
+ - Toptal Engineering
8
+ autorequire:
9
9
  bindir: bin
10
10
  cert_chain: []
11
- date: 2021-06-21 00:00:00.000000000 Z
11
+ date: 2021-12-23 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: actionpack
@@ -19,7 +19,7 @@ dependencies:
19
19
  version: '5.1'
20
20
  - - "<"
21
21
  - !ruby/object:Gem::Version
22
- version: '6.1'
22
+ version: '7.1'
23
23
  type: :runtime
24
24
  prerelease: false
25
25
  version_requirements: !ruby/object:Gem::Requirement
@@ -29,21 +29,21 @@ dependencies:
29
29
  version: '5.1'
30
30
  - - "<"
31
31
  - !ruby/object:Gem::Version
32
- version: '6.1'
32
+ version: '7.1'
33
33
  - !ruby/object:Gem::Dependency
34
34
  name: active_data
35
35
  requirement: !ruby/object:Gem::Requirement
36
36
  requirements:
37
37
  - - "~>"
38
38
  - !ruby/object:Gem::Version
39
- version: 1.1.5
39
+ version: 1.2.0
40
40
  type: :runtime
41
41
  prerelease: false
42
42
  version_requirements: !ruby/object:Gem::Requirement
43
43
  requirements:
44
44
  - - "~>"
45
45
  - !ruby/object:Gem::Version
46
- version: 1.1.5
46
+ version: 1.2.0
47
47
  - !ruby/object:Gem::Dependency
48
48
  name: activesupport
49
49
  requirement: !ruby/object:Gem::Requirement
@@ -53,7 +53,7 @@ dependencies:
53
53
  version: '5.1'
54
54
  - - "<"
55
55
  - !ruby/object:Gem::Version
56
- version: '6.1'
56
+ version: '7.1'
57
57
  type: :runtime
58
58
  prerelease: false
59
59
  version_requirements: !ruby/object:Gem::Requirement
@@ -63,7 +63,7 @@ dependencies:
63
63
  version: '5.1'
64
64
  - - "<"
65
65
  - !ruby/object:Gem::Version
66
- version: '6.1'
66
+ version: '7.1'
67
67
  - !ruby/object:Gem::Dependency
68
68
  name: memoist
69
69
  requirement: !ruby/object:Gem::Requirement
@@ -84,20 +84,28 @@ dependencies:
84
84
  requirements:
85
85
  - - ">="
86
86
  - !ruby/object:Gem::Version
87
- version: '5.0'
88
- - - "<"
89
- - !ruby/object:Gem::Version
90
- version: '6.1'
87
+ version: '0'
91
88
  type: :development
92
89
  prerelease: false
93
90
  version_requirements: !ruby/object:Gem::Requirement
94
91
  requirements:
95
92
  - - ">="
96
93
  - !ruby/object:Gem::Version
97
- version: '5.0'
98
- - - "<"
94
+ version: '0'
95
+ - !ruby/object:Gem::Dependency
96
+ name: appraisal
97
+ requirement: !ruby/object:Gem::Requirement
98
+ requirements:
99
+ - - ">="
99
100
  - !ruby/object:Gem::Version
100
- version: '6.1'
101
+ version: '0'
102
+ type: :development
103
+ prerelease: false
104
+ version_requirements: !ruby/object:Gem::Requirement
105
+ requirements:
106
+ - - ">="
107
+ - !ruby/object:Gem::Version
108
+ version: '0'
101
109
  - !ruby/object:Gem::Dependency
102
110
  name: capybara
103
111
  requirement: !ruby/object:Gem::Requirement
@@ -294,8 +302,8 @@ dependencies:
294
302
  - - "~>"
295
303
  - !ruby/object:Gem::Version
296
304
  version: '0.15'
297
- description:
298
- email:
305
+ description:
306
+ email:
299
307
  executables: []
300
308
  extensions: []
301
309
  extra_rdoc_files: []
@@ -334,6 +342,7 @@ files:
334
342
  - lib/granite/action/translations.rb
335
343
  - lib/granite/action/types.rb
336
344
  - lib/granite/action/types/collection.rb
345
+ - lib/granite/assign_data.rb
337
346
  - lib/granite/base.rb
338
347
  - lib/granite/config.rb
339
348
  - lib/granite/context.rb
@@ -374,7 +383,7 @@ homepage: https://github.com/toptal/granite
374
383
  licenses:
375
384
  - MIT
376
385
  metadata: {}
377
- post_install_message:
386
+ post_install_message:
378
387
  rdoc_options: []
379
388
  require_paths:
380
389
  - lib
@@ -389,8 +398,8 @@ required_rubygems_version: !ruby/object:Gem::Requirement
389
398
  - !ruby/object:Gem::Version
390
399
  version: '0'
391
400
  requirements: []
392
- rubygems_version: 3.0.9
393
- signing_key:
401
+ rubygems_version: 3.2.29
402
+ signing_key:
394
403
  specification_version: 4
395
404
  summary: Another business actions architecture for Rails apps
396
405
  test_files: []