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.
Files changed (101) hide show
  1. checksums.yaml +4 -4
  2. data/.rubocop.yml +5 -4
  3. data/CHANGELOG.md +33 -2
  4. data/README.md +478 -90
  5. data/app/models/active_fields/field/boolean.rb +3 -0
  6. data/app/models/active_fields/field/date.rb +3 -0
  7. data/app/models/active_fields/field/date_array.rb +3 -0
  8. data/app/models/active_fields/field/date_time.rb +4 -1
  9. data/app/models/active_fields/field/date_time_array.rb +4 -1
  10. data/app/models/active_fields/field/decimal.rb +6 -1
  11. data/app/models/active_fields/field/decimal_array.rb +6 -1
  12. data/app/models/active_fields/field/enum.rb +3 -0
  13. data/app/models/active_fields/field/enum_array.rb +3 -0
  14. data/app/models/active_fields/field/integer.rb +3 -0
  15. data/app/models/active_fields/field/integer_array.rb +3 -0
  16. data/app/models/active_fields/field/text.rb +3 -0
  17. data/app/models/active_fields/field/text_array.rb +3 -0
  18. data/app/models/active_fields/field.rb +5 -0
  19. data/app/models/concerns/active_fields/customizable_concern.rb +93 -4
  20. data/app/models/concerns/active_fields/field_concern.rb +26 -3
  21. data/db/migrate/20240229230000_create_active_fields_tables.rb +1 -1
  22. data/lib/active_fields/casters/date_time_caster.rb +1 -3
  23. data/lib/active_fields/casters/decimal_caster.rb +2 -5
  24. data/lib/active_fields/constants.rb +55 -0
  25. data/lib/active_fields/engine.rb +7 -0
  26. data/lib/active_fields/finders/array_finder.rb +112 -0
  27. data/lib/active_fields/finders/base_finder.rb +73 -0
  28. data/lib/active_fields/finders/boolean_finder.rb +20 -0
  29. data/lib/active_fields/finders/date_array_finder.rb +65 -0
  30. data/lib/active_fields/finders/date_finder.rb +32 -0
  31. data/lib/active_fields/finders/date_time_array_finder.rb +65 -0
  32. data/lib/active_fields/finders/date_time_finder.rb +32 -0
  33. data/lib/active_fields/finders/decimal_array_finder.rb +65 -0
  34. data/lib/active_fields/finders/decimal_finder.rb +32 -0
  35. data/lib/active_fields/finders/enum_array_finder.rb +41 -0
  36. data/lib/active_fields/finders/enum_finder.rb +20 -0
  37. data/lib/active_fields/finders/integer_array_finder.rb +65 -0
  38. data/lib/active_fields/finders/integer_finder.rb +32 -0
  39. data/lib/active_fields/finders/singular_finder.rb +66 -0
  40. data/lib/active_fields/finders/text_array_finder.rb +47 -0
  41. data/lib/active_fields/finders/text_finder.rb +81 -0
  42. data/lib/active_fields/has_active_fields.rb +3 -4
  43. data/lib/active_fields/registry.rb +38 -0
  44. data/lib/active_fields/version.rb +1 -1
  45. data/lib/active_fields.rb +79 -31
  46. data/lib/generators/active_fields/install/install_generator.rb +1 -1
  47. data/lib/generators/active_fields/scaffold/USAGE +9 -0
  48. data/lib/generators/active_fields/scaffold/scaffold_generator.rb +34 -0
  49. data/lib/generators/active_fields/scaffold/templates/controllers/active_fields_controller.rb +133 -0
  50. data/lib/generators/active_fields/scaffold/templates/controllers/concerns/active_fields_controller_concern.rb +33 -0
  51. data/lib/generators/active_fields/scaffold/templates/helpers/active_fields_helper.rb +100 -0
  52. data/lib/generators/active_fields/scaffold/templates/javascript/controllers/active_fields_finders_form_controller.js +59 -0
  53. data/lib/generators/active_fields/scaffold/templates/javascript/controllers/array_field_controller.js +25 -0
  54. data/lib/generators/active_fields/scaffold/templates/views/active_fields/edit.html.erb +5 -0
  55. data/lib/generators/active_fields/scaffold/templates/views/active_fields/finders/_form.html.erb +42 -0
  56. data/lib/generators/active_fields/scaffold/templates/views/active_fields/finders/inputs/_array_size.html.erb +16 -0
  57. data/lib/generators/active_fields/scaffold/templates/views/active_fields/finders/inputs/_boolean.html.erb +21 -0
  58. data/lib/generators/active_fields/scaffold/templates/views/active_fields/finders/inputs/_date.html.erb +16 -0
  59. data/lib/generators/active_fields/scaffold/templates/views/active_fields/finders/inputs/_date_array.html.erb +16 -0
  60. data/lib/generators/active_fields/scaffold/templates/views/active_fields/finders/inputs/_datetime.html.erb +16 -0
  61. data/lib/generators/active_fields/scaffold/templates/views/active_fields/finders/inputs/_datetime_array.html.erb +16 -0
  62. data/lib/generators/active_fields/scaffold/templates/views/active_fields/finders/inputs/_decimal.html.erb +16 -0
  63. data/lib/generators/active_fields/scaffold/templates/views/active_fields/finders/inputs/_decimal_array.html.erb +16 -0
  64. data/lib/generators/active_fields/scaffold/templates/views/active_fields/finders/inputs/_enum.html.erb +16 -0
  65. data/lib/generators/active_fields/scaffold/templates/views/active_fields/finders/inputs/_enum_array.html.erb +16 -0
  66. data/lib/generators/active_fields/scaffold/templates/views/active_fields/finders/inputs/_integer.html.erb +16 -0
  67. data/lib/generators/active_fields/scaffold/templates/views/active_fields/finders/inputs/_integer_array.html.erb +16 -0
  68. data/lib/generators/active_fields/scaffold/templates/views/active_fields/finders/inputs/_text.html.erb +16 -0
  69. data/lib/generators/active_fields/scaffold/templates/views/active_fields/finders/inputs/_text_array.html.erb +16 -0
  70. data/lib/generators/active_fields/scaffold/templates/views/active_fields/forms/_boolean.html.erb +53 -0
  71. data/lib/generators/active_fields/scaffold/templates/views/active_fields/forms/_date.html.erb +58 -0
  72. data/lib/generators/active_fields/scaffold/templates/views/active_fields/forms/_date_array.html.erb +70 -0
  73. data/lib/generators/active_fields/scaffold/templates/views/active_fields/forms/_datetime.html.erb +63 -0
  74. data/lib/generators/active_fields/scaffold/templates/views/active_fields/forms/_datetime_array.html.erb +75 -0
  75. data/lib/generators/active_fields/scaffold/templates/views/active_fields/forms/_decimal.html.erb +63 -0
  76. data/lib/generators/active_fields/scaffold/templates/views/active_fields/forms/_decimal_array.html.erb +76 -0
  77. data/lib/generators/active_fields/scaffold/templates/views/active_fields/forms/_enum.html.erb +61 -0
  78. data/lib/generators/active_fields/scaffold/templates/views/active_fields/forms/_enum_array.html.erb +73 -0
  79. data/lib/generators/active_fields/scaffold/templates/views/active_fields/forms/_integer.html.erb +58 -0
  80. data/lib/generators/active_fields/scaffold/templates/views/active_fields/forms/_integer_array.html.erb +70 -0
  81. data/lib/generators/active_fields/scaffold/templates/views/active_fields/forms/_text.html.erb +53 -0
  82. data/lib/generators/active_fields/scaffold/templates/views/active_fields/forms/_text_array.html.erb +70 -0
  83. data/lib/generators/active_fields/scaffold/templates/views/active_fields/index.html.erb +41 -0
  84. data/lib/generators/active_fields/scaffold/templates/views/active_fields/new.html.erb +5 -0
  85. data/lib/generators/active_fields/scaffold/templates/views/active_fields/show.html.erb +29 -0
  86. data/lib/generators/active_fields/scaffold/templates/views/active_fields/values/inputs/_boolean.html.erb +8 -0
  87. data/lib/generators/active_fields/scaffold/templates/views/active_fields/values/inputs/_date.html.erb +4 -0
  88. data/lib/generators/active_fields/scaffold/templates/views/active_fields/values/inputs/_date_array.html.erb +12 -0
  89. data/lib/generators/active_fields/scaffold/templates/views/active_fields/values/inputs/_datetime.html.erb +4 -0
  90. data/lib/generators/active_fields/scaffold/templates/views/active_fields/values/inputs/_datetime_array.html.erb +12 -0
  91. data/lib/generators/active_fields/scaffold/templates/views/active_fields/values/inputs/_decimal.html.erb +4 -0
  92. data/lib/generators/active_fields/scaffold/templates/views/active_fields/values/inputs/_decimal_array.html.erb +12 -0
  93. data/lib/generators/active_fields/scaffold/templates/views/active_fields/values/inputs/_enum.html.erb +4 -0
  94. data/lib/generators/active_fields/scaffold/templates/views/active_fields/values/inputs/_enum_array.html.erb +4 -0
  95. data/lib/generators/active_fields/scaffold/templates/views/active_fields/values/inputs/_integer.html.erb +4 -0
  96. data/lib/generators/active_fields/scaffold/templates/views/active_fields/values/inputs/_integer_array.html.erb +12 -0
  97. data/lib/generators/active_fields/scaffold/templates/views/active_fields/values/inputs/_text.html.erb +4 -0
  98. data/lib/generators/active_fields/scaffold/templates/views/active_fields/values/inputs/_text_array.html.erb +12 -0
  99. data/lib/generators/active_fields/scaffold/templates/views/shared/_array_field.html.erb +19 -0
  100. metadata +78 -10
  101. 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
