granite 0.9.5 → 0.10.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: a5ed01c6853ba3bf3e13e4881c89aac4bdaa5d58737237878602df69ae494bba
4
- data.tar.gz: 8c4a75737a577c8f1fb36a8fbb12413aaa0d2d5722e990135f43d6cea52ef619
3
+ metadata.gz: 6899733124529783eda1187a978475dea09fe5c716349d50f4dbf02f0c70a90c
4
+ data.tar.gz: 81113bf6131ba438bb21af75c0b870ca3e0f8e07cbd8d754c274c259311ae640
5
5
  SHA512:
6
- metadata.gz: e8b7af99f9099f4e2b49bcd1a6addbab8d03d0439371d65365b652d529b622312ce4ae9c553d55222ddaf6b91b4ba9f0562c5fc15c27834f7c4c306e70c485ed
7
- data.tar.gz: 939fb9e924c7a14b08430d85b83b864ae8afb7880f8476b58ecc61e7ec23c6c1906c87ab73d1542f9b25cab701dd54bb75be308e982b3d25f6b528759e628aee
6
+ metadata.gz: 1f6715806d76f01ec2f017c83bacecc3a33dc3ed9099493a2f245f84eadabe87abe5835bcfcb16fda8fc2f5b71022c84053291a1f4d4c829691cfaf216068d46
7
+ data.tar.gz: ec7f880050ff0a6b7de11d576d22038d5dcca6d0e3e71c2c7a2c4adb59280f153e05210bf7c7e4df519ddada2d5d337c12fdec34c3fddb060cf5dd82520ae3ff
@@ -1,9 +1,9 @@
1
- require 'granite/projector/translations/helper'
2
1
  require 'action_controller'
3
2
 
4
3
  module Granite
5
4
  class Controller < Granite.base_controller_class
6
- include Granite::Projector::Translations::Helper
5
+ include Controller::Translations
6
+ helper Controller::Translations
7
7
 
8
8
  singleton_class.__send__(:attr_accessor, :projector_class)
9
9
  singleton_class.delegate :projector_path, :projector_name, :action_class, to: :projector_class
@@ -0,0 +1,16 @@
1
+ module Granite
2
+ class Controller
3
+ module Translations
4
+ def i18n_scopes
5
+ Granite::Translations.combine_paths(projector.i18n_scopes, [*action_name, nil])
6
+ end
7
+
8
+ def translate(*args, **options)
9
+ key, options = Granite::Translations.scope_translation_args(i18n_scopes, *args, **options)
10
+ super(key, **options)
11
+ end
12
+
13
+ alias t translate
14
+ end
15
+ end
16
+ end
@@ -0,0 +1,3 @@
1
+ Lint/UselessAccessModifier:
2
+ ContextCreatingMethods:
3
+ - projector
data/lib/granite.rb CHANGED
@@ -4,6 +4,7 @@ require 'action_controller'
4
4
  require 'granite/version'
5
5
  require 'granite/config'
6
6
  require 'granite/context'
7
+ require 'granite/util'
7
8
 
8
9
  module Granite
9
10
  def self.config
@@ -7,10 +7,12 @@ require 'granite/action/types'
7
7
  require 'granite/action/error'
8
8
  require 'granite/action/performing'
9
9
  require 'granite/action/performer'
10
+ require 'granite/action/precondition'
10
11
  require 'granite/action/preconditions'
11
12
  require 'granite/action/policies'
12
13
  require 'granite/action/projectors'
13
14
  require 'granite/action/subject'
15
+ require 'granite/action/translations'
14
16
 
15
17
  module Granite
16
18
  class Action
@@ -38,6 +40,7 @@ module Granite
38
40
  end
39
41
 
40
42
  include Base
43
+ include Translations
41
44
  include Performing
42
45
  include Subject
43
46
  include Performer
@@ -64,10 +67,6 @@ module Granite
64
67
  end
65
68
  end
66
69
 
