active_fields 1.0.0 → 1.1.0

Sign up to get free protection for your applications and to get access to all the features.
Files changed (46) hide show
  1. checksums.yaml +4 -4
  2. data/.rubocop.yml +1 -1
  3. data/CHANGELOG.md +6 -0
  4. data/README.md +92 -77
  5. data/app/models/concerns/active_fields/customizable_concern.rb +4 -1
  6. data/lib/active_fields/engine.rb +6 -0
  7. data/lib/active_fields/version.rb +1 -1
  8. data/lib/active_fields.rb +51 -31
  9. data/lib/generators/active_fields/install/install_generator.rb +1 -1
  10. data/lib/generators/active_fields/scaffold/USAGE +9 -0
  11. data/lib/generators/active_fields/scaffold/scaffold_generator.rb +25 -0
  12. data/lib/generators/active_fields/scaffold/templates/controllers/active_fields_controller.rb +143 -0
  13. data/lib/generators/active_fields/scaffold/templates/helpers/active_fields_helper.rb +33 -0
  14. data/lib/generators/active_fields/scaffold/templates/javascript/controllers/array_field_controller.js +25 -0
  15. data/lib/generators/active_fields/scaffold/templates/views/active_fields/edit.html.erb +5 -0
  16. data/lib/generators/active_fields/scaffold/templates/views/active_fields/forms/_boolean.html.erb +53 -0
  17. data/lib/generators/active_fields/scaffold/templates/views/active_fields/forms/_date.html.erb +58 -0
  18. data/lib/generators/active_fields/scaffold/templates/views/active_fields/forms/_date_array.html.erb +70 -0
  19. data/lib/generators/active_fields/scaffold/templates/views/active_fields/forms/_datetime.html.erb +63 -0
  20. data/lib/generators/active_fields/scaffold/templates/views/active_fields/forms/_datetime_array.html.erb +75 -0
  21. data/lib/generators/active_fields/scaffold/templates/views/active_fields/forms/_decimal.html.erb +63 -0
  22. data/lib/generators/active_fields/scaffold/templates/views/active_fields/forms/_decimal_array.html.erb +76 -0
  23. data/lib/generators/active_fields/scaffold/templates/views/active_fields/forms/_enum.html.erb +61 -0
  24. data/lib/generators/active_fields/scaffold/templates/views/active_fields/forms/_enum_array.html.erb +73 -0
  25. data/lib/generators/active_fields/scaffold/templates/views/active_fields/forms/_integer.html.erb +58 -0
  26. data/lib/generators/active_fields/scaffold/templates/views/active_fields/forms/_integer_array.html.erb +70 -0
  27. data/lib/generators/active_fields/scaffold/templates/views/active_fields/forms/_text.html.erb +53 -0
  28. data/lib/generators/active_fields/scaffold/templates/views/active_fields/forms/_text_array.html.erb +70 -0
  29. data/lib/generators/active_fields/scaffold/templates/views/active_fields/index.html.erb +41 -0
  30. data/lib/generators/active_fields/scaffold/templates/views/active_fields/new.html.erb +5 -0
  31. data/lib/generators/active_fields/scaffold/templates/views/active_fields/show.html.erb +29 -0
  32. data/lib/generators/active_fields/scaffold/templates/views/active_fields/values/inputs/_boolean.html.erb +8 -0
  33. data/lib/generators/active_fields/scaffold/templates/views/active_fields/values/inputs/_date.html.erb +4 -0
  34. data/lib/generators/active_fields/scaffold/templates/views/active_fields/values/inputs/_date_array.html.erb +12 -0
  35. data/lib/generators/active_fields/scaffold/templates/views/active_fields/values/inputs/_datetime.html.erb +4 -0
  36. data/lib/generators/active_fields/scaffold/templates/views/active_fields/values/inputs/_datetime_array.html.erb +12 -0
  37. data/lib/generators/active_fields/scaffold/templates/views/active_fields/values/inputs/_decimal.html.erb +4 -0
  38. data/lib/generators/active_fields/scaffold/templates/views/active_fields/values/inputs/_decimal_array.html.erb +12 -0
  39. data/lib/generators/active_fields/scaffold/templates/views/active_fields/values/inputs/_enum.html.erb +4 -0
  40. data/lib/generators/active_fields/scaffold/templates/views/active_fields/values/inputs/_enum_array.html.erb +4 -0
  41. data/lib/generators/active_fields/scaffold/templates/views/active_fields/values/inputs/_integer.html.erb +4 -0
  42. data/lib/generators/active_fields/scaffold/templates/views/active_fields/values/inputs/_integer_array.html.erb +12 -0
  43. data/lib/generators/active_fields/scaffold/templates/views/active_fields/values/inputs/_text.html.erb +4 -0
  44. data/lib/generators/active_fields/scaffold/templates/views/active_fields/values/inputs/_text_array.html.erb +12 -0
  45. data/lib/generators/active_fields/scaffold/templates/views/shared/_array_field.html.erb +19 -0
  46. metadata +39 -3
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: e668baeb2df705293feec23b2948fd6ca762bd3e5f6671aec273579133cfb368
4
- data.tar.gz: a7eac6f5cb2a46e9d246e2d3d8023a9e1995446d92d9c2cf93ab1100f265d283
3
+ metadata.gz: bd75dc9f715c95766a238fd268c8174c7920e2b64ddce086f4bd7eed9ea2c36a
4
+ data.tar.gz: 74ba8eaf11a578067d0c45730a59aa60dc23363b9e5ffcce5bfca7795ee12da3
5
5
  SHA512:
