active_element 0.0.13 → 0.0.15

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 (44) hide show
  1. checksums.yaml +4 -4
  2. data/Gemfile.lock +16 -31
  3. data/app/assets/javascripts/active_element/form.js +2 -1
  4. data/app/assets/javascripts/active_element/json_field.js +8 -1
  5. data/app/views/active_element/_title.html.erb +1 -0
  6. data/app/views/active_element/components/button.html.erb +6 -4
  7. data/app/views/active_element/components/form/_check_boxes.html.erb +6 -4
  8. data/app/views/active_element/components/form/_date_range_field.html.erb +14 -0
  9. data/app/views/active_element/components/form/_field.html.erb +3 -0
  10. data/app/views/active_element/components/form/_summary.html.erb +10 -0
  11. data/app/views/active_element/components/form.html.erb +12 -2
  12. data/app/views/active_element/components/table/_collection_row.html.erb +3 -3
  13. data/app/views/active_element/components/table/_field.html.erb +2 -2
  14. data/app/views/active_element/components/table/collection.html.erb +20 -11
  15. data/app/views/active_element/components/table/item.html.erb +4 -0
  16. data/app/views/active_element/default_views/forbidden.html.erb +17 -3
  17. data/app/views/active_element/default_views/index.html.erb +14 -6
  18. data/app/views/layouts/active_element.html.erb +4 -2
  19. data/config/locales/en.yml +3 -0
  20. data/example_app/.ruby-version +1 -1
  21. data/example_app/Gemfile +0 -2
  22. data/example_app/Gemfile.lock +5 -4
  23. data/lib/active_element/components/button.rb +6 -4
  24. data/lib/active_element/components/collection_table.rb +10 -3
  25. data/lib/active_element/components/form.rb +27 -2
  26. data/lib/active_element/components/item_table.rb +5 -3
  27. data/lib/active_element/components/navbar.rb +1 -1
  28. data/lib/active_element/components/util/association_mapping.rb +50 -26
  29. data/lib/active_element/components/util/default_display_value.rb +51 -0
  30. data/lib/active_element/components/util/display_value_mapping.rb +10 -0
  31. data/lib/active_element/components/util/field_mapping.rb +2 -2
  32. data/lib/active_element/components/util/form_field_mapping.rb +68 -15
  33. data/lib/active_element/components/util/record_mapping.rb +24 -3
  34. data/lib/active_element/components/util/record_path.rb +51 -20
  35. data/lib/active_element/components/util.rb +13 -0
  36. data/lib/active_element/controller_interface.rb +11 -1
  37. data/lib/active_element/controller_state.rb +4 -2
  38. data/lib/active_element/default_controller/params.rb +9 -1
  39. data/lib/active_element/default_controller/search.rb +22 -16
  40. data/lib/active_element/field_options.rb +20 -0
  41. data/lib/active_element/version.rb +1 -1
  42. data/lib/active_element.rb +1 -0
  43. data/rspec-documentation/pages/016-Default Controller.md +10 -1
  44. metadata +6 -2
@@ -8,6 +8,7 @@ require_relative 'util/form_field_mapping'
8
8
  require_relative 'util/form_value_mapping'
9
9
  require_relative 'util/display_value_mapping'
10
10
  require_relative 'util/association_mapping'
11
+ require_relative 'util/default_display_value'
11
12
  require_relative 'util/decorator'
12
13
  require_relative 'util/numeric_field'
13
14
 
@@ -23,12 +24,24 @@ module ActiveElement
23
24
  record&.try(:model_name)&.try(&:singular) || default_record_name(record)
24
25
  end
25
26
 
27
+ # TODO: Remove and use .sti_record_names everywhere instead.
26
28
  def self.sti_record_name(record)
27
29
  return default_record_name(record) unless record.class.respond_to?(:inheritance_column)
28
30
 
29
31
  record&.class&.superclass&.model_name&.singular if record&.try(record.class.inheritance_column).present?
30
32
  end
31
33
 
34
+ def self.sti_record_names(record) # rubocop:disable Metrics/CyclomaticComplexity
35
+ record.class.ancestors.select do |ancestor|
36
+ next false if ancestor == record.class
37
+ next false if ancestor.try(:inheritance_column).blank?
38
+ next false unless ancestor < ActiveRecord::Base
39
+ next false if ancestor.abstract_class?
40
+
41
+ true
42
+ end.map(&:model_name).map(&:singular)
43
+ end
44
+
32
45
  def self.default_record_name(record)
33
46
  (record.is_a?(Class) ? record.name : record.class.name).demodulize.underscore
34
47
  end
@@ -38,10 +38,16 @@ module ActiveElement
38
38
  state.editable_fields.concat(args.map(&:to_sym)).uniq!
39
39
  end
40
40
 
41
- def searchable_fields(*args)
41
+ def searchable_fields(*args, required: false)
42
42
  state.searchable_fields.concat(args.map(&:to_sym)).uniq!
