active_interaction-extras 1.1.0 → 2.0.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 +4 -4
- data/.github/workflows/ci.yml +3 -2
- data/CHANGELOG.md +24 -0
- data/README.md +325 -0
- data/active_interaction-extras.gemspec +6 -5
- data/lib/active_interaction/extras/active_job.rb +36 -5
- data/lib/active_interaction/extras/add_to_batch.rb +13 -0
- data/lib/active_interaction/extras/after_initialize.rb +20 -0
- data/lib/active_interaction/extras/all.rb +16 -1
- data/lib/active_interaction/extras/all_runner.rb +91 -0
- data/lib/active_interaction/extras/append_inputs.rb +60 -0
- data/lib/active_interaction/extras/current.rb +23 -0
- data/lib/active_interaction/extras/file_blobs.rb +80 -0
- data/lib/active_interaction/extras/filter_alias.rb +28 -2
- data/lib/active_interaction/extras/filter_extensions.rb +8 -0
- data/lib/active_interaction/extras/filters/ar_relation_filter.rb +22 -0
- data/lib/active_interaction/extras/filters/uuid_filter.rb +1 -1
- data/lib/active_interaction/extras/form_for.rb +33 -0
- data/lib/active_interaction/extras/good_job.rb +45 -0
- data/lib/active_interaction/extras/include_errors.rb +10 -0
- data/lib/active_interaction/extras/initialize_with.rb +16 -0
- data/lib/active_interaction/extras/instance_runnable.rb +35 -0
- data/lib/active_interaction/extras/model_fields.rb +68 -33
- data/lib/active_interaction/extras/model_names.rb +20 -0
- data/lib/active_interaction/extras/named_callbacks.rb +54 -0
- data/lib/active_interaction/extras/nested_attributes.rb +126 -0
- data/lib/active_interaction/extras/strong_params.rb +48 -10
- data/lib/active_interaction/extras/transaction.rb +27 -5
- data/lib/active_interaction/extras/version.rb +1 -1
- data/lib/active_interaction/extras.rb +10 -23
- metadata +41 -15
|
@@ -0,0 +1,60 @@
|
|
|
1
|
+
module ActiveInteraction::Extras::AppendInputs
|
|
2
|
+
extend ActiveSupport::Concern
|
|
3
|
+
|
|
4
|
+
module InputsRefinement
|
|
5
|
+
refine ActiveInteraction::Inputs do
|
|
6
|
+
# Based on ActiveInteraction::Inputs#initialize
|
|
7
|
+
def append(raw_inputs, overwrite: false)
|
|
8
|
+
new_normalized_inputs = normalize(raw_inputs)
|
|
9
|
+
|
|
10
|
+
keys = new_normalized_inputs.keys.map(&:to_sym)
|
|
11
|
+
keys = keys.reject { given?(_1) } if !overwrite
|
|
12
|
+
|
|
13
|
+
new_inputs = @base.class.filters
|
|
14
|
+
.slice(*keys)
|
|
15
|
+
.each_with_object({}) do |(name, filter), inputs|
|
|
16
|
+
inputs[name] = filter.process(new_normalized_inputs[name], @base)
|
|
17
|
+
|
|
18
|
+
yield(name, inputs[name]) if block_given?
|
|
19
|
+
end
|
|
20
|
+
|
|
21
|
+
@normalized_inputs = @normalized_inputs.merge(new_normalized_inputs)
|
|
22
|
+
@inputs = @inputs.merge(new_inputs)
|
|
23
|
+
@to_h = nil
|
|
24
|
+
end
|
|
25
|
+
end
|
|
26
|
+
end
|
|
27
|
+
|
|
28
|
+
using InputsRefinement
|
|
29
|
+
|
|
30
|
+
def default_inputs(*args)
|
|
31
|
+
inputs.append(*args) do |name, input|
|
|
32
|
+
public_send("#{name}=", input.value)
|
|
33
|
+
end
|
|
34
|
+
end
|
|
35
|
+
|
|
36
|
+
# def default_input(name, &block)
|
|
37
|
+
# if !inputs.given?(name)
|
|
38
|
+
# default_inputs(name: block.call)
|
|
39
|
+
# end
|
|
40
|
+
# end
|
|
41
|
+
|
|
42
|
+
class_methods do
|
|
43
|
+
def default_inputs(&block)
|
|
44
|
+
# prepend: true - so it runs after ActiveInteraction::Extras::ModelFields.assign_model_values_to_filters
|
|
45
|
+
# callback order is inverse, so that's why it's confusing
|
|
46
|
+
after_initialize prepend: true do
|
|
47
|
+
hash = block.call
|
|
48
|
+
default_inputs(hash)
|
|
49
|
+
end
|
|
50
|
+
end
|
|
51
|
+
|
|
52
|
+
def default_input(name, &block)
|
|
53
|
+
# prepend: true - so it runs after ActiveInteraction::Extras::ModelFields.assign_model_values_to_filters
|
|
54
|
+
# callback order is inverse, so that's why it's confusing
|
|
55
|
+
after_initialize prepend: true do
|
|
56
|
+
default_inputs(name => instance_exec(&block))
|
|
57
|
+
end
|
|
58
|
+
end
|
|
59
|
+
end
|
|
60
|
+
end
|
|
@@ -0,0 +1,23 @@
|
|
|
1
|
+
module ActiveInteraction::Extras::Current
|
|
2
|
+
extend ActiveSupport::Concern
|
|
3
|
+
|
|
4
|
+
class CurrentContext < ActiveSupport::CurrentAttributes
|
|
5
|
+
attribute :job
|
|
6
|
+
attribute :batch_event
|
|
7
|
+
attribute :batch
|
|
8
|
+
|
|
9
|
+
def batch
|
|
10
|
+
super || job&.batch
|
|
11
|
+
end
|
|
12
|
+
end
|
|
13
|
+
|
|
14
|
+
def current
|
|
15
|
+
self.class.current
|
|
16
|
+
end
|
|
17
|
+
|
|
18
|
+
class_methods do
|
|
19
|
+
def current
|
|
20
|
+
CurrentContext
|
|
21
|
+
end
|
|
22
|
+
end
|
|
23
|
+
end
|
|
@@ -0,0 +1,80 @@
|
|
|
1
|
+
module ActiveInteraction::Extras::FileBlobs
|
|
2
|
+
extend ActiveSupport::Concern
|
|
3
|
+
|
|
4
|
+
# Returns the blobs for a given array field.
|
|
5
|
+
#
|
|
6
|
+
# @example has_one_attached :file
|
|
7
|
+
#
|
|
8
|
+
# # doc/_form.html.slim
|
|
9
|
+
# = f.hidden_field :file, value: nil
|
|
10
|
+
#
|
|
11
|
+
# - form.blobs_for(:file).each do |blob|
|
|
12
|
+
# div data-controller="remove"
|
|
13
|
+
# = f.hidden_field :file, value: blob.signed_id
|
|
14
|
+
# - if blob.representable?
|
|
15
|
+
# = image_tag url_for(blob.representation(resize_to_fit: [300, 80])), height: 80
|
|
16
|
+
# button type="button" data-action="click->remove#remove"
|
|
17
|
+
# 'Remove
|
|
18
|
+
#
|
|
19
|
+
# = f.input :new_file, as: :file
|
|
20
|
+
#
|
|
21
|
+
# # doc/form.rb
|
|
22
|
+
# class Doc::Form < ActiveInteraction::Base
|
|
23
|
+
# object :doc
|
|
24
|
+
#
|
|
25
|
+
# model_fields(:doc) do
|
|
26
|
+
# anything :file
|
|
27
|
+
# end
|
|
28
|
+
#
|
|
29
|
+
# anything :new_file, permit: true, default: nil
|
|
30
|
+
#
|
|
31
|
+
# def execute
|
|
32
|
+
# save_model!(:doc)
|
|
33
|
+
#
|
|
34
|
+
# if inputs.given?(:new_file)
|
|
35
|
+
# doc.file = new_file
|
|
36
|
+
# doc.save!
|
|
37
|
+
# end
|
|
38
|
+
# end
|
|
39
|
+
# end
|
|
40
|
+
#
|
|
41
|
+
# @example has_many_attached :files
|
|
42
|
+
#
|
|
43
|
+
# # doc/_form.html.slim
|
|
44
|
+
# = f.file_field :files, multiple: true, direct_upload: true
|
|
45
|
+
#
|
|
46
|
+
# # doc/form.rb
|
|
47
|
+
# class Doc::Form < ActiveInteraction::Base
|
|
48
|
+
# object :doc
|
|
49
|
+
#
|
|
50
|
+
# model_fields(:doc) do
|
|
51
|
+
# array :files
|
|
52
|
+
# end
|
|
53
|
+
#
|
|
54
|
+
# def execute
|
|
55
|
+
# save_model!(:files)
|
|
56
|
+
# end
|
|
57
|
+
# end
|
|
58
|
+
#
|
|
59
|
+
def blobs_for(array_field)
|
|
60
|
+
# If there is a new upload field, prioritize it
|
|
61
|
+
new_upload = "new_#{array_field}"
|
|
62
|
+
if respond_to?(new_upload) && public_send(new_upload).present?
|
|
63
|
+
array_field = new_upload
|
|
64
|
+
end
|
|
65
|
+
|
|
66
|
+
list = public_send(array_field)
|
|
67
|
+
list = Array.wrap(list)
|
|
68
|
+
|
|
69
|
+
list.compact_blank.map do |file|
|
|
70
|
+
case file
|
|
71
|
+
when ActiveStorage::Blob
|
|
72
|
+
file
|
|
73
|
+
when ActiveStorage::Attachment, ActiveStorage::Attached::One
|
|
74
|
+
file.blob
|
|
75
|
+
when String
|
|
76
|
+
ActiveStorage::Blob.find_signed(file)
|
|
77
|
+
end
|
|
78
|
+
end.compact
|
|
79
|
+
end
|
|
80
|
+
end
|
|
@@ -1,16 +1,42 @@
|
|
|
1
1
|
# frozen_string_literal: true
|
|
2
2
|
|
|
3
3
|
# Add :as option, which is a read alias to filter
|
|
4
|
-
# hash :
|
|
4
|
+
# hash :account, as: :account_attributes
|
|
5
5
|
#
|
|
6
6
|
module ActiveInteraction::Extras::FilterAlias
|
|
7
7
|
extend ActiveSupport::Concern
|
|
8
8
|
|
|
9
|
+
# concern :InputsExtension do
|
|
10
|
+
# def normalize(inputs)
|
|
11
|
+
# @base.class.filters.each do |name, filter|
|
|
12
|
+
# alias_name = filter.options[:as]
|
|
13
|
+
#
|
|
14
|
+
# if alias_name.nil?
|
|
15
|
+
# next
|
|
16
|
+
#
|
|
17
|
+
# elsif inputs.key?(alias_name.to_sym)
|
|
18
|
+
# inputs[name.to_sym] = inputs[alias_name.to_sym]
|
|
19
|
+
#
|
|
20
|
+
# elsif inputs.key?(alias_name.to_s)
|
|
21
|
+
# inputs[name.to_s] = inputs[alias_name.to_s]
|
|
22
|
+
# end
|
|
23
|
+
# end
|
|
24
|
+
#
|
|
25
|
+
# super
|
|
26
|
+
# end
|
|
27
|
+
# end
|
|
28
|
+
#
|
|
29
|
+
# included do
|
|
30
|
+
# ActiveInteraction::Inputs.prepend InputsExtension
|
|
31
|
+
# end
|
|
32
|
+
|
|
9
33
|
class_methods do
|
|
10
34
|
def initialize_filter(filter)
|
|
11
35
|
super.tap do
|
|
12
36
|
if filter.options[:as]
|
|
13
|
-
|
|
37
|
+
name = filter.options[:as]
|
|
38
|
+
alias_method name, filter.name
|
|
39
|
+
# alias_method "#{name}=", "#{filter.name}="
|
|
14
40
|
end
|
|
15
41
|
end
|
|
16
42
|
end
|
|
@@ -0,0 +1,22 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
class ActiveInteraction::Extras::Filters::ArRelationFilter < ActiveInteraction::ObjectFilter
|
|
4
|
+
register :ar_relation
|
|
5
|
+
|
|
6
|
+
def initialize(...)
|
|
7
|
+
super
|
|
8
|
+
@options.reverse_merge!(class: ActiveRecord::Relation)
|
|
9
|
+
end
|
|
10
|
+
|
|
11
|
+
def matches?(value)
|
|
12
|
+
# value == nil triggers active record loading
|
|
13
|
+
if value.nil? && default?
|
|
14
|
+
# as per v5 there is no way to know if value of `nil` is given by caller or
|
|
15
|
+
# is a default value when it's missing. We want to maintain standard default
|
|
16
|
+
# behaviour when value is `nil`. Returning `false` will trigger default value
|
|
17
|
+
false
|
|
18
|
+
else
|
|
19
|
+
true
|
|
20
|
+
end
|
|
21
|
+
end
|
|
22
|
+
end
|
|
@@ -3,7 +3,7 @@
|
|
|
3
3
|
class ActiveInteraction::Extras::Filters::UUIDFilter < ActiveInteraction::StringFilter
|
|
4
4
|
register :uuid
|
|
5
5
|
|
|
6
|
-
REGEX = /^[0-9A-F]{8}-[0-9A-F]{4}-[
|
|
6
|
+
REGEX = /^[0-9A-F]{8}-[0-9A-F]{4}-[457][0-9A-F]{3}-[89AB][0-9A-F]{3}-[0-9A-F]{12}$/i.freeze
|
|
7
7
|
|
|
8
8
|
def matches?(value)
|
|
9
9
|
super && REGEX.match?(value)
|
|
@@ -0,0 +1,33 @@
|
|
|
1
|
+
module ActiveInteraction::Extras::FormFor
|
|
2
|
+
extend ActiveSupport::Concern
|
|
3
|
+
|
|
4
|
+
class_methods do
|
|
5
|
+
# Changes route key so url helpers work
|
|
6
|
+
#
|
|
7
|
+
# @example
|
|
8
|
+
# class User::Form < ActiveInteraction::Base
|
|
9
|
+
# object :user
|
|
10
|
+
# form_for :user
|
|
11
|
+
# end
|
|
12
|
+
#
|
|
13
|
+
# url_for(form) #=> "/users/1"
|
|
14
|
+
def form_for(field_name)
|
|
15
|
+
delegate :persisted?, :id, :to_param, to: field_name
|
|
16
|
+
|
|
17
|
+
if filters.key?(field_name)
|
|
18
|
+
klass = filters.fetch(field_name).send(:klass)
|
|
19
|
+
model_name.route_key = klass.model_name.route_key
|
|
20
|
+
model_name.singular_route_key = klass.model_name.singular_route_key
|
|
21
|
+
end
|
|
22
|
+
|
|
23
|
+
# TODO: Routes reload is broken in Dev
|
|
24
|
+
# Resolve form object route to object
|
|
25
|
+
# e.g. resolve "User::Form" { |form| form.user }
|
|
26
|
+
# Rails.application.config.after_routes_loaded do |app|
|
|
27
|
+
# app.routes.add_polymorphic_mapping(name, {}) do |form|
|
|
28
|
+
# form.send(field_name)
|
|
29
|
+
# end
|
|
30
|
+
# end
|
|
31
|
+
end
|
|
32
|
+
end
|
|
33
|
+
end
|
|
@@ -0,0 +1,45 @@
|
|
|
1
|
+
require 'active_job'
|
|
2
|
+
|
|
3
|
+
module ActiveInteraction::Extras::GoodJob
|
|
4
|
+
extend ActiveSupport::Concern
|
|
5
|
+
include ActiveInteraction::Extras::ActiveJob
|
|
6
|
+
|
|
7
|
+
module BatchJobPerform
|
|
8
|
+
def perform(batch, options)
|
|
9
|
+
ActiveInteraction::Extras::Current::CurrentContext
|
|
10
|
+
.set(batch:, batch_event: options[:event]) do
|
|
11
|
+
super(batch.properties)
|
|
12
|
+
end
|
|
13
|
+
end
|
|
14
|
+
end
|
|
15
|
+
|
|
16
|
+
# Note: Should include GoodJob::ActiveJobExtensions::Concurrency before this
|
|
17
|
+
module Perform
|
|
18
|
+
extend ActiveSupport::Concern
|
|
19
|
+
include ActiveInteraction::Extras::ActiveJob::Perform
|
|
20
|
+
|
|
21
|
+
def _good_job_default_concurrency_key
|
|
22
|
+
Digest::MD5.base64digest([self.class.name, arguments].to_param)
|
|
23
|
+
end
|
|
24
|
+
end
|
|
25
|
+
|
|
26
|
+
class_methods do
|
|
27
|
+
def define_job_class(klass)
|
|
28
|
+
super
|
|
29
|
+
|
|
30
|
+
unless const_defined?(:BatchJob, false)
|
|
31
|
+
const_set(:BatchJob, Class.new(job_class) do
|
|
32
|
+
include BatchJobPerform
|
|
33
|
+
end)
|
|
34
|
+
end
|
|
35
|
+
end
|
|
36
|
+
|
|
37
|
+
def batch_job(job_params)
|
|
38
|
+
batch = GoodJob::Batch.new
|
|
39
|
+
batch.description = self.name
|
|
40
|
+
batch.on_finish = self::BatchJob.name
|
|
41
|
+
batch.properties = job_params
|
|
42
|
+
batch
|
|
43
|
+
end
|
|
44
|
+
end
|
|
45
|
+
end
|
|
@@ -0,0 +1,16 @@
|
|
|
1
|
+
module ActiveInteraction::Extras::InitializeWith
|
|
2
|
+
extend ActiveSupport::Concern
|
|
3
|
+
|
|
4
|
+
include ActiveInteraction::Extras::AfterInitialize
|
|
5
|
+
|
|
6
|
+
class_methods do
|
|
7
|
+
def initialize_with(&block)
|
|
8
|
+
after_initialize do
|
|
9
|
+
hash = instance_exec(&block)
|
|
10
|
+
hash&.each do |filter_name, value|
|
|
11
|
+
public_send "#{filter_name}=", value if !inputs.given?(filter_name)
|
|
12
|
+
end
|
|
13
|
+
end
|
|
14
|
+
end
|
|
15
|
+
end
|
|
16
|
+
end
|
|
@@ -0,0 +1,35 @@
|
|
|
1
|
+
# Allows Interaction.new.run
|
|
2
|
+
# Useful for controllers, so single object could be instantiated for new, create, edit, update
|
|
3
|
+
# @example
|
|
4
|
+
# def form
|
|
5
|
+
# @form ||= Form.new(record:, params:)
|
|
6
|
+
# end
|
|
7
|
+
#
|
|
8
|
+
# def create
|
|
9
|
+
# if form.save
|
|
10
|
+
# ...
|
|
11
|
+
# else
|
|
12
|
+
# ...
|
|
13
|
+
# end
|
|
14
|
+
# end
|
|
15
|
+
#
|
|
16
|
+
module ActiveInteraction::Extras::InstanceRunnable
|
|
17
|
+
extend ActiveSupport::Concern
|
|
18
|
+
|
|
19
|
+
def save
|
|
20
|
+
run
|
|
21
|
+
valid?
|
|
22
|
+
end
|
|
23
|
+
|
|
24
|
+
def save!
|
|
25
|
+
run!
|
|
26
|
+
end
|
|
27
|
+
|
|
28
|
+
def run
|
|
29
|
+
if @_executed
|
|
30
|
+
raise("Service already executed")
|
|
31
|
+
end
|
|
32
|
+
@_executed = true
|
|
33
|
+
super
|
|
34
|
+
end
|
|
35
|
+
end
|
|
@@ -2,38 +2,17 @@ require "active_support/core_ext/object/with_options"
|
|
|
2
2
|
|
|
3
3
|
module ActiveInteraction::Extras::ModelFields
|
|
4
4
|
extend ActiveSupport::Concern
|
|
5
|
-
|
|
6
|
-
# returns hash of all model fields and their values
|
|
7
|
-
def model_fields(model_name)
|
|
8
|
-
fields = self.class.model_field_cache[model_name]
|
|
9
|
-
fields.to_h do |field|
|
|
10
|
-
[field, public_send(field)]
|
|
11
|
-
end
|
|
12
|
-
end
|
|
13
|
-
|
|
14
|
-
# returns hash of only changed model fields and their values
|
|
15
|
-
def changed_model_fields(model_name)
|
|
16
|
-
model_fields(model_name).select do |field, _value|
|
|
17
|
-
any_changed?(field)
|
|
18
|
-
end
|
|
19
|
-
end
|
|
20
|
-
|
|
21
|
-
# returns hash of only given model fields and their values
|
|
22
|
-
def given_model_fields(model_name)
|
|
23
|
-
model_fields(model_name).select do |field, _value|
|
|
24
|
-
inputs.given?(field)
|
|
25
|
-
end
|
|
26
|
-
end
|
|
5
|
+
include ActiveInteraction::Extras::AfterInitialize
|
|
27
6
|
|
|
28
7
|
class Context < SimpleDelegator
|
|
29
8
|
attr_accessor :from_model_name
|
|
30
9
|
attr_accessor :model_field_cache
|
|
31
10
|
|
|
32
|
-
def custom_filter_attribute(name, opts = {})
|
|
11
|
+
def custom_filter_attribute(name, opts = {}, &block)
|
|
33
12
|
from_model_name = self.from_model_name
|
|
34
13
|
model_field_cache[from_model_name] = model_field_cache[from_model_name] << name
|
|
35
14
|
|
|
36
|
-
__getobj__.send __callee__, name, opts
|
|
15
|
+
__getobj__.send __callee__, name, opts, &block
|
|
37
16
|
end
|
|
38
17
|
|
|
39
18
|
alias interface custom_filter_attribute
|
|
@@ -56,25 +35,81 @@ module ActiveInteraction::Extras::ModelFields
|
|
|
56
35
|
alias uuid custom_filter_attribute
|
|
57
36
|
end
|
|
58
37
|
|
|
38
|
+
included do
|
|
39
|
+
after_initialize :assign_model_values_to_filters
|
|
40
|
+
end
|
|
41
|
+
|
|
42
|
+
# returns hash of all model fields and their values
|
|
43
|
+
def model_fields(model_name)
|
|
44
|
+
fields = self.class.model_field_cache[model_name]
|
|
45
|
+
fields.to_h do |field|
|
|
46
|
+
[field, public_send(field)]
|
|
47
|
+
end
|
|
48
|
+
end
|
|
49
|
+
|
|
50
|
+
# returns hash of only changed model fields and their values
|
|
51
|
+
def changed_model_fields(model_name)
|
|
52
|
+
model_fields(model_name).select do |field, _value|
|
|
53
|
+
any_changed?(field)
|
|
54
|
+
end
|
|
55
|
+
end
|
|
56
|
+
|
|
57
|
+
def assign_model_fields(model_name)
|
|
58
|
+
model = public_send(model_name)
|
|
59
|
+
model.assign_attributes(model_fields(model_name))
|
|
60
|
+
model
|
|
61
|
+
end
|
|
62
|
+
|
|
63
|
+
def save_model!(model_name)
|
|
64
|
+
model = assign_model_fields(model_name)
|
|
65
|
+
model.save
|
|
66
|
+
include_errors! model
|
|
67
|
+
model
|
|
68
|
+
end
|
|
69
|
+
|
|
70
|
+
def assign_model_changes(model_name)
|
|
71
|
+
model = public_send(model_name)
|
|
72
|
+
model.assign_attributes(changed_model_fields(model_name))
|
|
73
|
+
model
|
|
74
|
+
end
|
|
75
|
+
|
|
76
|
+
def save_model_changes!(model_name)
|
|
77
|
+
model = assign_model_changes(model_name)
|
|
78
|
+
model.save
|
|
79
|
+
include_errors! model
|
|
80
|
+
model
|
|
81
|
+
end
|
|
82
|
+
|
|
83
|
+
# returns hash of only given model fields and their values
|
|
84
|
+
def given_model_fields(model_name)
|
|
85
|
+
model_fields(model_name).select do |field, _value|
|
|
86
|
+
inputs.given?(field)
|
|
87
|
+
end
|
|
88
|
+
end
|
|
89
|
+
|
|
59
90
|
# checks if value was given to the service and the value is different from
|
|
60
91
|
# the one on the model
|
|
61
92
|
def any_changed?(*fields)
|
|
62
93
|
fields.any? do |field|
|
|
63
94
|
model_field = self.class.model_field_cache_inverse[field]
|
|
64
|
-
value_changed = true
|
|
65
|
-
|
|
66
95
|
if model_field
|
|
67
|
-
|
|
96
|
+
model = send(model_field)
|
|
97
|
+
if model.respond_to?(:new_record?) && model.new_record?
|
|
98
|
+
true
|
|
99
|
+
elsif inputs.given?(field)
|
|
100
|
+
# NOTE: this causes bug when form attribute was manually changed, but `inputs.given?` is false
|
|
101
|
+
# change won't be detected
|
|
102
|
+
model.send(field) != send(field)
|
|
103
|
+
else
|
|
104
|
+
false
|
|
105
|
+
end
|
|
106
|
+
else
|
|
107
|
+
inputs.given?(field)
|
|
68
108
|
end
|
|
69
|
-
|
|
70
|
-
inputs.given?(field) && value_changed
|
|
71
109
|
end
|
|
72
110
|
end
|
|
73
111
|
|
|
74
|
-
|
|
75
|
-
def initialize(...)
|
|
76
|
-
super
|
|
77
|
-
|
|
112
|
+
def assign_model_values_to_filters
|
|
78
113
|
self.class.filters.each do |name, filter|
|
|
79
114
|
next if inputs.given?(name)
|
|
80
115
|
|
|
@@ -0,0 +1,20 @@
|
|
|
1
|
+
module ActiveInteraction::Extras::ModelNames
|
|
2
|
+
extend ActiveSupport::Concern
|
|
3
|
+
|
|
4
|
+
class_methods do
|
|
5
|
+
def inherited(subclass)
|
|
6
|
+
super.tap { subclass.set_model_naming }
|
|
7
|
+
end
|
|
8
|
+
|
|
9
|
+
def set_model_naming
|
|
10
|
+
str = name.deconstantize.underscore
|
|
11
|
+
model_name.route_key = str.pluralize.to_sym
|
|
12
|
+
model_name.singular_route_key = str.singularize.to_sym
|
|
13
|
+
end
|
|
14
|
+
|
|
15
|
+
def singular_resource_route_key!
|
|
16
|
+
str = name.deconstantize.underscore
|
|
17
|
+
model_name.route_key = str.singularize.to_sym
|
|
18
|
+
end
|
|
19
|
+
end
|
|
20
|
+
end
|
|
@@ -0,0 +1,54 @@
|
|
|
1
|
+
module ActiveInteraction::Extras::NamedCallbacks
|
|
2
|
+
extend ActiveSupport::Concern
|
|
3
|
+
|
|
4
|
+
class_methods do
|
|
5
|
+
def before_filter(...)
|
|
6
|
+
set_callback(:filter, :before, ...)
|
|
7
|
+
end
|
|
8
|
+
|
|
9
|
+
def after_filter(...)
|
|
10
|
+
set_callback(:filter, :after, ...)
|
|
11
|
+
end
|
|
12
|
+
|
|
13
|
+
def around_filter(...)
|
|
14
|
+
set_callback(:filter, :around, ...)
|
|
15
|
+
end
|
|
16
|
+
|
|
17
|
+
|
|
18
|
+
def before_validate(...)
|
|
19
|
+
set_callback(:validate, :before, ...)
|
|
20
|
+
end
|
|
21
|
+
|
|
22
|
+
def after_validate(...)
|
|
23
|
+
set_callback(:validate, :after, ...)
|
|
24
|
+
end
|
|
25
|
+
|
|
26
|
+
def around_validate(...)
|
|
27
|
+
set_callback(:validate, :around, ...)
|
|
28
|
+
end
|
|
29
|
+
|
|
30
|
+
def before_validation(...)
|
|
31
|
+
before_validate(...)
|
|
32
|
+
end
|
|
33
|
+
|
|
34
|
+
def after_validation(...)
|
|
35
|
+
after_validate(...)
|
|
36
|
+
end
|
|
37
|
+
|
|
38
|
+
def around_validation(...)
|
|
39
|
+
around_validate(...)
|
|
40
|
+
end
|
|
41
|
+
|
|
42
|
+
def before_execute(...)
|
|
43
|
+
set_callback(:execute, :before, ...)
|
|
44
|
+
end
|
|
45
|
+
|
|
46
|
+
def after_execute(...)
|
|
47
|
+
set_callback(:execute, :after, ...)
|
|
48
|
+
end
|
|
49
|
+
|
|
50
|
+
def around_execute(...)
|
|
51
|
+
set_callback(:execute, :around, ...)
|
|
52
|
+
end
|
|
53
|
+
end
|
|
54
|
+
end
|