67
- def self.i18n_scope
68
- :granite_action
69
- end
70
-
71
70
  # Almost the same as Dirty `#changed?` method, but
72
71
  # doesn't check subject reference key
73
72
  def attributes_changed?(except: [])
@@ -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
@@ -0,0 +1,38 @@
1
+ module Granite
2
+ class Action
3
+ class Precondition < BasicObject
4
+ UNDEFINED = ::Object.new.freeze
5
+
6
+ def self.description(text = UNDEFINED)
7
+ case text
8
+ when UNDEFINED
9
+ @description
10
+ else
11
+ @description = text
12
+ end
13
+ end
14
+
15
+ def initialize(context)
16
+ @context = context
17
+ end
18
+
19
+ def call(*)
20
+ fail NotImplementedError, "#call method must be implemented for #{self.class}"
21
+ end
22
+
23
+ def method_missing(method_name, *args, &blk)
24
+ super unless @context.respond_to?(method_name)
25
+
26
+ @context.__send__(method_name, *args, &blk)
27
+ end
28
+
29
+ def respond_to_missing?(method_name, _include_private = false)
30
+ @context.respond_to?(method_name)
31
+ end
32
+
33
+ private
34
+
35
+ attr_reader :context
36
+ end
37
+ end
38
+ end
@@ -1,5 +1,6 @@
1
1
  require 'granite/action/preconditions/base_precondition'
2
2
  require 'granite/action/preconditions/embedded_precondition'
3
+ require 'granite/action/preconditions/object_precondition'
3
4
 
4
5
  module Granite
5
6
  class Action
@@ -13,6 +14,10 @@ module Granite
13
14
  extend ActiveSupport::Concern
14
15
 
15
16
  class PreconditionsCollection
17
+ include Enumerable
18
+
19
+ delegate :each, to: :@preconditions
20
+
16
21
  def initialize(*preconditions)
17
22
  @preconditions = preconditions.flatten
18
23
  end
@@ -43,16 +48,10 @@ module Granite
43
48
  options = args.extract_options!
44
49
  if block
45
50
  add_precondition(BasePrecondition, options, &block)
51
+ elsif args.first.is_a?(Class)
52
+ add_precondition(ObjectPrecondition, *args, options)
46
53
  else
47
- common_options = options.extract!(:if, :unless)
48
- args.each do |type|
49
- precondition common_options.merge(type => {})
50
- end
51
- options.each do |key, value|
52
- value = Array.wrap(value)
53
- precondition_options = value.extract_options!
54
- add_precondition(klass(key), *value, precondition_options.merge!(common_options))
55
- end
54
+ add_preconditions_hash(*args, **options)
56
55
  end
57
56
  end
58
57
 
@@ -65,6 +64,18 @@ module Granite
65
64
  end || fail(NameError, "No precondition class for #{key}Precondition")
66
65
  end
67
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
+
68
79
  def add_precondition(klass, *args, &block)
69
80
  self._preconditions += klass.new(*args, &block)
70
81
  end
@@ -9,10 +9,7 @@ module Granite
9
9
  end
10
10
 
11
11
  def execute!(context)
12
- return if @options[:if] && !context.instance_exec(&@options[:if])
13
- return if @options[:unless] && context.instance_exec(&@options[:unless])
14
-
15
- _execute(context)
12
+ _execute(context) if context.conditions_satisfied?(**@options)
16
13
  end
17
14
 
18
15
  private
