active_fields 1.0.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/.rubocop.yml +5 -4
- data/CHANGELOG.md +33 -2
- data/README.md +478 -90
- data/app/models/active_fields/field/boolean.rb +3 -0
- data/app/models/active_fields/field/date.rb +3 -0
- data/app/models/active_fields/field/date_array.rb +3 -0
- data/app/models/active_fields/field/date_time.rb +4 -1
- data/app/models/active_fields/field/date_time_array.rb +4 -1
- data/app/models/active_fields/field/decimal.rb +6 -1
- data/app/models/active_fields/field/decimal_array.rb +6 -1
- data/app/models/active_fields/field/enum.rb +3 -0
- data/app/models/active_fields/field/enum_array.rb +3 -0
- data/app/models/active_fields/field/integer.rb +3 -0
- data/app/models/active_fields/field/integer_array.rb +3 -0
- data/app/models/active_fields/field/text.rb +3 -0
- data/app/models/active_fields/field/text_array.rb +3 -0
- data/app/models/active_fields/field.rb +5 -0
- data/app/models/concerns/active_fields/customizable_concern.rb +93 -4
- data/app/models/concerns/active_fields/field_concern.rb +26 -3
- data/db/migrate/20240229230000_create_active_fields_tables.rb +1 -1
- data/lib/active_fields/casters/date_time_caster.rb +1 -3
- data/lib/active_fields/casters/decimal_caster.rb +2 -5
- data/lib/active_fields/constants.rb +55 -0
- data/lib/active_fields/engine.rb +7 -0
- data/lib/active_fields/finders/array_finder.rb +112 -0
- data/lib/active_fields/finders/base_finder.rb +73 -0
- data/lib/active_fields/finders/boolean_finder.rb +20 -0
- data/lib/active_fields/finders/date_array_finder.rb +65 -0
- data/lib/active_fields/finders/date_finder.rb +32 -0
- data/lib/active_fields/finders/date_time_array_finder.rb +65 -0
- data/lib/active_fields/finders/date_time_finder.rb +32 -0
- data/lib/active_fields/finders/decimal_array_finder.rb +65 -0
- data/lib/active_fields/finders/decimal_finder.rb +32 -0
- data/lib/active_fields/finders/enum_array_finder.rb +41 -0
- data/lib/active_fields/finders/enum_finder.rb +20 -0
- data/lib/active_fields/finders/integer_array_finder.rb +65 -0
- data/lib/active_fields/finders/integer_finder.rb +32 -0
- data/lib/active_fields/finders/singular_finder.rb +66 -0
- data/lib/active_fields/finders/text_array_finder.rb +47 -0
- data/lib/active_fields/finders/text_finder.rb +81 -0
- data/lib/active_fields/has_active_fields.rb +3 -4
- data/lib/active_fields/registry.rb +38 -0
- data/lib/active_fields/version.rb +1 -1
- data/lib/active_fields.rb +79 -31
- data/lib/generators/active_fields/install/install_generator.rb +1 -1
- data/lib/generators/active_fields/scaffold/USAGE +9 -0
- data/lib/generators/active_fields/scaffold/scaffold_generator.rb +34 -0
- data/lib/generators/active_fields/scaffold/templates/controllers/active_fields_controller.rb +133 -0
- data/lib/generators/active_fields/scaffold/templates/controllers/concerns/active_fields_controller_concern.rb +33 -0
- data/lib/generators/active_fields/scaffold/templates/helpers/active_fields_helper.rb +100 -0
- data/lib/generators/active_fields/scaffold/templates/javascript/controllers/active_fields_finders_form_controller.js +59 -0
- data/lib/generators/active_fields/scaffold/templates/javascript/controllers/array_field_controller.js +25 -0
- data/lib/generators/active_fields/scaffold/templates/views/active_fields/edit.html.erb +5 -0
- data/lib/generators/active_fields/scaffold/templates/views/active_fields/finders/_form.html.erb +42 -0
- data/lib/generators/active_fields/scaffold/templates/views/active_fields/finders/inputs/_array_size.html.erb +16 -0
- data/lib/generators/active_fields/scaffold/templates/views/active_fields/finders/inputs/_boolean.html.erb +21 -0
- data/lib/generators/active_fields/scaffold/templates/views/active_fields/finders/inputs/_date.html.erb +16 -0
- data/lib/generators/active_fields/scaffold/templates/views/active_fields/finders/inputs/_date_array.html.erb +16 -0
- data/lib/generators/active_fields/scaffold/templates/views/active_fields/finders/inputs/_datetime.html.erb +16 -0
- data/lib/generators/active_fields/scaffold/templates/views/active_fields/finders/inputs/_datetime_array.html.erb +16 -0
- data/lib/generators/active_fields/scaffold/templates/views/active_fields/finders/inputs/_decimal.html.erb +16 -0
- data/lib/generators/active_fields/scaffold/templates/views/active_fields/finders/inputs/_decimal_array.html.erb +16 -0
- data/lib/generators/active_fields/scaffold/templates/views/active_fields/finders/inputs/_enum.html.erb +16 -0
- data/lib/generators/active_fields/scaffold/templates/views/active_fields/finders/inputs/_enum_array.html.erb +16 -0
- data/lib/generators/active_fields/scaffold/templates/views/active_fields/finders/inputs/_integer.html.erb +16 -0
- data/lib/generators/active_fields/scaffold/templates/views/active_fields/finders/inputs/_integer_array.html.erb +16 -0
- data/lib/generators/active_fields/scaffold/templates/views/active_fields/finders/inputs/_text.html.erb +16 -0
- data/lib/generators/active_fields/scaffold/templates/views/active_fields/finders/inputs/_text_array.html.erb +16 -0
- data/lib/generators/active_fields/scaffold/templates/views/active_fields/forms/_boolean.html.erb +53 -0
- data/lib/generators/active_fields/scaffold/templates/views/active_fields/forms/_date.html.erb +58 -0
- data/lib/generators/active_fields/scaffold/templates/views/active_fields/forms/_date_array.html.erb +70 -0
- data/lib/generators/active_fields/scaffold/templates/views/active_fields/forms/_datetime.html.erb +63 -0
- data/lib/generators/active_fields/scaffold/templates/views/active_fields/forms/_datetime_array.html.erb +75 -0
- data/lib/generators/active_fields/scaffold/templates/views/active_fields/forms/_decimal.html.erb +63 -0
- data/lib/generators/active_fields/scaffold/templates/views/active_fields/forms/_decimal_array.html.erb +76 -0
- data/lib/generators/active_fields/scaffold/templates/views/active_fields/forms/_enum.html.erb +61 -0
- data/lib/generators/active_fields/scaffold/templates/views/active_fields/forms/_enum_array.html.erb +73 -0
- data/lib/generators/active_fields/scaffold/templates/views/active_fields/forms/_integer.html.erb +58 -0
- data/lib/generators/active_fields/scaffold/templates/views/active_fields/forms/_integer_array.html.erb +70 -0
- data/lib/generators/active_fields/scaffold/templates/views/active_fields/forms/_text.html.erb +53 -0
- data/lib/generators/active_fields/scaffold/templates/views/active_fields/forms/_text_array.html.erb +70 -0
- data/lib/generators/active_fields/scaffold/templates/views/active_fields/index.html.erb +41 -0
- data/lib/generators/active_fields/scaffold/templates/views/active_fields/new.html.erb +5 -0
- data/lib/generators/active_fields/scaffold/templates/views/active_fields/show.html.erb +29 -0
- data/lib/generators/active_fields/scaffold/templates/views/active_fields/values/inputs/_boolean.html.erb +8 -0
- data/lib/generators/active_fields/scaffold/templates/views/active_fields/values/inputs/_date.html.erb +4 -0
- data/lib/generators/active_fields/scaffold/templates/views/active_fields/values/inputs/_date_array.html.erb +12 -0
- data/lib/generators/active_fields/scaffold/templates/views/active_fields/values/inputs/_datetime.html.erb +4 -0
- data/lib/generators/active_fields/scaffold/templates/views/active_fields/values/inputs/_datetime_array.html.erb +12 -0
- data/lib/generators/active_fields/scaffold/templates/views/active_fields/values/inputs/_decimal.html.erb +4 -0
- data/lib/generators/active_fields/scaffold/templates/views/active_fields/values/inputs/_decimal_array.html.erb +12 -0
- data/lib/generators/active_fields/scaffold/templates/views/active_fields/values/inputs/_enum.html.erb +4 -0
- data/lib/generators/active_fields/scaffold/templates/views/active_fields/values/inputs/_enum_array.html.erb +4 -0
- data/lib/generators/active_fields/scaffold/templates/views/active_fields/values/inputs/_integer.html.erb +4 -0
- data/lib/generators/active_fields/scaffold/templates/views/active_fields/values/inputs/_integer_array.html.erb +12 -0
- data/lib/generators/active_fields/scaffold/templates/views/active_fields/values/inputs/_text.html.erb +4 -0
- data/lib/generators/active_fields/scaffold/templates/views/active_fields/values/inputs/_text_array.html.erb +12 -0
- data/lib/generators/active_fields/scaffold/templates/views/shared/_array_field.html.erb +19 -0
- metadata +78 -10
- data/lib/active_fields/customizable_config.rb +0 -24
@@ -6,13 +6,12 @@ module ActiveFields
|
|
6
6
|
extend ActiveSupport::Concern
|
7
7
|
|
8
8
|
class_methods do
|
9
|
-
attr_reader :active_fields_config
|
10
|
-
|
11
9
|
def has_active_fields(types: ActiveFields.config.type_names)
|
12
10
|
include CustomizableConcern
|
13
11
|
|
14
|
-
|
15
|
-
|
12
|
+
types.each do |field_type|
|
13
|
+
ActiveFields.registry.add(field_type, name)
|
14
|
+
end
|
16
15
|
end
|
17
16
|
end
|
18
17
|
end
|
@@ -0,0 +1,38 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module ActiveFields
|
4
|
+
# Storage for configured relations between active fields and customizable models
|
5
|
+
class Registry
|
6
|
+
include Singleton
|
7
|
+
|
8
|
+
def initialize
|
9
|
+
@customizables = {}
|
10
|
+
@fields = {}
|
11
|
+
end
|
12
|
+
|
13
|
+
# Add relation between active field type and customizable type
|
14
|
+
def add(field_type_name, customizable_type)
|
15
|
+
if ActiveFields.config.type_names.exclude?(field_type_name)
|
16
|
+
raise ArgumentError, "undefined ActiveFields type provided for #{customizable_type}: #{field_type_name}"
|
17
|
+
end
|
18
|
+
|
19
|
+
@customizables[field_type_name] ||= Set.new
|
20
|
+
@customizables[field_type_name] << customizable_type
|
21
|
+
|
22
|
+
@fields[customizable_type] ||= Set.new
|
23
|
+
@fields[customizable_type] << field_type_name
|
24
|
+
|
25
|
+
nil
|
26
|
+
end
|
27
|
+
|
28
|
+
# Returns customizable types that allow provided active field type name
|
29
|
+
def customizable_types_for(field_type_name)
|
30
|
+
@customizables[field_type_name]
|
31
|
+
end
|
32
|
+
|
33
|
+
# Returns active field type names, allowed for given customizable type
|
34
|
+
def field_type_names_for(customizable_type)
|
35
|
+
@fields[customizable_type]
|
36
|
+
end
|
37
|
+
end
|
38
|
+
end
|
data/lib/active_fields.rb
CHANGED
@@ -1,45 +1,91 @@
|
|
1
1
|
# frozen_string_literal: true
|
2
2
|
|
3
3
|
require_relative "active_fields/version"
|
4
|
+
require_relative "active_fields/constants"
|
4
5
|
require_relative "active_fields/engine"
|
5
6
|
|
6
7
|
module ActiveFields
|
7
|
-
|
8
|
-
|
9
|
-
|
8
|
+
extend ActiveSupport::Autoload
|
9
|
+
|
10
|
+
class << self
|
11
|
+
def eager_load!
|
12
|
+
super
|
13
|
+
Casters.eager_load!
|
14
|
+
Validators.eager_load!
|
15
|
+
Finders.eager_load!
|
16
|
+
end
|
17
|
+
end
|
18
|
+
|
19
|
+
eager_autoload do
|
20
|
+
autoload :Config
|
21
|
+
autoload :Registry
|
22
|
+
autoload :HasActiveFields
|
23
|
+
end
|
10
24
|
|
11
25
|
module Casters
|
12
|
-
|
13
|
-
|
14
|
-
|
15
|
-
|
16
|
-
|
17
|
-
|
18
|
-
|
19
|
-
|
20
|
-
|
21
|
-
|
22
|
-
|
23
|
-
|
24
|
-
|
25
|
-
|
26
|
+
extend ActiveSupport::Autoload
|
27
|
+
|
28
|
+
eager_autoload do
|
29
|
+
autoload :BaseCaster
|
30
|
+
autoload :BooleanCaster
|
31
|
+
autoload :DateCaster
|
32
|
+
autoload :DateArrayCaster
|
33
|
+
autoload :DateTimeCaster
|
34
|
+
autoload :DateTimeArrayCaster
|
35
|
+
autoload :DecimalCaster
|
36
|
+
autoload :DecimalArrayCaster
|
37
|
+
autoload :EnumCaster
|
38
|
+
autoload :EnumArrayCaster
|
39
|
+
autoload :IntegerCaster
|
40
|
+
autoload :IntegerArrayCaster
|
41
|
+
autoload :TextCaster
|
42
|
+
autoload :TextArrayCaster
|
43
|
+
end
|
26
44
|
end
|
27
45
|
|
28
46
|
module Validators
|
29
|
-
|
30
|
-
|
31
|
-
|
32
|
-
|
33
|
-
|
34
|
-
|
35
|
-
|
36
|
-
|
37
|
-
|
38
|
-
|
39
|
-
|
40
|
-
|
41
|
-
|
42
|
-
|
47
|
+
extend ActiveSupport::Autoload
|
48
|
+
|
49
|
+
eager_autoload do
|
50
|
+
autoload :BaseValidator
|
51
|
+
autoload :BooleanValidator
|
52
|
+
autoload :DateValidator
|
53
|
+
autoload :DateArrayValidator
|
54
|
+
autoload :DateTimeValidator
|
55
|
+
autoload :DateTimeArrayValidator
|
56
|
+
autoload :DecimalValidator
|
57
|
+
autoload :DecimalArrayValidator
|
58
|
+
autoload :EnumValidator
|
59
|
+
autoload :EnumArrayValidator
|
60
|
+
autoload :IntegerValidator
|
61
|
+
autoload :IntegerArrayValidator
|
62
|
+
autoload :TextValidator
|
63
|
+
autoload :TextArrayValidator
|
64
|
+
end
|
65
|
+
end
|
66
|
+
|
67
|
+
module Finders
|
68
|
+
extend ActiveSupport::Autoload
|
69
|
+
|
70
|
+
eager_autoload do
|
71
|
+
autoload :BaseFinder
|
72
|
+
autoload :SingularFinder
|
73
|
+
autoload :ArrayFinder
|
74
|
+
|
75
|
+
autoload :BooleanFinder
|
76
|
+
autoload :DateFinder
|
77
|
+
autoload :DateArrayFinder
|
78
|
+
autoload :DateTimeFinder
|
79
|
+
autoload :DateTimeArrayFinder
|
80
|
+
autoload :DecimalFinder
|
81
|
+
autoload :DecimalArrayFinder
|
82
|
+
autoload :EnumFinder
|
83
|
+
autoload :EnumArrayFinder
|
84
|
+
autoload :IntegerFinder
|
85
|
+
autoload :IntegerArrayFinder
|
86
|
+
autoload :TextFinder
|
87
|
+
autoload :TextArrayFinder
|
88
|
+
end
|
43
89
|
end
|
44
90
|
|
45
91
|
class << self
|
@@ -49,5 +95,7 @@ module ActiveFields
|
|
49
95
|
end
|
50
96
|
|
51
97
|
alias_method :configure, :config
|
98
|
+
|
99
|
+
def registry = Registry.instance
|
52
100
|
end
|
53
101
|
end
|
@@ -5,7 +5,7 @@ require "rails/generators"
|
|
5
5
|
module ActiveFields
|
6
6
|
module Generators
|
7
7
|
class InstallGenerator < ::Rails::Generators::Base
|
8
|
-
desc "This generator creates an
|
8
|
+
desc "This generator creates an initializer and copies plugin migrations"
|
9
9
|
|
10
10
|
def create_initializer
|
11
11
|
initializer "active_fields.rb", <<~RUBY
|
@@ -0,0 +1,9 @@
|
|
1
|
+
Description:
|
2
|
+
Creates a controller, routes and views for managing Active Fields,
|
3
|
+
along with form inputs for Active Values and some useful helper methods.
|
4
|
+
|
5
|
+
The code it generates is unlikely to be a perfect fit for your application.
|
6
|
+
You’ll most probably want to customize the generated code.
|
7
|
+
|
8
|
+
Examples:
|
9
|
+
`bin/rails generate active_fields:scaffold`
|
@@ -0,0 +1,34 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
require "rails/generators"
|
4
|
+
|
5
|
+
module ActiveFields
|
6
|
+
module Generators
|
7
|
+
class ScaffoldGenerator < ::Rails::Generators::Base
|
8
|
+
source_root File.expand_path("templates", __dir__)
|
9
|
+
|
10
|
+
desc "This generator creates some useful templates"
|
11
|
+
|
12
|
+
def copy_files
|
13
|
+
Dir.glob("**/*", base: self.class.source_root).each do |path|
|
14
|
+
next unless File.file?(File.expand_path(path, self.class.source_root))
|
15
|
+
|
16
|
+
copy_file path, File.join("app", path)
|
17
|
+
end
|
18
|
+
end
|
19
|
+
|
20
|
+
def insert_into_application_controller
|
21
|
+
inject_into_class "app/controllers/application_controller.rb", "ApplicationController" do
|
22
|
+
optimize_indentation(<<~CODE, 2)
|
23
|
+
include ActiveFieldsControllerConcern
|
24
|
+
helper ActiveFieldsHelper
|
25
|
+
CODE
|
26
|
+
end
|
27
|
+
end
|
28
|
+
|
29
|
+
def add_routes
|
30
|
+
route "resources :active_fields"
|
31
|
+
end
|
32
|
+
end
|
33
|
+
end
|
34
|
+
end
|
@@ -0,0 +1,133 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
class ActiveFieldsController < ApplicationController
|
4
|
+
before_action :set_active_field, only: %i[show edit update destroy]
|
5
|
+
|
6
|
+
def index
|
7
|
+
@active_fields = ActiveFields.config.field_base_class.order(:customizable_type, :id)
|
8
|
+
end
|
9
|
+
|
10
|
+
def show; end
|
11
|
+
|
12
|
+
def new
|
13
|
+
@active_field = model_class.new
|
14
|
+
end
|
15
|
+
|
16
|
+
def create
|
17
|
+
@active_field = model_class.new(active_field_create_params(model_class))
|
18
|
+
|
19
|
+
if @active_field.save
|
20
|
+
redirect_to edit_active_field_path(@active_field), status: :see_other
|
21
|
+
else
|
22
|
+
render :new, status: :unprocessable_entity
|
23
|
+
end
|
24
|
+
end
|
25
|
+
|
26
|
+
def edit; end
|
27
|
+
|
28
|
+
def update
|
29
|
+
if @active_field.update(active_field_update_params(@active_field.class))
|
30
|
+
redirect_to edit_active_field_path(@active_field), status: :see_other
|
31
|
+
else
|
32
|
+
render :edit, status: :unprocessable_entity
|
33
|
+
end
|
34
|
+
end
|
35
|
+
|
36
|
+
def destroy
|
37
|
+
@active_field.destroy!
|
38
|
+
|
39
|
+
redirect_to active_fields_path, status: :see_other
|
40
|
+
end
|
41
|
+
|
42
|
+
private
|
43
|
+
|
44
|
+
def active_field_create_params(model_class)
|
45
|
+
params.require(:active_field).permit(*permitted_attributes_for_create(model_class)).tap do |attrs|
|
46
|
+
attrs.transform_values! do |value|
|
47
|
+
value.is_a?(Array) ? compact_array_param(value) : value
|
48
|
+
end
|
49
|
+
end
|
50
|
+
end
|
51
|
+
|
52
|
+
def active_field_update_params(model_class)
|
53
|
+
params.require(:active_field).permit(*permitted_attributes_for_update(model_class)).tap do |attrs|
|
54
|
+
attrs.transform_values! do |value|
|
55
|
+
value.is_a?(Array) ? compact_array_param(value) : value
|
56
|
+
end
|
57
|
+
end
|
58
|
+
end
|
59
|
+
|
60
|
+
# It is strongly recommended to move it to, for example, policies.
|
61
|
+
def permitted_attributes_for_create(model_class)
|
62
|
+
if model_class == ActiveFields::Field::Boolean
|
63
|
+
%i[customizable_type name required nullable default_value]
|
64
|
+
elsif model_class == ActiveFields::Field::Date
|
65
|
+
%i[customizable_type name required min max default_value]
|
66
|
+
elsif model_class == ActiveFields::Field::DateArray
|
67
|
+
[:customizable_type, :name, :min_size, :max_size, :min, :max, default_value: []]
|
68
|
+
elsif model_class == ActiveFields::Field::DateTime
|
69
|
+
%i[customizable_type name required min max precision default_value]
|
70
|
+
elsif model_class == ActiveFields::Field::DateTimeArray
|
71
|
+
[:customizable_type, :name, :min_size, :max_size, :min, :max, :precision, default_value: []]
|
72
|
+
elsif model_class == ActiveFields::Field::Decimal
|
73
|
+
%i[customizable_type name required min max precision default_value]
|
74
|
+
elsif model_class == ActiveFields::Field::DecimalArray
|
75
|
+
[:customizable_type, :name, :min_size, :max_size, :min, :max, :precision, default_value: []]
|
76
|
+
elsif model_class == ActiveFields::Field::Enum
|
77
|
+
[:customizable_type, :name, :required, :default_value, allowed_values: []]
|
78
|
+
elsif model_class == ActiveFields::Field::EnumArray
|
79
|
+
[:customizable_type, :name, :min_size, :max_size, allowed_values: [], default_value: []]
|
80
|
+
elsif model_class == ActiveFields::Field::Integer
|
81
|
+
%i[customizable_type name required min max default_value]
|
82
|
+
elsif model_class == ActiveFields::Field::IntegerArray
|
83
|
+
[:customizable_type, :name, :min_size, :max_size, :min, :max, default_value: []]
|
84
|
+
elsif model_class == ActiveFields::Field::Text
|
85
|
+
%i[customizable_type name min_length max_length default_value]
|
86
|
+
elsif model_class == ActiveFields::Field::TextArray
|
87
|
+
[:customizable_type, :name, :min_size, :max_size, :min_length, :max_length, default_value: []]
|
88
|
+
else
|
89
|
+
raise ArgumentError, "undefined model_class `#{model_class.inspect}`"
|
90
|
+
end
|
91
|
+
end
|
92
|
+
|
93
|
+
# It is strongly recommended to move it to, for example, policies.
|
94
|
+
def permitted_attributes_for_update(model_class)
|
95
|
+
if model_class == ActiveFields::Field::Boolean
|
96
|
+
%i[name default_value]
|
97
|
+
elsif model_class == ActiveFields::Field::Date
|
98
|
+
%i[name default_value]
|
99
|
+
elsif model_class == ActiveFields::Field::DateArray
|
100
|
+
[:name, default_value: []]
|
101
|
+
elsif model_class == ActiveFields::Field::DateTime
|
102
|
+
%i[name default_value]
|
103
|
+
elsif model_class == ActiveFields::Field::DateTimeArray
|
104
|
+
[:name, default_value: []]
|
105
|
+
elsif model_class == ActiveFields::Field::Decimal
|
106
|
+
%i[name default_value]
|
107
|
+
elsif model_class == ActiveFields::Field::DecimalArray
|
108
|
+
[:name, default_value: []]
|
109
|
+
elsif model_class == ActiveFields::Field::Enum
|
110
|
+
%i[name default_value]
|
111
|
+
elsif model_class == ActiveFields::Field::EnumArray
|
112
|
+
[:name, default_value: []]
|
113
|
+
elsif model_class == ActiveFields::Field::Integer
|
114
|
+
%i[name default_value]
|
115
|
+
elsif model_class == ActiveFields::Field::IntegerArray
|
116
|
+
[:name, default_value: []]
|
117
|
+
elsif model_class == ActiveFields::Field::Text
|
118
|
+
%i[name default_value]
|
119
|
+
elsif model_class == ActiveFields::Field::TextArray
|
120
|
+
[:name, default_value: []]
|
121
|
+
else
|
122
|
+
raise ArgumentError, "undefined model_class `#{model_class.inspect}`"
|
123
|
+
end
|
124
|
+
end
|
125
|
+
|
126
|
+
def set_active_field
|
127
|
+
@active_field = ActiveFields.config.field_base_class.find(params[:id])
|
128
|
+
end
|
129
|
+
|
130
|
+
def model_class
|
131
|
+
(ActiveFields.config.fields[params[:type]&.to_sym] || ActiveFields.config.type_class_names.first).constantize
|
132
|
+
end
|
133
|
+
end
|
@@ -0,0 +1,33 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module ActiveFieldsControllerConcern
|
4
|
+
extend ActiveSupport::Concern
|
5
|
+
|
6
|
+
included do
|
7
|
+
helper_method :active_fields_finders_params
|
8
|
+
end
|
9
|
+
|
10
|
+
def active_fields_finders_params
|
11
|
+
@active_fields_finders_params ||=
|
12
|
+
params.permit(
|
13
|
+
f: [
|
14
|
+
:n,
|
15
|
+
:name,
|
16
|
+
:op,
|
17
|
+
:operator,
|
18
|
+
:v,
|
19
|
+
:value,
|
20
|
+
v: [],
|
21
|
+
value: [],
|
22
|
+
],
|
23
|
+
)[:f] || {}
|
24
|
+
end
|
25
|
+
|
26
|
+
def compact_array_param(value)
|
27
|
+
if value.first == ""
|
28
|
+
value[1..-1]
|
29
|
+
else
|
30
|
+
value
|
31
|
+
end
|
32
|
+
end
|
33
|
+
end
|
@@ -0,0 +1,100 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module ActiveFieldsHelper
|
4
|
+
ARRAY_SIZE_OPERATIONS = %i[size_eq size_not_eq size_gt size_gteq size_lt size_lteq].freeze
|
5
|
+
private_constant :ARRAY_SIZE_OPERATIONS
|
6
|
+
|
7
|
+
def render_array_field(form:, name:, value:, field_method:, field_opts: {})
|
8
|
+
render partial: "shared/array_field", locals: {
|
9
|
+
form: form,
|
10
|
+
name: name,
|
11
|
+
value: value,
|
12
|
+
field_method: field_method,
|
13
|
+
field_opts: field_opts,
|
14
|
+
}
|
15
|
+
end
|
16
|
+
|
17
|
+
def render_active_field_form(active_field:)
|
18
|
+
partial = active_field_form(active_field)
|
19
|
+
|
20
|
+
render partial: partial, locals: { active_field: active_field }
|
21
|
+
end
|
22
|
+
|
23
|
+
def render_active_value_input(form:, active_value:)
|
24
|
+
partial = active_value_input(active_value.active_field)
|
25
|
+
|
26
|
+
render partial: partial, locals: { form: form, active_value: active_value, active_field: active_value.active_field }
|
27
|
+
end
|
28
|
+
|
29
|
+
def render_active_fields_finders_form(active_fields:, url:)
|
30
|
+
render partial: "active_fields/finders/form", locals: { active_fields: active_fields, url: url }
|
31
|
+
end
|
32
|
+
|
33
|
+
def active_field_finder_input_types(active_field:)
|
34
|
+
return [] if active_field.value_finder_class.nil?
|
35
|
+
|
36
|
+
types = [active_field.type_name]
|
37
|
+
types += [:array_size] if active_field.value_finder_class.operations.intersect?(ARRAY_SIZE_OPERATIONS)
|
38
|
+
|
39
|
+
types
|
40
|
+
end
|
41
|
+
|
42
|
+
def render_active_field_finder_input(form:, type:, active_field:, template: false, selected: {})
|
43
|
+
if type == :array_size
|
44
|
+
operations = active_field.value_finder_class.operations & ARRAY_SIZE_OPERATIONS
|
45
|
+
partial = "active_fields/finders/inputs/array_size"
|
46
|
+
else
|
47
|
+
operations = active_field.value_finder_class.operations - ARRAY_SIZE_OPERATIONS
|
48
|
+
partial = "active_fields/finders/inputs/#{type}"
|
49
|
+
end
|
50
|
+
|
51
|
+
render partial: partial, locals: {
|
52
|
+
form: form,
|
53
|
+
active_field: active_field,
|
54
|
+
operations: operations,
|
55
|
+
template: template,
|
56
|
+
selected: selected,
|
57
|
+
}
|
58
|
+
end
|
59
|
+
|
60
|
+
def render_selected_active_field_finder_input(form:, active_fields:, finder_params:)
|
61
|
+
active_field_name = finder_params[:n] || finder_params[:name]
|
62
|
+
active_field = active_fields.find { |active_field| active_field.name == active_field_name }
|
63
|
+
return if active_field&.value_finder_class.nil?
|
64
|
+
|
65
|
+
op = (finder_params[:op] || finder_params[:operator])&.to_sym
|
66
|
+
if active_field.value_finder_class.operations.include?(op)
|
67
|
+
operation = op
|
68
|
+
operator = active_field.value_finder_class.operator_for(op)
|
69
|
+
else
|
70
|
+
operation = active_field.value_finder_class.operation_for(op)
|
71
|
+
operator = op
|
72
|
+
end
|
73
|
+
return if operation.nil? || operator.nil?
|
74
|
+
|
75
|
+
value = finder_params[:v] || finder_params[:value]
|
76
|
+
|
77
|
+
type =
|
78
|
+
if ARRAY_SIZE_OPERATIONS.include?(operation)
|
79
|
+
:array_size
|
80
|
+
else
|
81
|
+
active_field.type_name
|
82
|
+
end
|
83
|
+
|
84
|
+
render_active_field_finder_input(
|
85
|
+
form: form,
|
86
|
+
type: type,
|
87
|
+
active_field: active_field,
|
88
|
+
template: false,
|
89
|
+
selected: { operation: operation, operator: operator, value: value },
|
90
|
+
)
|
91
|
+
end
|
92
|
+
|
93
|
+
def active_field_form(active_field)
|
94
|
+
"active_fields/forms/#{active_field.type_name}"
|
95
|
+
end
|
96
|
+
|
97
|
+
def active_value_input(active_field)
|
98
|
+
"active_fields/values/inputs/#{active_field.type_name}"
|
99
|
+
end
|
100
|
+
end
|
@@ -0,0 +1,59 @@
|
|
1
|
+
import { Controller } from "@hotwired/stimulus"
|
2
|
+
|
3
|
+
export default class extends Controller {
|
4
|
+
static targets = [
|
5
|
+
"inputTemplatesContainer",
|
6
|
+
"inputTemplate",
|
7
|
+
"inputsContainer",
|
8
|
+
"input",
|
9
|
+
]
|
10
|
+
|
11
|
+
connect() {
|
12
|
+
this.inputTemplatesContainerTarget.style.display = "none"
|
13
|
+
}
|
14
|
+
|
15
|
+
addInput(e) {
|
16
|
+
e.preventDefault()
|
17
|
+
|
18
|
+
const selectedOption = e.target.options[e.target.selectedIndex]
|
19
|
+
const selectedName = selectedOption.dataset.activeFieldName
|
20
|
+
const selectedType = selectedOption.value
|
21
|
+
|
22
|
+
if (!selectedName || !selectedType) { return }
|
23
|
+
|
24
|
+
const templateInput = this.inputTemplateTargets.find((templateInput) => {
|
25
|
+
return templateInput.dataset.activeFieldName === selectedName && templateInput.dataset.type === selectedType
|
26
|
+
})
|
27
|
+
|
28
|
+
let newInput = templateInput.cloneNode(true)
|
29
|
+
newInput.dataset.activeFieldsFindersFormTarget = "input"
|
30
|
+
this.#setInputIndex(newInput)
|
31
|
+
|
32
|
+
this.inputsContainerTarget.append(newInput)
|
33
|
+
|
34
|
+
e.target.value = ""
|
35
|
+
}
|
36
|
+
|
37
|
+
removeInput(e) {
|
38
|
+
e.preventDefault()
|
39
|
+
|
40
|
+
e.target.parentElement.remove()
|
41
|
+
}
|
42
|
+
|
43
|
+
#getLastIndex() {
|
44
|
+
if (this.inputTargets.length === 0) { return -1 }
|
45
|
+
|
46
|
+
return Math.max(...this.inputTargets.map((input) => +input.dataset.index))
|
47
|
+
}
|
48
|
+
|
49
|
+
#setInputIndex(input) {
|
50
|
+
const newIndex = this.#getLastIndex() + 1
|
51
|
+
|
52
|
+
input.dataset.index = newIndex.toString()
|
53
|
+
Array.from(input.children).forEach((element) => {
|
54
|
+
if (element.hasAttribute("name") && element.name.includes("[0]")) {
|
55
|
+
element.name = element.name.replace("[0]", `[${newIndex}]`)
|
56
|
+
}
|
57
|
+
})
|
58
|
+
}
|
59
|
+
}
|
@@ -0,0 +1,25 @@
|
|
1
|
+
import { Controller } from "@hotwired/stimulus"
|
2
|
+
|
3
|
+
export default class extends Controller {
|
4
|
+
static targets = ["templateFieldGroup"]
|
5
|
+
|
6
|
+
connect() {
|
7
|
+
this.templateFieldGroupTarget.style.display = "none"
|
8
|
+
}
|
9
|
+
|
10
|
+
addField(e) {
|
11
|
+
e.preventDefault()
|
12
|
+
|
13
|
+
let newFieldGroup = this.templateFieldGroupTarget.cloneNode(true)
|
14
|
+
delete newFieldGroup.dataset.arrayFieldTarget
|
15
|
+
newFieldGroup.style.removeProperty("display")
|
16
|
+
|
17
|
+
e.target.before(newFieldGroup)
|
18
|
+
}
|
19
|
+
|
20
|
+
removeField(e) {
|
21
|
+
e.preventDefault()
|
22
|
+
|
23
|
+
e.target.parentElement.remove()
|
24
|
+
}
|
25
|
+
}
|
data/lib/generators/active_fields/scaffold/templates/views/active_fields/finders/_form.html.erb
ADDED
@@ -0,0 +1,42 @@
|
|
1
|
+
<div data-controller="active-fields-finders-form">
|
2
|
+
<% selector_options = [["-", [["< Add filter >", ""]]]] + active_fields.map do |active_field|
|
3
|
+
[
|
4
|
+
active_field.name,
|
5
|
+
active_field_finder_input_types(active_field: active_field).map do |type|
|
6
|
+
[
|
7
|
+
"#{active_field.name} [#{type}]",
|
8
|
+
type,
|
9
|
+
{ data: { "active-field-name": active_field.name } },
|
10
|
+
]
|
11
|
+
end
|
12
|
+
]
|
13
|
+
end
|
14
|
+
%>
|
15
|
+
<%= select_tag :selector,
|
16
|
+
grouped_options_for_select(selector_options),
|
17
|
+
data: { "active-fields-finders-form-target": "selector", action: "change->active-fields-finders-form#addInput" }
|
18
|
+
%>
|
19
|
+
|
20
|
+
<div data-active-fields-finders-form-target="inputTemplatesContainer" style="display: none;">
|
21
|
+
<% active_fields.each do |active_field| %>
|
22
|
+
<%= fields_for :f, nil, index: 0 do |ff| %>
|
23
|
+
<% active_field_finder_input_types(active_field: active_field).each do |type| %>
|
24
|
+
<%= render_active_field_finder_input(form: ff, type: type, active_field: active_field, template: true) %>
|
25
|
+
<% end %>
|
26
|
+
<% end %>
|
27
|
+
<% end %>
|
28
|
+
</div>
|
29
|
+
|
30
|
+
<%= form_with(url: url, method: :get) do |f| %>
|
31
|
+
<div data-active-fields-finders-form-target="inputsContainer">
|
32
|
+
<% active_fields_finders_params.each do |index, finder_params| %>
|
33
|
+
<%= fields_for :f, nil, index: index do |ff| %>
|
34
|
+
<%= render_selected_active_field_finder_input(form: ff, active_fields: active_fields, finder_params: finder_params) %>
|
35
|
+
<% end %>
|
36
|
+
<% end %>
|
37
|
+
</div>
|
38
|
+
|
39
|
+
<%= f.submit "Filter" %>
|
40
|
+
<%= link_to "Reset", url %>
|
41
|
+
<% end %>
|
42
|
+
</div>
|
@@ -0,0 +1,16 @@
|
|
1
|
+
<div
|
2
|
+
data-active-fields-finders-form-target="<%= template ? "inputTemplate" : "input" %>"
|
3
|
+
data-type="array_size"
|
4
|
+
data-active-field-name="<%= active_field.name %>"
|
5
|
+
data-index="<%= form.index %>"
|
6
|
+
>
|
7
|
+
<%= form.label :n, active_field.name, for: nil %>
|
8
|
+
<%= form.hidden_field :n, value: active_field.name, id: nil %>
|
9
|
+
<%= form.select :op,
|
10
|
+
operations.map { |operation_name| [operation_name, active_field.value_finder_class.operator_for(operation_name)] },
|
11
|
+
{ selected: selected[:operator] },
|
12
|
+
{ id: nil }
|
13
|
+
%>
|
14
|
+
<%= form.number_field :v, { value: selected[:value], min: 0, max: active_field.max_size, id: nil } %>
|
15
|
+
<%= button_tag "Remove", type: "button", data: { action: "click->active-fields-finders-form#removeInput" } %>
|
16
|
+
</div>
|