43
+ state.search_required = required
43
44
  end
44
45
 
46
+ def field_options(field, &block)
47
+ state.field_options[field] = block
48
+ end
49
+ alias field field_options
50
+
45
51
  def deletable
46
52
  state.deletable = true
47
53
  end
@@ -50,6 +56,10 @@ module ActiveElement
50
56
  RailsComponent.new(::Rails).application_name
51
57
  end
52
58
 
59
+ def t(identifier, **kwargs)
60
+ ::I18n.t("active_element.#{identifier}", **kwargs)
61
+ end
62
+
53
63
  def authenticate_with(&block)
54
64
  state.authenticator = block
55
65
  end
@@ -5,9 +5,10 @@ module ActiveElement
5
5
  # configuration. Used throughout ActiveElement for generating dynamic content based on
6
6
  # controller configuration.
7
7
  class ControllerState
8
- attr_reader :permissions, :listable_fields, :viewable_fields, :editable_fields, :searchable_fields
8
+ attr_reader :permissions, :listable_fields, :viewable_fields, :editable_fields, :searchable_fields,
9
+ :field_options
9
10
  attr_accessor :sign_in_path, :sign_in, :sign_in_method, :sign_out_path, :sign_out_method,
10
- :deletable, :authorizor, :authenticator, :list_order
11
+ :deletable, :authorizor, :authenticator, :list_order, :search_required
11
12
 
12
13
  def initialize(controller:)
13
14
  @controller = controller
@@ -19,6 +20,7 @@ module ActiveElement
19
20
  @viewable_fields = []
20
21
  @editable_fields = []
21
22
  @searchable_fields = []
23
+ @field_options = {}
22
24
  end
23
25
 
24
26
  def deletable?
@@ -33,7 +33,15 @@ module ActiveElement
33
33
  scalar, json = controller.active_element.state.editable_fields.partition do |field|
34
34
  scalar?(field)
35
35
  end
36
- scalar + [json_params(json)]
36
+ scalar + relation_fields + [json_params(json)]
37
+ end
38
+
39
+ def relation_fields
40
+ controller.active_element.state.editable_fields.map do |field|
41
+ next nil unless relation?(field)
42
+
43
+ relation(field).try(:foreign_key)
44
+ end.compact
37
45
  end
38
46
 
39
47
  def scalar?(field)
@@ -25,9 +25,11 @@ module ActiveElement
25
25
  conditions = search_filters.to_h.map do |key, value|
26
26
  next relation_matches(key, value) if relation?(key)
27
27
  next datetime_between(key, value) if datetime?(key)
28
+ next model.arel_table[key].matches("#{value}%") if string_like_column?(key)
28
29
 
29
- model.arel_table[key].matches("#{value}%")
30
+ model.arel_table[key].eq(value)
30
31
  end
32
+
31
33
  conditions[1..].reduce(conditions.first) do |accumulated, condition|
32
34
  accumulated.and(condition)
33
35
  end
@@ -41,12 +43,27 @@ module ActiveElement
41
43
 
42
44
  attr_reader :controller, :model
43
45
 
46
+ def string_like_column?(key)
47
+ [:string, :text].include?(
48
+ model.columns.find { |column| column.name.to_s == key.to_s }&.type&.to_sym
49
+ )
50
+ end
51
+
44
52
  def searchable_fields
45
- controller.active_element.state.searchable_fields.map do |field|
53
+ fields = controller.active_element.state.searchable_fields.map do |field|
46
54
  next field unless field.to_s.end_with?('_at')
47
55
 
48
56
  { field => %i[from to] }
49
57
  end
58
+ (fields + relation_fields).uniq
59
+ end
60
+
61
+ def relation_fields
62
+ controller.active_element.state.searchable_fields.map do |field|
63
+ next nil unless relation?(field)
64
+
65
+ relation(field).try(:foreign_key)
66
+ end.compact
50
67
  end
51
68
 
52
69
  def noop
@@ -76,21 +93,10 @@ module ActiveElement
76
93
  end
77
94
 
78
95
  def relation_matches(key, value)
79
- fields = searchable_relation_fields(key)
80
- relation_model = relation(key).klass
81
- fields.select! do |field|
82
- relation_model.columns.find { |column| column.name.to_s == field.to_s }&.type == :string
83
- end
84
-
85
- return noop if fields.empty?
96
+ foreign_key = relation(key).try(:foreign_key)
97
+ return noop unless foreign_key.present?
86
98
 
87
- relation_conditions(fields, value, relation_model)
88
- end
89
-
90
- def relation_conditions(fields, value, relation_model)
91
- fields[1..].reduce(relation_model.arel_table[fields.first].matches("#{value}%")) do |condition, field|
92
- condition.or(relation_model.arel_table[field].matches("#{value}%"))
93
- end
99
+ model.arel_table[foreign_key].eq(value)
94
100
  end
