granite 0.15.4 → 0.16.0

Sign up to get free protection for your applications and to get access to all the features.
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: b2c26504cf64e66471237d5e225ec9a764c97a7d0a7e4fe70506b707402fb758
4
- data.tar.gz: 1a4e0f6d7713d2d3c2c3f8b4a0651c3a42aeb60bdc0057a92ebae516c7bf8cab
3
+ metadata.gz: de7f652f9f341a639c08c1f535e9bae3d07527ba5efc09599accf18895472607
4
+ data.tar.gz: f2792a296afbf39f20de772bf8d735ae778415f7214f1280dccde2dac58015e6
5
5
  SHA512:
6
- metadata.gz: 4385e90e1084b85136a23a6b441ce2f22ec709658ac70c4459bf178ec8de9145666886fececfcbb18d846e8edb28260f2c43313fd2f2e209a37fa22035074046
7
- data.tar.gz: 59b8b496841fbc135f11ccba324bc45036a85f808f3340b045ef5b92aa3cd72cfc43bd0e5c8e68740c866a6b4102dfbff6ba4ea078baee0ad214eef169f9b9c4
6
+ metadata.gz: 209c96c7464851ab67c3cc21c3b8b241c9874de746640924c322cb48a237e7c8f91a483e84d438247910103138215fe2cf5bdebe9855ae6633c5f180dc142b17
7
+ data.tar.gz: 8a27c93316cfdfbd050033567ff99ffeabe9cf73725810a73ed3c7ae76bfafde321c6b3e4f6b85bf7bb151517d61d36fd42fa084bf173c29bf784d44e7c5bd74
@@ -5,11 +5,11 @@ class GraniteGenerator < Rails::Generators::NamedBase
5
5
  class_option :collection, type: :boolean, aliases: '-C', desc: 'Generate collection action'
6
6
 
7
7
  def create_action
8
- template 'granite_action.rb.erb', "apq/actions/ba/#{file_path}.rb"
9
- template 'granite_business_action.rb.erb', "apq/actions/ba/#{class_path.join('/')}/business_action.rb" unless options.collection?
8
+ template 'granite_action.rb.erb', "apq/actions/#{file_path}.rb"
9
+ template 'granite_business_action.rb.erb', "apq/actions/#{class_path.join('/')}/business_action.rb" unless options.collection?
10
10
  template 'granite_base_action.rb.erb', 'apq/actions/base_action.rb', skip: true
11
- template 'granite_action_spec.rb.erb', "spec/apq/actions/ba/#{file_path}_spec.rb"
12
- empty_directory "apq/actions/ba/#{file_path}/#{projector}" if projector
11
+ template 'granite_action_spec.rb.erb', "spec/apq/actions/#{file_path}_spec.rb"
12
+ empty_directory "apq/actions/#{file_path}/#{projector}" if projector
13
13
  end
14
14
 
15
15
  private
@@ -18,7 +18,7 @@ class GraniteGenerator < Rails::Generators::NamedBase
18
18
  if options.collection?
19
19
  'BaseAction'
20
20
  else
21
- "BA::#{class_path.join('/').camelize}::BusinessAction"
21
+ "#{class_path.join('/').camelize}::BusinessAction"
22
22
  end
23
23
  end
24
24
 
@@ -1,4 +1,4 @@
1
- class BA::<%= class_name %> < <%= base_class_name %>
1
+ class <%= class_name %> < <%= base_class_name %>
2
2
  <% if projector -%>
3
3
  projector :<%= projector %>
4
4
 
@@ -1,6 +1,6 @@
1
1
  require 'rails_helper'
2
2
 
3
- RSpec.describe BA::<%= class_name %> do
3
+ RSpec.describe <%= class_name %> do
4
4
  <% if options.collection? -%>
5
5
  subject(:action) { described_class.as(performer).new(attributes) }
6
6
 
@@ -1,3 +1,3 @@
1
- class BA::<%= class_path.join('/').camelize %>::BusinessAction < BaseAction
1
+ class <%= class_path.join('/').camelize %>::BusinessAction < BaseAction
2
2
  subject :<%= subject_name %>
3
3
  end
@@ -42,7 +42,9 @@ 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
+ fail Rollback unless valid?(context)
46
+
47
+ perform_action(**options)
46
48
  end
47
49
  end
48
50
 
@@ -93,7 +95,6 @@ module Granite
93
95
 
94
96
  def perform_action(raise_errors: false, **options)
95
97
  result = run_callbacks(:execute_perform) do
96
- apply_association_changes!
97
98
  execute_perform!(**options)
98
99
  end
99
100
  @_action_performed = true