@@ -0,0 +1,15 @@
1
+ require 'granite/action/preconditions/base_precondition'
2
+
3
+ module Granite
4
+ class Action
5
+ module Preconditions
6
+ class ObjectPrecondition < BasePrecondition
7
+ private
8
+
9
+ def _execute(context)
10
+ @args.first.new(context).call(**@options.except(:if, :unless))
11
+ end
12
+ end
13
+ end
14
+ end
15
+ end
@@ -0,0 +1,25 @@
1
+ module Granite
2
+ class Action
3
+ module Translations
4
+ extend ActiveSupport::Concern
5
+
6
+ module ClassMethods
7
+ def i18n_scope
8
+ :granite_action
9
+ end
10
+
11
+ def i18n_scopes
12
+ lookup_ancestors.flat_map do |klass|
13
+ :"#{klass.i18n_scope}.#{klass.model_name.i18n_key}"
14
+ end + [nil]
15
+ end
16
+ end
17
+
18
+ def translate(*args, **options)
19
+ key, options = Granite::Translations.scope_translation_args(self.class.i18n_scopes, *args, **options)
20
+ I18n.translate(key, **options)
21
+ end
22
+ alias t translate
23
+ end
24
+ end
25
+ end
data/lib/granite/base.rb CHANGED
@@ -18,7 +18,7 @@ module Granite
18
18
  include ActiveData::Model::Primary
19
19
  include ActiveModel::Validations::Callbacks
20
20
 
21
- include Granite::Translations
21
+ include Granite::Util
22
22
  include Granite::Represents
23
23
  end
24
24
  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
51
  projector.action_for(request_method_symbol, projector_action)
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
@@ -1,52 +1,18 @@
1
1
  module Granite
2
2
  class Projector
3
3
  module Translations
4
- extend ActiveSupport::Concern
4
+ include ActionView::Helpers::TranslationHelper
5
5
 
6
- class TranslationsWrapper
7
- include ActionView::Helpers::TranslationHelper
6
+ def i18n_scopes
7
+ Granite::Translations.combine_paths(action_class.i18n_scopes, [:"#{projector_name}"])
8
8
  end
9
9
 
10
- def translate(*args)
11
- TranslationsWrapper.new.translate(*self.class.scope_translation_args_by_projector(args))
10
+ def translate(*args, **options)
11
+ key, options = Granite::Translations.scope_translation_args(i18n_scopes, *args, **options)
12
+ super(key, **options)
12
13
  end
13
- alias t translate
14
-
15
- module ClassMethods
16
- def scope_translation_args_by_projector(args, action_name: nil)
17
- options = args.extract_options!
18
-
19
- lookups = expand_relative_key(args.first, action_name).map(&:to_sym)
20
- lookups += [options[:default]]
21
- lookups = lookups.flatten.compact
22
-
23
- key = lookups.shift
24
- options[:default] = lookups
25
-
26
- [key, options]
27
- end
28
-
29
- private
30
14
 
31
- def expand_relative_key(key, action_name = nil)
32
- return [key] unless key.is_a?(String) && key.start_with?('.')
33
-
34
- base_keys = extract_base_keys(key, action_name)
35
-
36
- action_class.lookup_ancestors.map do |klass|
37
- base_keys.map do |base_key|
38
- :"#{klass.i18n_scope}.#{klass.model_name.i18n_key}.#{base_key}"
39
- end
40
- end.flatten + base_keys
41
- end
42
-
43
- def extract_base_keys(key, action_name)
44
- undotted_key = key.sub(/^\./, '')
45
- base_keys = [:"#{projector_name}.#{undotted_key}"]
46
- base_keys.unshift :"#{projector_name}.#{action_name}.#{undotted_key}" if action_name
47
- base_keys
48
- end
49
- end
15
+ alias t translate
50
16
  end
51
17
  end
52
18
  end
@@ -11,9 +11,7 @@ module Granite
11
11
  end
12
12
 
13
13
  def sync
14
- return if reference.nil?
15
-
16
- reference.public_send(writer, read)
14
+ reference.public_send(writer, read) if reference.respond_to?(writer)
17
15
  end
18
16
 
19
17
  def typecast(value)
@@ -5,8 +5,8 @@ module Granite
5
5
  module Routing
6
6
  module Mapper
7
7
  def granite(projector_path, **options)
