granite 0.15.0 → 0.16.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: 2cf5813f23b963898bbdd4f55d35b2f0699428c657413ee6f18d56713ffcf659
4
- data.tar.gz: 48a04a7a60bf1819222728acf33c8bf0a73149d9b9d83b2ea04bc70a2182ec04
3
+ metadata.gz: de7f652f9f341a639c08c1f535e9bae3d07527ba5efc09599accf18895472607
4
+ data.tar.gz: f2792a296afbf39f20de772bf8d735ae778415f7214f1280dccde2dac58015e6
5
5
  SHA512:
6
- metadata.gz: b182f94aa37d25756606fe9384a5ea98963279f8a58f84a66a7660fd21a3076b46f6965d56e26d5bc75357431f9bec2dfabb639bd198b0d00529d2567dd21628
7
- data.tar.gz: 814dbec1866a2845d869489d82051feb017e4c850460fef1af69f13d31cf6c40fb66dbddec52529e84dfd292acce8bf73bb4479f9f8e2fca42c586c9dd12b2a2
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)
@@ -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.0'.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.0
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: 2022-11-22 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.2.33
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