- @active_fields_config = CustomizableConfig.new(self)
15
- @active_fields_config.types = types
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
@@ -1,5 +1,5 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  module ActiveFields
4
- VERSION = "1.0.0"
4
+ VERSION = "2.0.0"
5
5
  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
- autoload :Config, "active_fields/config"
8
- autoload :CustomizableConfig, "active_fields/customizable_config"
9
- autoload :HasActiveFields, "active_fields/has_active_fields"
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
- autoload :BaseCaster, "active_fields/casters/base_caster"
13
- autoload :BooleanCaster, "active_fields/casters/boolean_caster"
14
- autoload :DateCaster, "active_fields/casters/date_caster"
15
- autoload :DateArrayCaster, "active_fields/casters/date_array_caster"
16
- autoload :DateTimeCaster, "active_fields/casters/date_time_caster"
17
- autoload :DateTimeArrayCaster, "active_fields/casters/date_time_array_caster"
18
- autoload :DecimalCaster, "active_fields/casters/decimal_caster"
19
- autoload :DecimalArrayCaster, "active_fields/casters/decimal_array_caster"
20
- autoload :EnumCaster, "active_fields/casters/enum_caster"
21
- autoload :EnumArrayCaster, "active_fields/casters/enum_array_caster"
22
- autoload :IntegerCaster, "active_fields/casters/integer_caster"
23
- autoload :IntegerArrayCaster, "active_fields/casters/integer_array_caster"
24
- autoload :TextCaster, "active_fields/casters/text_caster"
25
- autoload :TextArrayCaster, "active_fields/casters/text_array_caster"
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
- autoload :BaseValidator, "active_fields/validators/base_validator"
30
- autoload :BooleanValidator, "active_fields/validators/boolean_validator"
31
- autoload :DateValidator, "active_fields/validators/date_validator"
32
- autoload :DateArrayValidator, "active_fields/validators/date_array_validator"
33
- autoload :DateTimeValidator, "active_fields/validators/date_time_validator"
34
- autoload :DateTimeArrayValidator, "active_fields/validators/date_time_array_validator"
35
- autoload :DecimalValidator, "active_fields/validators/decimal_validator"
36
- autoload :DecimalArrayValidator, "active_fields/validators/decimal_array_validator"
37
- autoload :EnumValidator, "active_fields/validators/enum_validator"
38
- autoload :EnumArrayValidator, "active_fields/validators/enum_array_validator"
39
- autoload :IntegerValidator, "active_fields/validators/integer_validator"
40
- autoload :IntegerArrayValidator, "active_fields/validators/integer_array_validator"
41
- autoload :TextValidator, "active_fields/validators/text_validator"
42
- autoload :TextArrayValidator, "active_fields/validators/text_array_validator"
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 create_initializer and copies plugin migrations"
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
+ }
@@ -0,0 +1,5 @@
1
+ <h1>Editing Active Field #<%= @active_field.id %></h1>
2
+
3
+ <%= render_active_field_form(active_field: @active_field) %>
4
+
5
+ <div><%= link_to "Back", active_fields_path %></div>
@@ -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>