active_fields 1.0.0 → 1.1.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 (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