@@ -6,20 +6,8 @@ module Granite
6
6
  extend ActiveSupport::Concern
7
7
 
8
8
  included do
9
- define_callbacks :commit
10
- end
11
-
12
- module ClassMethods
13
- delegate :transaction, to: :'Granite::Action::TransactionManager'
14
-
15
- private
16
-
17
- # Defines a callback which will be triggered right after transaction committed
18
- # Uses the same arguments as `ActiveSupport::Callbacks.set_callback` except for the first two
19
- #
20
- def after_commit(*args, &block)
21
- set_callback :commit, :after, *args, &block
22
- end
9
+ define_model_callbacks :commit, only: :after
10
+ singleton_class.delegate :transaction, to: :'Granite::Action::TransactionManager'
23
11
  end
24
12
 
25
13
  def run_callbacks(event)
@@ -36,10 +24,10 @@ module Granite
36
24
 
37
25
  private
38
26
 
39
- attr_accessor :granite_in_transaction
27
+ attr_accessor :in_transaction
40
28
 
41
29
  def transaction(&block)
42
- if granite_in_transaction
30
+ if in_transaction
43
31
  yield
44
32
  else
45
33
  run_in_transaction(&block)
@@ -47,14 +35,14 @@ module Granite
47
35
  end
48
36
 
49
37
  def run_in_transaction
50
- self.granite_in_transaction = true
38
+ self.in_transaction = true
51
39
 
52
40
  TransactionManager.transaction do
53
41
  TransactionManager.after_commit(self)
54
42
  yield
55
43
  end
56
44
  ensure
57
- self.granite_in_transaction = false
45
+ self.in_transaction = false
58
46
  end
59
47
  end
60
48
  end
@@ -62,7 +62,7 @@ module Granite
62
62
  collected_errors = []
63
63
 
64
64
  callbacks.reverse_each do |callback|
65
- callback.respond_to?(:run_callbacks) ? callback.run_callbacks(:commit) : callback.call
65
+ callback.respond_to?(:_run_commit_callbacks) ? callback._run_commit_callbacks : callback.call
66
66
  rescue StandardError => e
67
67
  collected_errors << e
68
68
  end
@@ -3,7 +3,6 @@ require 'active_record/errors'
3
3
  require 'active_record/validations'
4
4
  require 'active_support/callbacks'
5
5
 
6
- require 'granite/action/types'
7
6
  require 'granite/action/error'
8
7
  require 'granite/action/instrumentation'
9
8
  require 'granite/action/performing'
@@ -63,6 +62,13 @@ module Granite
63
62
  merge_errors(e.action.errors)
64
63
  end
65
64
 
65
+ define_model_callbacks :initialize, only: :after
66
+
67
+ def initialize(*)
68
+ super
69
+ _run_initialize_callbacks
70
+ end
71
+
66
72
  if ActiveModel.version < Gem::Version.new('6.1.0')
67
73
  def merge_errors(other_errors)
68
74
  errors.messages.deep_merge!(other_errors.messages) do |_, this, other|
@@ -10,6 +10,8 @@ module Granite
10
10
 
11
11
  alias_method :only_run_validations!, :run_validations!
12
12
  protected :only_run_validations!
13
+
14
+ assign_data :sync_attributes
13
15
  end
14
16
 
15
17
  module ClassMethods
data/lib/granite/base.rb CHANGED
@@ -1,10 +1,8 @@
1
1
  require 'granite/form/model'
2
2
  require 'granite/form/model/primary'
3
- require 'granite/form/model/lifecycle'
4
3
  require 'granite/form/model/associations'
5
4
 
6
5
  require 'granite/translations'
7
- require 'granite/represents'
8
6
  require 'granite/assign_data'
9
7
 
10
8
  module Granite
@@ -12,8 +10,8 @@ module Granite
12
10
  # embeds_many)
13
11
  module Base
14
12
  extend ActiveSupport::Concern
13
+ extend ActiveModel::Callbacks
15
14
 
16
- include ActiveSupport::Callbacks
17
15
  include Granite::Form::Model
18
16
  include Granite::Form::Model::Representation
19
17
  include Granite::Form::Model::Dirty
@@ -21,8 +19,6 @@ module Granite
21
19
  include Granite::Form::Model::Primary
22
20
  include ActiveModel::Validations::Callbacks
23
21
 
24
- include Granite::Util
25
22
  include Granite::AssignData
26
- include Granite::Represents
27
23
  end
28
24
  end
@@ -5,7 +5,7 @@ module Granite
5
5
  class ActionNotMountedError < Error
6
6
  def initialize(projector)