8
- route = Route.new(projector_path, options.extract!(:path, :as, :projector_prefix))
9
- Declarer.declare(self, route, options)
8
+ route = Route.new(projector_path, **options.extract!(:path, :as, :projector_prefix))
9
+ Declarer.declare(self, route, **options)
10
10
  end
11
11
  end
12
12
  end
@@ -1,19 +1,14 @@
1
1
  module Granite
2
- module Translations
3
- extend ActiveSupport::Concern
4
-
5
- def translate(*args)
6
- I18n.translate(*self.class.scope_translation_args(args))
7
- end
8
- alias t translate
9
-
10
- module ClassMethods
11
- def scope_translation_args(args)
12
- options = args.extract_options!
2
+ class Translations
3
+ class << self
4
+ def combine_paths(paths1, paths2)
5
+ paths1.flat_map do |path1|
6
+ paths2.map { |path2| [*path1, *path2].join('.') }
7
+ end
8
+ end
13
9
 
14
- lookups = expand_relative_key(args.first).map(&:to_sym)
15
- lookups += [options[:default]]
16
- lookups = lookups.flatten.compact
10
+ def scope_translation_args(scopes, key, *, **options)
11
+ lookups = expand_relative_key(scopes, key) + Array(options[:default])
17
12
 
18
13
  key = lookups.shift
19
14
  options[:default] = lookups
@@ -23,14 +18,10 @@ module Granite
23
18
 
24
19
  private
25
20
 
26
- def expand_relative_key(key)
21
+ def expand_relative_key(scopes, key)
27
22
  return [key] unless key.is_a?(String) && key.start_with?('.')
28
23
 
29
- base_key = key.sub(/^\./, '')
30
-
31
- lookup_ancestors.map do |klass|
32
- :"#{klass.i18n_scope}.#{klass.model_name.i18n_key}.#{base_key}"
33
- end.flatten + [base_key]
24
+ combine_paths(scopes, [key.sub(/^\./, '')]).map(&:to_sym)
34
25
  end
35
26
  end
36
27
  end
@@ -0,0 +1,51 @@
1
+ module Granite
2
+ module Util
3
+ extend ActiveSupport::Concern
4
+
5
+ # Evaluates value and returns result based on what was passed:
6
+ # - if Proc was passed, then executes it in context of self
7
+ # - if Symbol was passed, then calls a method with that name and returns result
8
+ # - otherwise just returns the value itself
9
+ # @param value [Object] value to evaluate
10
+ # @return [Object] result of evaluation
11
+ def evaluate(value)
12
+ case value
13
+ when Proc
14
+ evaluate_proc(value)
15
+ when Symbol
16
+ evaluate_symbol(value)
17
+ else
18
+ value
19
+ end
20
+ end
21
+
22
+ # Evaluates `if` or `unless` conditions present in the supplied
23
+ # `options` being it a symbol or callable.
24
+ #
25
+ # @param [Hash] options The method options to evaluate.
26
+ # @option options :if method name or callable
27
+ # @option options :unless method name or callable
28
+ # @return [Boolean] whether conditions are satisfied
29
+ def conditions_satisfied?(**options)
30
+ fail ArgumentError, 'You cannot specify both if and unless' if options.key?(:if) && options.key?(:unless)
31
+
32
+ if options.key?(:if)
33
+ evaluate(options[:if])
34
+ elsif options.key?(:unless)
35
+ !evaluate(options[:unless])
36
+ else
37
+ true
38
+ end
39
+ end
40
+
41
+ private
42
+
43
+ def evaluate_proc(value)
44
+ instance_exec(&value)
45
+ end
46
+
47
+ def evaluate_symbol(value)
48
+ __send__(value)
49
+ end
50
+ end
51
+ end
@@ -1,3 +1,3 @@
1
1
  module Granite
2
- VERSION = '0.9.5'.freeze
2
+ VERSION = '0.10.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.5
4
+ version: 0.10.0
5
5
  platform: ruby
6
6
  authors:
7
- - Arkadiy Zabazhanov & friends
7
+ - Toptal Engineering
8
8
  autorequire:
9
9
  bindir: bin
10
10
  cert_chain: []
11
- date: 2020-03-05 00:00:00.000000000 Z
11
+ date: 2021-07-07 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: actionpack
@@ -302,6 +302,8 @@ extra_rdoc_files: []
302
302
  files:
303
303
  - LICENSE
304
304
  - app/controllers/granite/controller.rb
305
+ - app/controllers/granite/controller/translations.rb
306
+ - config/rubocop-default.yml
305
307
  - lib/generators/USAGE
306
308
  - lib/generators/granite/install_controller_generator.rb
307
309
  - lib/generators/granite_generator.rb
@@ -319,14 +321,17 @@ files:
319
321
  - lib/granite/action/policies/always_allow_strategy.rb
320
322
  - lib/granite/action/policies/any_strategy.rb
321
323
  - lib/granite/action/policies/required_performer_strategy.rb
324
+ - lib/granite/action/precondition.rb
322
325
  - lib/granite/action/preconditions.rb
323
326
  - lib/granite/action/preconditions/base_precondition.rb
324
327
  - lib/granite/action/preconditions/embedded_precondition.rb
328
+ - lib/granite/action/preconditions/object_precondition.rb
325
329
  - lib/granite/action/projectors.rb
326
330
  - lib/granite/action/subject.rb
327
331
  - lib/granite/action/transaction.rb
328
332
  - lib/granite/action/transaction_manager.rb
329
333
  - lib/granite/action/transaction_manager/transactions_stack.rb
334
+ - lib/granite/action/translations.rb
330
335
  - lib/granite/action/types.rb
331
336
  - lib/granite/action/types/collection.rb
332
337
  - lib/granite/base.rb
@@ -341,8 +346,6 @@ files:
341
346
  - lib/granite/projector/error.rb
342
347
  - lib/granite/projector/helpers.rb
343
348
  - lib/granite/projector/translations.rb
344
- - lib/granite/projector/translations/helper.rb
345
- - lib/granite/projector/translations/view_helper.rb
346
349
  - lib/granite/rails.rb
347
350
  - lib/granite/represents.rb
348
351
  - lib/granite/represents/attribute.rb
@@ -362,6 +365,7 @@ files:
362
365
  - lib/granite/rspec/satisfy_preconditions.rb
363
366
  - lib/granite/translations.rb
364
367
  - lib/granite/typecasters.rb
368
+ - lib/granite/util.rb
365
369
  - lib/granite/version.rb
366
370
  - lib/rubocop-granite.rb
367
371
  - lib/rubocop/granite.rb
@@ -385,7 +389,7 @@ required_rubygems_version: !ruby/object:Gem::Requirement
385
389
  - !ruby/object:Gem::Version
386
390
  version: '0'
387
391
  requirements: []
388
- rubygems_version: 3.0.3
392
+ rubygems_version: 3.0.9
389
393
  signing_key:
390
394
  specification_version: 4
391
395
  summary: Another business actions architecture for Rails apps
@@ -1,22 +0,0 @@
1
- require 'granite/projector/translations/view_helper'
2
-
3
- module Granite
4
- class Projector
5
- module Translations
6
- module Helper
7
- extend ActiveSupport::Concern
8
-
9
- included do
10
- delegate :scope_translation_args_by_projector, to: :projector_class
11
- helper_method :scope_translation_args_by_projector
12
- helper ViewHelper
13
- end
14
-
15
- def translate(*args)
16
- super(*scope_translation_args_by_projector(args, action_name: action_name))
17
- end
18
- alias t translate
19
- end
20
- end
21
- end
22
- end
@@ -1,12 +0,0 @@
1
- module Granite
2
- class Projector
3
- module Translations
4
- module ViewHelper
5
- def translate(*args)
6
- super(*scope_translation_args_by_projector(args, action_name: action_name))
7
- end
8
- alias t translate
9
- end
10
- end
11
- end
12
- end