6
- metadata.gz: 1c7007b25907d9f0a25b5ef76bb4b031772a0cdf859243676c242fb57f457dab768b5f629760b68661a3b00aa60601b0bf856911a816313a36a53b74206dbb4b
7
- data.tar.gz: 83595ef579ea24057de81a0f5b07563eaf77a0e8e2a78da3b30bbf4f0312246c6ca237590f77d279c990c9765f982e8293958b1df73d92996e2f63dc567990c8
6
+ metadata.gz: 4cc1b2984a7912497bc1b26cec3fc793bb897cc813c76440b5a8513834acae27d5c4df5da9c5f87cf9b36ae79351cacb52ab60965195fa6b44ce6ec8fc4f3c4e
7
+ data.tar.gz: 2f03d79bb28e91af86a99d36ff60a992b14ecf9c13d6d8a13eb92490790c4cd71afb11465173ba8a2a756cc24c3cf50c488a13bc8fca90d619b1079ec88fe216
data/.rubocop.yml CHANGED
@@ -11,7 +11,7 @@ inherit_gem:
11
11
 
12
12
  AllCops:
13
13
  TargetRubyVersion: 3.3
14
- TargetRailsVersion: 7.1
14
+ TargetRailsVersion: 7.2
15
15
  NewCops: enable
16
16
  Exclude:
17
17
  - "spec/dummy/db/schema.rb"
data/CHANGELOG.md CHANGED
@@ -1,4 +1,10 @@
1
1
  ## [Unreleased]
2
+
3
+ ## [1.1.0] - 2024-09-10
4
+ - Added scaffold generator
5
+ - Disabled models reloading to prevent STI issues
6
+
7
+ ## [1.0.0] - 2024-09-07
2
8
  - Precision configuration for decimal fields
3
9
  - Added array field types mix-in `ActiveFields::FieldArrayConcern`
4
10
  - Fixed enum types behavior for blank values
data/README.md CHANGED
@@ -9,9 +9,9 @@ enabling the addition of custom fields to any model at runtime without requiring
9
9
 
10
10
  ## Key Concepts
11
11
 
12
- - **Active Field**: A record with the definition of a custom field.
13
- - **Active Value**: A record that stores the value of an _Active Field_ for a specific _Customizable_.
14
- - **Customizable**: A record that has custom fields.
12
+ - **Customizable**: A record that has custom fields (_Entity_).
13
+ - **Active Field**: A record with the definition of a custom field (_Attribute_).
14
+ - **Active Value**: A record that stores the value of an _Active Field_ for a specific _Customizable_ (_Value_).
15
15
 
16
16
  ## Models Structure
17
17
 
@@ -24,11 +24,11 @@ classDiagram
24
24
  + string name
25
25
  + string type
26
26
  + string customizable_type
27
- + json default_value
27
+ + json default_value_meta
28
28
  + json options
29
29
  }
30
30
  class ActiveValue {
31
- + json value
31
+ + json value_meta
32
32
  }