95
101
 
96
102
  def searchable_relation_fields(key)
@@ -0,0 +1,20 @@
1
+ module ActiveElement
2
+ class FieldOptions
3
+ attr_accessor :type, :options
4
+ attr_reader :field
5
+
6
+ def self.from_state(field, state, record)
7
+ block = state.field_options[field]
8
+ return nil if block.blank?
9
+
10
+ field_options = new(field)
11
+ block.call(field_options, record)
12
+ field_options
13
+ end
14
+
15
+ def initialize(field)
16
+ @field = field
17
+ @options = {}
18
+ end
19
+ end
20
+ end
@@ -1,5 +1,5 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  module ActiveElement
4
- VERSION = '0.0.13'
4
+ VERSION = '0.0.15'
5
5
  end
@@ -14,6 +14,7 @@ require_relative 'active_element/permissions_report'
14
14
  require_relative 'active_element/controller_interface'
15
15
  require_relative 'active_element/controller_state'
16
16
  require_relative 'active_element/controller_action'
17
+ require_relative 'active_element/field_options'
17
18
  require_relative 'active_element/default_controller'
18
19
  require_relative 'active_element/pre_render_processors'
19
20
  require_relative 'active_element/rails_component'
@@ -105,7 +105,7 @@ The results will be rendered in a horizontal table with one row for each item, i
105
105
  # app/controllers/restaurants_controller.rb
106
106
 
107
107
  class RestaurantsController < ApplicationController
108
- active_element.searchable_fields :name, :restaurateur, :created_at
108
+ active_element.viewable_fields :name, :restaurateur, :created_at
109
109
  end
110
110
  ```
111
111
 
@@ -119,6 +119,15 @@ Note that each field type can be overridden and configured by defining `config/f
119
119
 
120
120
  By default, `json` and `jsonb` fields use the [JSON Field](form-fields/json.html) type, allowing you to edit complex _JSON_ data structures via user-friendly _HTML_ forms. A [schema file](form-fields/json/schema.html) **must** be defined for these fields. See the `json_field` documentation for more details.
121
121
 
122
+
123
+ ```ruby
124
+ # app/controllers/restaurants_controller.rb
125
+
126
+ class RestaurantsController < ApplicationController
127
+ active_element.editable_fields :name, :restaurateur
128
+ end
129
+ ```
130
+
122
131
  ## Deletable
123
132
 
124
133
  The `active_element.deletable` declaration does not receive any arguments but specifies that a record can be deleted by a user.
metadata CHANGED
@@ -1,14 +1,14 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: active_element
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.0.13
4
+ version: 0.0.15
5
5
  platform: ruby
6
6
  authors:
7
7
  - Bob Farrell
8
8
  autorequire:
9
9
  bindir: exe
10
10
  cert_chain: []
11
- date: 2023-06-22 00:00:00.000000000 Z
11
+ date: 2023-12-14 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: bootstrap
@@ -131,6 +131,7 @@ files:
131
131
  - app/assets/stylesheets/active_element/application.scss
132
132
  - app/controllers/active_element/application_controller.rb
133
133
  - app/controllers/concerns/active_element/default_controller_actions.rb
134
+ - app/views/active_element/_title.html.erb
134
135
  - app/views/active_element/_user.html.erb
135
136
  - app/views/active_element/components/_horizontal_tabs.html.erb
136
137
  - app/views/active_element/components/_vertical_tabs.html.erb
@@ -140,6 +141,7 @@ files:
140
141
  - app/views/active_element/components/form.html.erb
141
142
  - app/views/active_element/components/form/_check_box.html.erb
142
143
  - app/views/active_element/components/form/_check_boxes.html.erb
144
+ - app/views/active_element/components/form/_date_range_field.html.erb
143
145
  - app/views/active_element/components/form/_datetime_range_field.html.erb
144
146
  - app/views/active_element/components/form/_field.html.erb
145
147
  - app/views/active_element/components/form/_generic_field.html.erb
@@ -307,6 +309,7 @@ files:
307
309
  - lib/active_element/components/util.rb
308
310
  - lib/active_element/components/util/association_mapping.rb
309
311
  - lib/active_element/components/util/decorator.rb
312
+ - lib/active_element/components/util/default_display_value.rb
310
313
  - lib/active_element/components/util/display_value_mapping.rb
311
314
  - lib/active_element/components/util/field_mapping.rb
312
315
  - lib/active_element/components/util/form_field_mapping.rb
@@ -325,6 +328,7 @@ files:
325
328
  - lib/active_element/default_controller/params.rb
326
329
  - lib/active_element/default_controller/search.rb
327
330
  - lib/active_element/engine.rb
331
+ - lib/active_element/field_options.rb
328
332
  - lib/active_element/json_field_schema.rb
329
333
  - lib/active_element/permissions_check.rb
330
334
  - lib/active_element/permissions_report.rb