7
7
  super("Seems like #{projector.class} was not mounted. \
8
- Do you have #{projector.action_class.name.underscore}##{projector.projector_name} declared in routes?", projector)
8
+ Do you have #{projector.action_name}##{projector.projector_name} declared in routes?", projector)
9
9
  end
10
10
  end
11
11
 
@@ -43,7 +43,7 @@ Do you have #{projector.action_class.name.underscore}##{projector.projector_name
43
43
  end
44
44
 
45
45
  def route_id
46
- [action_class.name.underscore, projector_name]
46
+ [action_name, projector_name]
47
47
  end
48
48
 
49
49
  def url_options
@@ -12,7 +12,7 @@ module Granite
12
12
  include Translations
13
13
 
14
14
  singleton_class.__send__(:attr_accessor, :action_class)
15
- delegate :action_class, :projector_name, to: 'self.class'
15
+ delegate :action_class, :projector_name, :action_name, to: 'self.class'
16
16
  attr_reader :action
17
17
 
18
18
  def self.controller_class
@@ -31,6 +31,10 @@ module Granite
31
31
  @projector_name ||= name.demodulize.remove(/Projector$/).underscore
32
32
  end
33
33
 
34
+ def self.action_name
35
+ @action_name ||= action_class.name.underscore
36
+ end
37
+
34
38
  def initialize(*args)
35
39
  @action = if args.first.is_a?(Granite::Action) # Temporary solutions for backwards compatibility.
36
40
  args.first
@@ -1,9 +1,8 @@
1
1
  module Granite::ProjectorHelpers
2
2
  extend ActiveSupport::Concern
3
+ include RSpec::Rails::ControllerExampleGroup
3
4
 
4
5
  included do
5
- include RSpec::Rails::ControllerExampleGroup
6
- include RSpec::Rails::RequestExampleGroup
7
6
  before { Granite::Routing::Declarer.dispatcher.unmemoize_all }
8
7
  end
9
8
 
@@ -23,16 +22,17 @@ module Granite::ProjectorHelpers
23
22
  end
24
23
 
25
24
  def projector(&block)
26
- setup_controller(&block)
25
+ setup_controller
27
26
  setup_view_context
27
+ let(:projector_class, &block)
28
28
  let(:projector) { controller.projector }
29
29
  end
30
30
 
31
31
  private
32
32
 
33
- def setup_controller(&block)
33
+ def setup_controller
34
34
  define_method :setup_controller_request_and_response do
35
- @controller ||= instance_eval(&block).controller_class.new
35
+ @controller ||= projector_class.controller_class.new
36
36
  super()
37
37
  end
38
38
  end
@@ -42,6 +42,12 @@ module Granite::ProjectorHelpers
42
42
  after { Granite.view_context = nil }
43
43
  end
44
44
  end
45
+
46
+ # Overrides ActionController::TestCase::Behavior#process to include granite_action and granite_projector
47
+ def process(action, **options)
48
+ projector_params = {granite_action: projector_class.action_name, granite_projector: projector_class.projector_name}
49
+ super(action, **options, params: projector_params.reverse_merge(options[:params] || {}))
50
+ end
45
51
  end
46
52
 
47
53
  RSpec.configuration.define_derived_metadata(file_path: %r{spec/apq/projectors/}) { |metadata| metadata[:type] ||= :granite_projector }
@@ -1,3 +1,3 @@
1
1
  module Granite
2
- VERSION = '0.15.4'.freeze
2
+ VERSION = '0.16.0'.freeze
3
3
  end
data/lib/granite.rb CHANGED
@@ -5,7 +5,6 @@ require 'ruby2_keywords'
5
5
  require 'granite/version'
6
6
  require 'granite/config'
7
7
  require 'granite/context'
8
- require 'granite/util'
9
8
 
10
9
  module Granite
11
10
  def self.config
@@ -25,7 +24,6 @@ require 'granite/dispatcher'
25
24
  require 'granite/action'
26
25
  require 'granite/projector'
27
26
  require 'granite/routing'
28
- require 'granite/typecasters'
29
- require 'granite/rails' if defined?(::Rails)
27
+ require 'granite/rails' if defined?(Rails)
30
28
 
31
29
  Granite::Form.base_concern = Granite::Base
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.15.4
4
+ version: 0.16.0
5
5
  platform: ruby
6
6
  authors:
7
7
  - Toptal Engineering
8
8
  autorequire:
9
9
  bindir: bin
10
10
  cert_chain: []
11
- date: 2024-01-19 00:00:00.000000000 Z
11
+ date: 2023-01-31 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: actionpack
@@ -36,14 +36,14 @@ dependencies:
36
36
  requirements:
37
37
  - - ">="
38
38
  - !ruby/object:Gem::Version
39
- version: '0'
39
+ version: 0.3.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: '0'
46
+ version: 0.3.0
47
47
  - !ruby/object:Gem::Dependency
48
48
  name: activesupport
49
49
  requirement: !ruby/object:Gem::Requirement
@@ -120,6 +120,20 @@ dependencies:
120
120
  - - ">="
121
121
  - !ruby/object:Gem::Version
122
122
  version: '0'
123
+ - !ruby/object:Gem::Dependency
124
+ name: bump
125
+ requirement: !ruby/object:Gem::Requirement
126
+ requirements:
127
+ - - ">="
128
+ - !ruby/object:Gem::Version
129
+ version: '0'
130
+ type: :development
131
+ prerelease: false
132
+ version_requirements: !ruby/object:Gem::Requirement
133
+ requirements:
134
+ - - ">="
135
+ - !ruby/object:Gem::Version
136
+ version: '0'
123
137
  - !ruby/object:Gem::Dependency
124
138
  name: capybara
125
139
  requirement: !ruby/object:Gem::Requirement
@@ -355,8 +369,6 @@ files:
355
369
  - lib/granite/action/transaction_manager.rb
356
370
  - lib/granite/action/transaction_manager/transactions_stack.rb
357
371
  - lib/granite/action/translations.rb
358
- - lib/granite/action/types.rb
359
- - lib/granite/action/types/collection.rb
360
372
  - lib/granite/assign_data.rb
361
373
  - lib/granite/base.rb
362
374
  - lib/granite/config.rb
@@ -372,9 +384,6 @@ files:
372
384
  - lib/granite/projector/helpers.rb
373
385
  - lib/granite/projector/translations.rb
374
386
  - lib/granite/rails.rb
375
- - lib/granite/represents.rb
376
- - lib/granite/represents/attribute.rb
377
- - lib/granite/represents/reflection.rb
378
387
  - lib/granite/routing.rb
379
388
  - lib/granite/routing/cache.rb
380
389
  - lib/granite/routing/caching.rb
@@ -390,8 +399,6 @@ files:
390
399
  - lib/granite/rspec/raise_validation_error.rb
391
400
  - lib/granite/rspec/satisfy_preconditions.rb
392
401
  - lib/granite/translations.rb
393
- - lib/granite/typecasters.rb
394
- - lib/granite/util.rb
395
402
  - lib/granite/version.rb
396
403
  - lib/rubocop-granite.rb
397
404
  - lib/rubocop/granite.rb
@@ -415,7 +422,7 @@ required_rubygems_version: !ruby/object:Gem::Requirement
415
422
  - !ruby/object:Gem::Version
416
423
  version: '0'
417
424
  requirements: []
418
- rubygems_version: 3.1.6
425
+ rubygems_version: 3.3.26
419
426
  signing_key:
420
427
  specification_version: 4
421
428
  summary: Another business actions architecture for Rails apps
@@ -1,13 +0,0 @@
1
- module Granite
2
- class Action
3
- module Types
4
- class Collection
5
- attr_reader :subtype
6
-
7
- def initialize(subtype)
8
- @subtype = subtype
9
- end
10
- end
11
- end
12
- end
13
- end
@@ -1 +0,0 @@
1
- require 'granite/action/types/collection'
@@ -1,109 +0,0 @@
1
- module Granite
2
- module Represents
3
- class Attribute < Granite::Form::Model::Attributes::Attribute
4
- types = {}
5
- types[ActiveRecord::Enum::EnumType] = String if defined?(ActiveRecord)
6
- TYPES = types.freeze
7
- delegate :writer, :reader, :reader_before_type_cast, to: :reflection
8
-
9
- def initialize(*_args)
10
- super
11
-
12
- set_default_value
13
- set_default_value_before_type_cast
14
- end
15
-
16
- def sync
17
- reference.public_send(writer, read) if reference.respond_to?(writer)
18
- end
19
-
20
- def typecast(value)
21
- return value if value.class == type # rubocop:disable Style/ClassEqualityComparison
22
-
23
- typecaster.call(value, self) unless value.nil?
24
- end
25
-
26
- def type
27
- return reflection.options[:type] if reflection.options[:type].present?
28
-
29
- granite_form_type || type_from_type_for_attribute || super
30
- end
31
-
32
- def typecaster
33
- @typecaster ||= begin
34
- type_class = type.instance_of?(Class) ? type : type.class
35
- @typecaster = Granite::Form.typecaster(type_class.ancestors.grep(Class))
36
- end
37
- end
38
-
39
- def changed?
40
- if reflection.options.key?(:default)
41
- reference.public_send(reader) != read
42
- else
43
- owner.public_send("#{name}_changed?")
44
- end
45
- end
46
-
47
- private
48
-
49
- def reference
50
- owner.__send__(reflection.reference)
51
- end
52
-
53
- def set_default_value
54
- return unless reference.respond_to?(reader)
55
-
56
- variable_cache(:value) do
57
- normalize(enumerize(typecast(defaultize(reference.public_send(reader)))))
58
- end
59
- end
60
-
61
- def set_default_value_before_type_cast
62
- return unless reference.respond_to?(reader_before_type_cast)
63
-
64
- variable_cache(:value_before_type_cast) do
65
- defaultize(reference.public_send(reader_before_type_cast))
66
- end
67
- end
68
-
69
- def granite_form_type
70
- return nil unless reference.is_a?(Granite::Form::Model)
71
-
72
- reference_attribute = reference.attribute(name)
73
-
74
- return nil if reference_attribute.nil?
75
-
76
- return Granite::Action::Types::Collection.new(reference_attribute.type) if [
77
- Granite::Form::Model::Attributes::ReferenceMany,
78
- Granite::Form::Model::Attributes::Collection,
79
- Granite::Form::Model::Attributes::Dictionary
80
- ].any? { |klass| reference_attribute.is_a? klass }
81
-
82
- reference_attribute.type # TODO: create `type_for_attribute` method inside of Granite::Form
83
- end
84
-
85
- def type_from_type_for_attribute
86
- return nil unless reference.respond_to?(:type_for_attribute)
87
-
88
- attribute_type = reference.type_for_attribute(attribute_name.to_s)
89
-
90
- return TYPES[attribute_type.class] if TYPES.key?(attribute_type.class)
91
- return Granite::Action::Types::Collection.new(convert_type_to_value_class(attribute_type.subtype)) if attribute_type.respond_to?(:subtype)
92
-
93
- convert_type_to_value_class(attribute_type)
94
- end
95
-
96
- def attribute_name
97
- return name if ActiveModel.version >= Gem::Version.new('6.1.0')
98
-
99
- reference.class.attribute_aliases[name.to_s] || name
100
- end
101
-
102
- def convert_type_to_value_class(attribute_type)
103
- return attribute_type.value_class if attribute_type.respond_to?(:value_class)
104
-
105
- Granite::Form::Model::Associations::PersistenceAdapters::ActiveRecord::TYPES[attribute_type.type&.to_sym]
106
- end
107
- end
108
- end
109
- end
@@ -1,24 +0,0 @@
1
- require 'granite/represents/attribute'
2
-
3
- module Granite
4
- module Represents
5
- class Reflection < Granite::Form::Model::Attributes::Reflections::Represents
6
- class << self
7
- def build(target, generated_methods, name, *args, &block)
8
- options = args.last
9
-
10
- reference = target.reflect_on_association(options[:of]) if target.respond_to?(:reflect_on_association)
11
- reference ||= target.reflect_on_attribute(options[:of]) if target.respond_to?(:reflect_on_attribute)
12
-
13
- target.validates_presence_of(reference.name) if reference
14
-
15
- super(target, generated_methods, name, *args, &block)
16
- end
17
-
18
- def attribute_class
19
- @attribute_class ||= Granite::Represents::Attribute
20
- end
21
- end
22
- end
23
- end
24
- end
@@ -1,21 +0,0 @@
1
- require 'granite/represents/reflection'
2
-
3
- module Granite
4
- module Represents
5
- extend ActiveSupport::Concern
6
-
7
- module ClassMethods
8
- private
9
-
10
- def represents(*fields, &block)
11
- options = fields.extract_options!.symbolize_keys
12
-
13
- fields.each do |field|
14
- add_attribute Granite::Represents::Reflection, field, options, &block
15
-
16
- assign_data { attribute(field).sync if attribute(field).changed? }
17
- end
18
- end
19
- end
20
- end
21
- end
@@ -1,10 +0,0 @@
1
- require 'granite/form'
2
-
3
- Granite::Form.typecaster('Granite::Action::Types::Collection') do |value, attribute|
4
- typecaster = Granite::Form.typecaster(attribute.type.subtype)
5
- if value.respond_to? :transform_values
6
- value.transform_values { |v| typecaster.call(v, attribute) }
7
- elsif value.respond_to?(:map)
8
- value.map { |v| typecaster.call(v, attribute) }
9
- end
10
- end
data/lib/granite/util.rb DELETED
@@ -1,51 +0,0 @@
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