active_fields 1.0.0 → 2.0.0

Sign up to get free protection for your applications and to get access to all the features.
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>