33
33
  class Customizable {
34
34
  // This is your model
@@ -56,78 +56,90 @@ such as booleans, strings, numbers, arrays, etc.
56
56
  3. Add the `has_active_fields` method to any models where you want to enable custom fields:
57
57
 
58
58
  ```ruby
59
- class Author < ApplicationRecord
59
+ class Post < ApplicationRecord
60
60
  has_active_fields
61
61
  end
62
62
  ```
63
63
 
64
- 4. Implement the necessary code to work with _Active Fields_.
64
+ 4. Run scaffold generator.
65
65
 
66
- This plugin provides a convenient API and helpers, allowing you to write code that meets your specific needs
66
+ This plugin provides a convenient API, allowing you to write code that meets your specific needs
67
67
  without being forced to use predefined implementations that is hard to extend.
68
68
 
69
- Generally, you should:
70
- - Implement a controller and UI for managing _Active Fields_.
71
- - Add inputs for _Active Values_ in _Customizable_ forms and permit their params in the controller.
72
-
73
- To set _Active Values_ for your _Customizable_, use the `active_fields_attributes=` method,
74
- that integrates with Rails `fields_for` to generate appropriate form fields.
75
- Alternatively, the alias `active_fields=` can be used in contexts without `fields_for`, such as APIs.
76
-
77
- To prepare a collection of _Active Values_ for use with the `fields_for` builder,
78
- call the `initialize_active_values` method.
79
-
80
- **Note:** By default, Rails form fields insert an empty string into array (multiple) parameters.
81
- You’ll need to handle the removal of these empty strings.
82
-
83
- ```ruby
84
- # app/controllers/posts_controller.rb
85
- # ...
86
-
87
- def new
88
- @post = Post.new
89
- @post.initialize_active_values
90
- end
91
-
92
- def edit
93
- @post.initialize_active_values
94
- end
95
-
96
- def post_params
97
- permitted_params = params.require(:post).permit(
98
- # ...
99
- active_fields_attributes: [:name, :value, :_destroy, value: []],
100
- )
101
- permitted_params[:active_fields_attributes]&.each do |_index, value_attrs|
102
- value_attrs[:value] = compact_array_param(value_attrs[:value]) if value_attrs[:value].is_a?(Array)
103
- end
104
-
105
- permitted_params
106
- end
107
-
108
- def compact_array_param(value)
109
- if value.first == ""
110
- value[1..-1]
111
- else
112
- value
113
- end
114
- end
115
- ```
116
-
117
- ```erb
118
- # app/views/posts/_form.html.erb
119
- # ...
120
-
121
- <%= form.fields_for :active_fields, post.active_values.sort_by(&:active_field_id), include_id: false do |active_fields_form| %>
122
- <%= active_fields_form.hidden_field :name %>
123
- # Render appropriate Active Value input and (optionally) destroy flag here
124
- <% end %>
125
-
126
- # ...
127
- ```
128
-
129
- You can find a detailed [example](https://github.com/lassoid/active_fields/blob/main/spec/dummy)
130
- of how to implement this in a full-stack Rails application.
69
+ However, for a quick start, you can generate a scaffold by running the following command:
70
+
71
+ ```shell
72
+ bin/rails generate active_fields:scaffold
73
+ ```
74
+
75
+ This command generates a controller, routes and views for managing _Active Fields_,
76
+ along with form inputs for _Active Values_ and some useful helper methods.
77
+
78
+ **Note:** Don't forget to add available _Customizable_ types in generated _Active Fields_ forms.
79
+
80
+ **Note:** The array field helper uses _Stimulus_ for interactivity.
81
+ If your app doesn't already include _Stimulus_, you can [easily add it](https://github.com/hotwired/stimulus-rails).
82
+ Alternatively, if you prefer not to use _Stimulus_, you should implement your own JavaScript code.
83
+
84
+ 5. Add _Active Fields_ inputs in _Customizables_ forms and permit their params in controllers.
85
+
86
+ There are two methods available on _Customizable_ models for retrieving _Active Values_:
87
+ - `active_values` returns collection of only existing _Active Values_.
88
+ - `initialize_active_values` builds any missing _Active Values_ and returns the full collection.
89
+
90
+ Choose the method that suits your requirements.
91
+ In most cases, however, `initialize_active_values` is the more suitable option.
92
+
93
+ ```erb
94
+ # app/views/posts/_form.html.erb
95
+ # ...
96
+
97
+ <%= form.fields_for :active_fields, post.initialize_active_values.sort_by(&:active_field_id), include_id: false do |active_fields_form| %>
98
+ <%= active_fields_form.hidden_field :name %>
99
+ <%= render_active_value_input(form: active_fields_form, active_value: active_fields_form.object) %>
100
+ <% end %>
101
+
102
+ # ...
103
+ ```
104
+
105
+ Finally, permit the _Active Fields_ attributes in your _Customizables_ controllers:
106
+
107
+ ```ruby
108
+ # app/controllers/posts_controller.rb
109
+ # ...
110
+
111
+ def post_params
112
+ permitted_params = params.require(:post).permit(
113
+ # ...
114
+ active_fields_attributes: [:name, :value, :_destroy, value: []],
115
+ )
116
+ permitted_params[:active_fields_attributes]&.each do |_index, value_attrs|
117
+ value_attrs[:value] = compact_array_param(value_attrs[:value]) if value_attrs[:value].is_a?(Array)
118
+ end
119
+
120
+ permitted_params
121
+ end
122
+
123
+ # Removes an empty string from the beginning of the array parameter
124
+ def compact_array_param(value)
125
+ if value.first == ""
126
+ value[1..-1]
127
+ else
128
+ value
129
+ end
130
+ end
131
+ ```
132
+
133
+ **Note:** Here we use the `active_fields_attributes=` method (as a permitted parameter),
134
+ that integrates well with Rails `fields_for` to generate appropriate form fields.
135
+ Alternatively, the alias `active_fields=` can be used in contexts without `fields_for`, such as API controllers.
136
+
137
+ That's it!
138
+ You can now add _Active Fields_ to _Customizables_ at `http://localhost:3000/active_fields`
139
+ and fill in _Active Values_ within _Customizable_ forms.
140
+
141
+ You can also explore the [Demo app](https://github.com/lassoid/active_fields/blob/main/spec/dummy)
142
+ where the plugin is fully integrated into a full-stack Rails application.
131
143
  Feel free to explore the source code and run it locally:
132
144
 
133
145
  ```shell
@@ -251,7 +263,7 @@ classDiagram
251
263
  - `name`(`string`)
252
264
  - `type`(`string`)
253
265
  - `customizable_type`(`string`)
254
- - `default_value` (`json`)
266
+ - `default_value_meta` (`json`)
255
267
 
256
268
  ### Field Types Summary
257
269
 
@@ -495,6 +507,8 @@ For an example, refer to the [locale file](https://github.com/lassoid/active_fie
495
507
  leading to the associated _Customizables_ also becoming invalid,
496
508
  which could potentially result in update failures.
497
509
 
510
+ 3. Only _Zeitwerk_ autoloading mode is supported.
511
+
498
512
  ## API Overview
499
513
 
500
514
  ### Fields API
@@ -510,7 +524,7 @@ active_field.type # Class name of this Active Field (utilizing STI)
510
524
  active_field.customizable_type # Name of the Customizable model this Active Field is registered to
511
525
  active_field.name # Identifier of this Active Field, it should be unique in scope of customizable_type
512
526
  active_field.default_value_meta # JSON column declaring the default value. Consider using `default_value` instead
513
- active_field.options # A hash (json) containing type-specific attributes for this Active Field
527
+ active_field.options # JSON column containing type-specific attributes for this Active Field
514
528
 
515
529
  # Methods:
516
530
  active_field.default_value # Default value for all Active Values associated with this Active Field
@@ -523,7 +537,7 @@ active_field.customizable_model # Customizable model class
523
537
  active_field.type_name # Identifier of the type of this Active Field (instead of class name)
524
538
 
525
539
  # Scopes:
526
- ActiveFields::Field::Boolean.for("Author") # Collection of Active Fields registered for the specified Customizable type
540
+ ActiveFields::Field::Boolean.for("Post") # Collection of Active Fields registered for the specified Customizable type
527
541
  ```
528
542
 
529
543
  ### Values API
@@ -546,7 +560,7 @@ active_value.name # Name of the associated Active Field
546
560
  ### Customizable API
547
561
 
548
562
  ```ruby
549
- customizable = Author.take
563
+ customizable = Post.take
550
564
 
551
565
  # Associations:
552
566
  customizable.active_values # `has_many` association with Active Values linked to this Customizable
@@ -577,9 +591,10 @@ customizable.active_fields = [
577
591
  # Please use `active_fields_attributes=`/`active_fields=` instead.
578
592
  customizable.active_values_attributes = attributes
579
593
 
580
- # Build an Active Value, if it doesn't exist, with the default value for each Active Field.
594
+ # Build not existing Active Values, with the default value for each Active Field.
595
+ # Returns full collection of Active Values.
581
596
  # This method is useful with `fields_for`, allowing you to pass the collection as an argument to render new Active Values:
582
- # `form.fields_for :active_values, customizable.active_values`.
597
+ # `form.fields_for :active_fields, customizable.initialize_active_values`.
583
598
  customizable.initialize_active_values
584
599
  ```
585
600
 
@@ -602,7 +617,7 @@ ActiveFields.config.register_field(:ip, "IpField") # Register a custom Active Fi
602
617
  ### Customizable Config
603
618
 
604
619
  ```ruby
605
- customizable_model = Author
620
+ customizable_model = Post
606
621
  customizable_model.active_fields_config # Access the Customizable's configuration
607
622
  customizable_model.active_fields_config.customizable_model # The Customizable model itself
608
623
  customizable_model.active_fields_config.types # Allowed Active Field types (e.g., `[:boolean]`)
@@ -80,7 +80,8 @@ module ActiveFields
80
80
 
81
81
  alias_method :active_fields=, :active_fields_attributes=
82
82
 
83
- # Build an active_value, if it doesn't exist, with a default value for each available active_field
83
+ # Build an active_value, if it doesn't exist, with a default value for each available active_field.
84
+ # Returns active_values collection.
84
85
  def initialize_active_values
85
86
  existing_field_ids = active_values.map(&:active_field_id)
86
87
 
@@ -89,6 +90,8 @@ module ActiveFields
89
90
 
90
91
  active_values.new(active_field: active_field, value: active_field.default_value)
91
92
  end
93
+
94
+ active_values
92
95
  end
93
96
  end
94
97
  end
@@ -4,6 +4,12 @@ module ActiveFields
4
4
  class Engine < ::Rails::Engine
5
5
  isolate_namespace ActiveFields
6
6
 
7
+ config.eager_load_namespaces << ActiveFields
8
+
9
+ # Disable models reloading to avoid STI issues.
10
+ # Reloading can prevent subclasses from recognizing the base class.
11
+ config.autoload_once_paths = %W[#{root}/app/models #{root}/app/models/concerns]
12
+
7
13
  initializer "active_fields.active_record" do
8
14
  ActiveSupport.on_load(:active_record) do
9
15
  include HasActiveFields
@@ -1,5 +1,5 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  module ActiveFields
4
- VERSION = "1.0.0"
4
+ VERSION = "1.1.0"
5
5
  end
data/lib/active_fields.rb CHANGED
@@ -4,42 +4,62 @@ require_relative "active_fields/version"
4
4
  require_relative "active_fields/engine"
5
5
 
6
6
  module ActiveFields
7
- autoload :Config, "active_fields/config"
8
- autoload :CustomizableConfig, "active_fields/customizable_config"
9
- autoload :HasActiveFields, "active_fields/has_active_fields"
7
+ extend ActiveSupport::Autoload
8
+
9
+ class << self
10
+ def eager_load!
11
+ super
12
+ Casters.eager_load!
13
+ Validators.eager_load!
14
+ end
15
+ end
16
+
17
+ eager_autoload do
18
+ autoload :Config
19
+ autoload :CustomizableConfig
20
+ autoload :HasActiveFields
21
+ end
10
22
 
11
23
  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"
24
+ extend ActiveSupport::Autoload
25
+
26
+ eager_autoload do
27
+ autoload :BaseCaster
28
+ autoload :BooleanCaster
29
+ autoload :DateCaster
30
+ autoload :DateArrayCaster
31
+ autoload :DateTimeCaster
32
+ autoload :DateTimeArrayCaster
33
+ autoload :DecimalCaster
34
+ autoload :DecimalArrayCaster
35
+ autoload :EnumCaster
36
+ autoload :EnumArrayCaster
37
+ autoload :IntegerCaster
38
+ autoload :IntegerArrayCaster
39
+ autoload :TextCaster
40
+ autoload :TextArrayCaster
41
+ end
26
42
  end
27
43
 
28
44
  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"
45
+ extend ActiveSupport::Autoload
46
+
47
+ eager_autoload do
48
+ autoload :BaseValidator
49
+ autoload :BooleanValidator
50
+ autoload :DateValidator
51
+ autoload :DateArrayValidator
52
+ autoload :DateTimeValidator
53
+ autoload :DateTimeArrayValidator
54
+ autoload :DecimalValidator
55
+ autoload :DecimalArrayValidator
56
+ autoload :EnumValidator
57
+ autoload :EnumArrayValidator
58
+ autoload :IntegerValidator
59
+ autoload :IntegerArrayValidator
60
+ autoload :TextValidator
61
+ autoload :TextArrayValidator
62
+ end
43
63
  end
44
64
 
45
65
  class << self
@@ -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,25 @@
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 add_routes
21
+ route "resources :active_fields"
22
+ end
23
+ end
24
+ end
25
+ end
@@ -0,0 +1,143 @@
1
+ # frozen_string_literal: true
2
+
3
+ class ActiveFieldsController < ApplicationController
4
+ helper ActiveFieldsHelper
5
+
6
+ before_action :set_active_field, only: %i[show edit update destroy]
7
+
8
+ def index
9
+ @active_fields = ActiveFields.config.field_base_class.order(:customizable_type, :id)
10
+ end
11
+
12
+ def show; end
13
+
14
+ def new
15
+ @active_field = model_class.new
16
+ end
17
+
18
+ def create
19
+ @active_field = model_class.new(active_field_create_params(model_class))
20
+
21
+ if @active_field.save
22
+ redirect_to edit_active_field_path(@active_field), status: :see_other
23
+ else
24
+ render :new, status: :unprocessable_entity
25
+ end
26
+ end
27
+
28
+ def edit; end
29
+
30
+ def update
31
+ if @active_field.update(active_field_update_params(@active_field.class))
32
+ redirect_to edit_active_field_path(@active_field), status: :see_other
33
+ else
34
+ render :edit, status: :unprocessable_entity
35
+ end
36
+ end
37
+
38
+ def destroy
39
+ @active_field.destroy!
40
+
41
+ redirect_to active_fields_path, status: :see_other
42
+ end
43
+
44
+ private
45
+
46
+ def active_field_create_params(model_class)
47
+ params.require(:active_field).permit(*permitted_attributes_for_create(model_class)).tap do |attrs|
48
+ attrs.transform_values! do |value|
49
+ value.is_a?(Array) ? compact_array_param(value) : value
50
+ end
51
+ end
52
+ end
53
+
54
+ def active_field_update_params(model_class)
55
+ params.require(:active_field).permit(*permitted_attributes_for_update(model_class)).tap do |attrs|
56
+ attrs.transform_values! do |value|
57
+ value.is_a?(Array) ? compact_array_param(value) : value
58
+ end
59
+ end
60
+ end
61
+
62
+ def compact_array_param(value)
63
+ if value.first == ""
64
+ value[1..-1]
65
+ else
66
+ value
67
+ end
68
+ end
69
+
70
+ # It is strongly recommended to move it to, for example, policies.
71
+ def permitted_attributes_for_create(model_class)
72
+ if model_class == ActiveFields::Field::Boolean
73
+ %i[customizable_type name required nullable default_value]
74
+ elsif model_class == ActiveFields::Field::Date
75
+ %i[customizable_type name required min max default_value]
76
+ elsif model_class == ActiveFields::Field::DateArray
77
+ [:customizable_type, :name, :min_size, :max_size, :min, :max, default_value: []]
78
+ elsif model_class == ActiveFields::Field::DateTime
79
+ %i[customizable_type name required min max precision default_value]
80
+ elsif model_class == ActiveFields::Field::DateTimeArray
81
+ [:customizable_type, :name, :min_size, :max_size, :min, :max, :precision, default_value: []]
82
+ elsif model_class == ActiveFields::Field::Decimal
83
+ %i[customizable_type name required min max precision default_value]
84
+ elsif model_class == ActiveFields::Field::DecimalArray
85
+ [:customizable_type, :name, :min_size, :max_size, :min, :max, :precision, default_value: []]
86
+ elsif model_class == ActiveFields::Field::Enum
87
+ [:customizable_type, :name, :required, :default_value, allowed_values: []]
88
+ elsif model_class == ActiveFields::Field::EnumArray
89
+ [:customizable_type, :name, :min_size, :max_size, allowed_values: [], default_value: []]
90
+ elsif model_class == ActiveFields::Field::Integer
91
+ %i[customizable_type name required min max default_value]
92
+ elsif model_class == ActiveFields::Field::IntegerArray
93
+ [:customizable_type, :name, :min_size, :max_size, :min, :max, default_value: []]
94
+ elsif model_class == ActiveFields::Field::Text
95
+ %i[customizable_type name min_length max_length default_value]
96
+ elsif model_class == ActiveFields::Field::TextArray
97
+ [:customizable_type, :name, :min_size, :max_size, :min_length, :max_length, default_value: []]
98
+ else
99
+ raise ArgumentError, "undefined model_class `#{model_class.inspect}`"
100
+ end
101
+ end
102
+
103
+ # It is strongly recommended to move it to, for example, policies.
104
+ def permitted_attributes_for_update(model_class)
105
+ if model_class == ActiveFields::Field::Boolean
106
+ %i[name default_value]
107
+ elsif model_class == ActiveFields::Field::Date
108
+ %i[name default_value]
109
+ elsif model_class == ActiveFields::Field::DateArray
110
+ [:name, default_value: []]
111
+ elsif model_class == ActiveFields::Field::DateTime
112
+ %i[name default_value]
113
+ elsif model_class == ActiveFields::Field::DateTimeArray
114
+ [:name, default_value: []]
115
+ elsif model_class == ActiveFields::Field::Decimal
116
+ %i[name default_value]
117
+ elsif model_class == ActiveFields::Field::DecimalArray
118
+ [:name, default_value: []]
119
+ elsif model_class == ActiveFields::Field::Enum
120
+ %i[name default_value]
121
+ elsif model_class == ActiveFields::Field::EnumArray
122
+ [:name, default_value: []]
123
+ elsif model_class == ActiveFields::Field::Integer
124
+ %i[name default_value]
125
+ elsif model_class == ActiveFields::Field::IntegerArray
126
+ [:name, default_value: []]
127
+ elsif model_class == ActiveFields::Field::Text
128
+ %i[name default_value]
129
+ elsif model_class == ActiveFields::Field::TextArray
130
+ [:name, default_value: []]
131
+ else
132
+ raise ArgumentError, "undefined model_class `#{model_class.inspect}`"
133
+ end
134
+ end
135
+
136
+ def set_active_field
137
+ @active_field = ActiveFields.config.field_base_class.find(params[:id])
138
+ end
139
+
140
+ def model_class
141
+ (ActiveFields.config.fields[params[:type]&.to_sym] || ActiveFields.config.type_class_names.first).constantize
142
+ end
143
+ end
@@ -0,0 +1,33 @@
1
+ # frozen_string_literal: true
2
+
3
+ module ActiveFieldsHelper
4
+ def render_array_field(form:, name:, value:, field_method:, field_opts: {})
5
+ render partial: "shared/array_field", locals: {
6
+ form: form,
7
+ name: name,
8
+ value: value,
9
+ field_method: field_method,
10
+ field_opts: field_opts,
11
+ }
12
+ end
13
+
14
+ def render_active_field_form(active_field:)
15
+ partial = active_field_form(active_field)
16
+
17
+ render partial: partial, locals: { active_field: active_field }
18
+ end
19
+
20
+ def render_active_value_input(form:, active_value:)
21
+ partial = active_value_input(active_value.active_field)
22
+
23
+ render partial: partial, locals: { form: form, active_value: active_value, active_field: active_value.active_field }
24
+ end
25
+
26
+ def active_field_form(active_field)
27
+ "active_fields/forms/#{active_field.type_name}"
28
+ end
29
+
30
+ def active_value_input(active_field)
31
+ "active_fields/values/inputs/#{active_field.type_name}"
32
+ end
33
+ end