typed_eav 0.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 (86) hide show
  1. checksums.yaml +7 -0
  2. data/CHANGELOG.md +12 -0
  3. data/MIT-LICENSE +21 -0
  4. data/README.md +494 -0
  5. data/Rakefile +13 -0
  6. data/app/models/typed_eav/application_record.rb +7 -0
  7. data/app/models/typed_eav/field/base.rb +234 -0
  8. data/app/models/typed_eav/field/boolean.rb +22 -0
  9. data/app/models/typed_eav/field/color.rb +16 -0
  10. data/app/models/typed_eav/field/date.rb +24 -0
  11. data/app/models/typed_eav/field/date_array.rb +34 -0
  12. data/app/models/typed_eav/field/date_time.rb +29 -0
  13. data/app/models/typed_eav/field/decimal.rb +30 -0
  14. data/app/models/typed_eav/field/decimal_array.rb +31 -0
  15. data/app/models/typed_eav/field/email.rb +40 -0
  16. data/app/models/typed_eav/field/integer.rb +30 -0
  17. data/app/models/typed_eav/field/integer_array.rb +68 -0
  18. data/app/models/typed_eav/field/json.rb +26 -0
  19. data/app/models/typed_eav/field/long_text.rb +19 -0
  20. data/app/models/typed_eav/field/multi_select.rb +41 -0
  21. data/app/models/typed_eav/field/select.rb +28 -0
  22. data/app/models/typed_eav/field/text.rb +41 -0
  23. data/app/models/typed_eav/field/text_array.rb +36 -0
  24. data/app/models/typed_eav/field/url.rb +40 -0
  25. data/app/models/typed_eav/option.rb +24 -0
  26. data/app/models/typed_eav/section.rb +25 -0
  27. data/app/models/typed_eav/value.rb +149 -0
  28. data/db/migrate/20260330000000_create_typed_eav_tables.rb +132 -0
  29. data/lib/generators/typed_eav/install/install_generator.rb +28 -0
  30. data/lib/generators/typed_eav/scaffold/scaffold_generator.rb +106 -0
  31. data/lib/generators/typed_eav/scaffold/templates/config/initializers/typed_eav.rb +45 -0
  32. data/lib/generators/typed_eav/scaffold/templates/controllers/concerns/typed_eav_controller_concern.rb +24 -0
  33. data/lib/generators/typed_eav/scaffold/templates/controllers/typed_eav_controller.rb +231 -0
  34. data/lib/generators/typed_eav/scaffold/templates/helpers/typed_eav_helper.rb +150 -0
  35. data/lib/generators/typed_eav/scaffold/templates/javascript/controllers/array_field_controller.js +64 -0
  36. data/lib/generators/typed_eav/scaffold/templates/javascript/controllers/typed_eav_form_controller.js +32 -0
  37. data/lib/generators/typed_eav/scaffold/templates/views/shared/_array_field.html.erb +23 -0
  38. data/lib/generators/typed_eav/scaffold/templates/views/typed_eav/edit.html.erb +47 -0
  39. data/lib/generators/typed_eav/scaffold/templates/views/typed_eav/finders/_form.html.erb +80 -0
  40. data/lib/generators/typed_eav/scaffold/templates/views/typed_eav/forms/_boolean.html.erb +12 -0
  41. data/lib/generators/typed_eav/scaffold/templates/views/typed_eav/forms/_color.html.erb +11 -0
  42. data/lib/generators/typed_eav/scaffold/templates/views/typed_eav/forms/_common_fields.html.erb +57 -0
  43. data/lib/generators/typed_eav/scaffold/templates/views/typed_eav/forms/_date.html.erb +21 -0
  44. data/lib/generators/typed_eav/scaffold/templates/views/typed_eav/forms/_date_array.html.erb +16 -0
  45. data/lib/generators/typed_eav/scaffold/templates/views/typed_eav/forms/_date_time.html.erb +21 -0
  46. data/lib/generators/typed_eav/scaffold/templates/views/typed_eav/forms/_decimal.html.erb +21 -0
  47. data/lib/generators/typed_eav/scaffold/templates/views/typed_eav/forms/_decimal_array.html.erb +16 -0
  48. data/lib/generators/typed_eav/scaffold/templates/views/typed_eav/forms/_email.html.erb +11 -0
  49. data/lib/generators/typed_eav/scaffold/templates/views/typed_eav/forms/_integer.html.erb +21 -0
  50. data/lib/generators/typed_eav/scaffold/templates/views/typed_eav/forms/_integer_array.html.erb +16 -0
  51. data/lib/generators/typed_eav/scaffold/templates/views/typed_eav/forms/_json.html.erb +11 -0
  52. data/lib/generators/typed_eav/scaffold/templates/views/typed_eav/forms/_long_text.html.erb +21 -0
  53. data/lib/generators/typed_eav/scaffold/templates/views/typed_eav/forms/_multi_select.html.erb +6 -0
  54. data/lib/generators/typed_eav/scaffold/templates/views/typed_eav/forms/_select.html.erb +14 -0
  55. data/lib/generators/typed_eav/scaffold/templates/views/typed_eav/forms/_text.html.erb +26 -0
  56. data/lib/generators/typed_eav/scaffold/templates/views/typed_eav/forms/_text_array.html.erb +16 -0
  57. data/lib/generators/typed_eav/scaffold/templates/views/typed_eav/forms/_url.html.erb +11 -0
  58. data/lib/generators/typed_eav/scaffold/templates/views/typed_eav/index.html.erb +42 -0
  59. data/lib/generators/typed_eav/scaffold/templates/views/typed_eav/new.html.erb +7 -0
  60. data/lib/generators/typed_eav/scaffold/templates/views/typed_eav/show.html.erb +44 -0
  61. data/lib/generators/typed_eav/scaffold/templates/views/typed_eav/values/inputs/_boolean.html.erb +10 -0
  62. data/lib/generators/typed_eav/scaffold/templates/views/typed_eav/values/inputs/_color.html.erb +4 -0
  63. data/lib/generators/typed_eav/scaffold/templates/views/typed_eav/values/inputs/_date.html.erb +6 -0
  64. data/lib/generators/typed_eav/scaffold/templates/views/typed_eav/values/inputs/_date_array.html.erb +9 -0
  65. data/lib/generators/typed_eav/scaffold/templates/views/typed_eav/values/inputs/_date_time.html.erb +5 -0
  66. data/lib/generators/typed_eav/scaffold/templates/views/typed_eav/values/inputs/_decimal.html.erb +6 -0
  67. data/lib/generators/typed_eav/scaffold/templates/views/typed_eav/values/inputs/_decimal_array.html.erb +9 -0
  68. data/lib/generators/typed_eav/scaffold/templates/views/typed_eav/values/inputs/_email.html.erb +5 -0
  69. data/lib/generators/typed_eav/scaffold/templates/views/typed_eav/values/inputs/_integer.html.erb +6 -0
  70. data/lib/generators/typed_eav/scaffold/templates/views/typed_eav/values/inputs/_integer_array.html.erb +9 -0
  71. data/lib/generators/typed_eav/scaffold/templates/views/typed_eav/values/inputs/_json.html.erb +7 -0
  72. data/lib/generators/typed_eav/scaffold/templates/views/typed_eav/values/inputs/_long_text.html.erb +7 -0
  73. data/lib/generators/typed_eav/scaffold/templates/views/typed_eav/values/inputs/_multi_select.html.erb +7 -0
  74. data/lib/generators/typed_eav/scaffold/templates/views/typed_eav/values/inputs/_select.html.erb +7 -0
  75. data/lib/generators/typed_eav/scaffold/templates/views/typed_eav/values/inputs/_text.html.erb +6 -0
  76. data/lib/generators/typed_eav/scaffold/templates/views/typed_eav/values/inputs/_text_array.html.erb +9 -0
  77. data/lib/generators/typed_eav/scaffold/templates/views/typed_eav/values/inputs/_url.html.erb +5 -0
  78. data/lib/typed_eav/column_mapping.rb +64 -0
  79. data/lib/typed_eav/config.rb +91 -0
  80. data/lib/typed_eav/engine.rb +20 -0
  81. data/lib/typed_eav/has_typed_eav.rb +484 -0
  82. data/lib/typed_eav/query_builder.rb +133 -0
  83. data/lib/typed_eav/registry.rb +52 -0
  84. data/lib/typed_eav/version.rb +5 -0
  85. data/lib/typed_eav.rb +86 -0
  86. metadata +146 -0
@@ -0,0 +1,133 @@
1
+ # frozen_string_literal: true
2
+
3
+ module TypedEAV
4
+ # Replaces the per-type Finder class hierarchy from active_fields.
5
+ #
6
+ # Because values live in native typed columns, ActiveRecord already knows
7
+ # the column types from the schema. Arel predicates (eq, gt, lt, matches, etc.)
8
+ # automatically go through the column's ActiveRecord::Type for casting.
9
+ #
10
+ # This means:
11
+ # where(integer_value: "42") -> Rails casts "42" to 42 automatically
12
+ # arel[:date_value].gt(value) -> Rails casts string dates to Date objects
13
+ #
14
+ # No manual CAST() calls. No per-type caster classes for queries.
15
+ # One module handles all field types.
16
+ #
17
+ # Usage:
18
+ # QueryBuilder.filter(field, :gt, 42)
19
+ # # => ActiveRecord::Relation scoped to matching values
20
+ #
21
+ # QueryBuilder.filter(field, :contains, "hello")
22
+ # # => ILIKE query against the field's string_value column
23
+ #
24
+ class QueryBuilder
25
+ class << self
26
+ # Returns an ActiveRecord::Relation of TypedEAV::Value records
27
+ # matching the given field, operator, and comparison value.
28
+ #
29
+ # The relation is suitable for subquery use:
30
+ # Model.where(id: QueryBuilder.filter(field, :gt, 5).select(:entity_id))
31
+ # rubocop:disable Metrics/AbcSize, Metrics/CyclomaticComplexity, Metrics/MethodLength -- one operator-dispatch case statement; flattening keeps the supported-operators list scannable in one place.
32
+ def filter(field, operator, value)
33
+ col = field.class.value_column
34
+ operator = operator.to_sym
35
+
36
+ # Validate operator is supported by this field type
37
+ supported = field.class.supported_operators
38
+ unless supported.include?(operator)
39
+ raise ArgumentError,
40
+ "Operator :#{operator} is not supported for #{field.class.name}. " \
41
+ "Supported operators: #{supported.map { |o| ":#{o}" }.join(", ")}"
42
+ end
43
+
44
+ arel_col = values_table[col]
45
+
46
+ base = value_scope(field)
47
+
48
+ case operator
49
+ when :eq
50
+ eq_predicate(base, arel_col, col, value)
51
+ when :not_eq
52
+ not_eq_predicate(base, arel_col, col, value)
53
+ when :gt
54
+ base.where(arel_col.gt(value))
55
+ when :gteq
56
+ base.where(arel_col.gteq(value))
57
+ when :lt
58
+ base.where(arel_col.lt(value))
59
+ when :lteq
60
+ base.where(arel_col.lteq(value))
61
+ when :between
62
+ unless value.respond_to?(:first) && value.respond_to?(:last)
63
+ raise ArgumentError,
64
+ ":between expects a Range or two-element Array"
65
+ end
66
+
67
+ base.where(arel_col.between(value.first..value.last))
68
+ when :contains
69
+ base.where(arel_col.matches("%#{sanitize_like(value)}%"))
70
+ when :not_contains
71
+ base.where(arel_col.does_not_match("%#{sanitize_like(value)}%"))
72
+ when :starts_with
73
+ base.where(arel_col.matches("#{sanitize_like(value)}%"))
74
+ when :ends_with
75
+ base.where(arel_col.matches("%#{sanitize_like(value)}"))
76
+ when :is_null
77
+ base.where(col => nil)
78
+ when :is_not_null
79
+ base.where.not(col => nil)
80
+ when :any_eq
81
+ # For json_value arrays: contains the given element
82
+ base.where("#{col} @> ?", [value].to_json)
83
+ when :all_eq
84
+ # For json_value arrays: contains all given elements
85
+ base.where("#{col} @> ?", Array(value).to_json)
86
+ else
87
+ raise ArgumentError, "Unhandled operator: #{operator}"
88
+ end
89
+ end
90
+
91
+ # rubocop:enable Metrics/AbcSize, Metrics/CyclomaticComplexity, Metrics/MethodLength
92
+
93
+ # Convenience: returns entity IDs matching the filter.
94
+ # Useful for subqueries: Model.where(id: QueryBuilder.entity_ids(field, :gt, 5))
95
+ def entity_ids(field, operator, value)
96
+ filter(field, operator, value).distinct.select(:entity_id)
97
+ end
98
+
99
+ private
100
+
101
+ def values_table
102
+ TypedEAV::Value.arel_table
103
+ end
104
+
105
+ # Base scope: values for this specific field
106
+ def value_scope(field)
107
+ TypedEAV::Value.where(field: field)
108
+ end
109
+
110
+ # NULL-safe equality: AR `where(col => nil)` already emits IS NULL, and
111
+ # `where(col => true/false)` already emits IS TRUE/FALSE on PG, so the
112
+ # same `base.where(col => value)` covers booleans and other types.
113
+ def eq_predicate(base, _arel_col, col, value)
114
+ base.where(col => value)
115
+ end
116
+
117
+ # NULL-safe inequality: includes NULL rows (they're "not equal" to any value)
118
+ def not_eq_predicate(base, arel_col, col, value)
119
+ if value.nil?
120
+ base.where.not(col => nil)
121
+ else
122
+ # NOT col = value OR col IS NULL
123
+ # Without the OR, NULLs are excluded (SQL tri-valued logic)
124
+ base.where(arel_col.not_eq(value).or(arel_col.eq(nil)))
125
+ end
126
+ end
127
+
128
+ def sanitize_like(value)
129
+ value.to_s.gsub(/[%_\\]/) { |m| "\\#{m}" }
130
+ end
131
+ end
132
+ end
133
+ end
@@ -0,0 +1,52 @@
1
+ # frozen_string_literal: true
2
+
3
+ require "active_support/configurable"
4
+
5
+ module TypedEAV
6
+ # Registry of entity types (host ActiveRecord models) that have opted
7
+ # into typed fields via `has_typed_eav`. Tracks optional field-type
8
+ # restrictions per entity.
9
+ #
10
+ # Populated automatically when a host model calls `has_typed_eav`;
11
+ # read by Field::Base#validate_type_allowed_for_entity to enforce
12
+ # restrictions on field creation.
13
+ class Registry
14
+ include ActiveSupport::Configurable
15
+
16
+ config_accessor(:entities) { {} }
17
+
18
+ class << self
19
+ # Register an entity type with optional type restrictions.
20
+ def register(entity_type, types: nil)
21
+ entities[entity_type] = { types: types }
22
+ end
23
+
24
+ # All registered entity type names.
25
+ def entity_types
26
+ entities.keys
27
+ end
28
+
29
+ # Field-type restrictions for a given entity, or nil if unrestricted.
30
+ def allowed_types_for(entity_type)
31
+ entry = entities[entity_type]
32
+ return nil unless entry
33
+
34
+ entry[:types]
35
+ end
36
+
37
+ # Whether a field type class is allowed for an entity.
38
+ def type_allowed?(entity_type, field_type_class)
39
+ allowed = allowed_types_for(entity_type)
40
+ return true if allowed.nil?
41
+
42
+ type_name = field_type_class.name.demodulize.underscore.to_sym
43
+ allowed.include?(type_name)
44
+ end
45
+
46
+ # Clear all registrations (test isolation).
47
+ def reset!
48
+ entities.clear
49
+ end
50
+ end
51
+ end
52
+ end
@@ -0,0 +1,5 @@
1
+ # frozen_string_literal: true
2
+
3
+ module TypedEAV
4
+ VERSION = "0.1.0"
5
+ end
data/lib/typed_eav.rb ADDED
@@ -0,0 +1,86 @@
1
+ # frozen_string_literal: true
2
+
3
+ require "active_support/inflector"
4
+ ActiveSupport::Inflector.inflections(:en) do |inflect|
5
+ inflect.acronym "EAV"
6
+ end
7
+
8
+ require_relative "typed_eav/version"
9
+ require_relative "typed_eav/engine"
10
+
11
+ module TypedEAV
12
+ extend ActiveSupport::Autoload
13
+
14
+ autoload :Config
15
+ autoload :Registry
16
+ autoload :HasTypedEAV
17
+ autoload :QueryBuilder
18
+
19
+ # Raised when a model declared `has_typed_eav scope_method: ...` but no
20
+ # scope can be resolved at query time and `config.require_scope` is truthy.
21
+ class ScopeRequired < StandardError; end
22
+
23
+ THREAD_SCOPE_STACK = :typed_eav_scope_stack
24
+ THREAD_UNSCOPED = :typed_eav_unscoped
25
+ private_constant :THREAD_SCOPE_STACK, :THREAD_UNSCOPED
26
+
27
+ class << self
28
+ def config
29
+ yield Config if block_given?
30
+ Config
31
+ end
32
+
33
+ alias configure config
34
+
35
+ def registry = Registry
36
+
37
+ # Current ambient scope value. Resolution order:
38
+ # 1. Inside `unscoped { }` → nil (hard bypass)
39
+ # 2. Innermost `with_scope(v)` → v
40
+ # 3. Configured `scope_resolver` callable
41
+ # 4. nil
42
+ #
43
+ # Returns a string (via normalize), or nil when nothing resolves.
44
+ def current_scope
45
+ return nil if Thread.current[THREAD_UNSCOPED]
46
+
47
+ stack = Thread.current[THREAD_SCOPE_STACK]
48
+ return normalize_scope(stack.last) if stack.present?
49
+
50
+ normalize_scope(Config.scope_resolver&.call)
51
+ end
52
+
53
+ # Run the block with `value` as the ambient scope, restoring the prior
54
+ # stack on exit (exception-safe). Nests cleanly.
55
+ def with_scope(value)
56
+ stack = (Thread.current[THREAD_SCOPE_STACK] ||= [])
57
+ stack.push(value)
58
+ yield
59
+ ensure
60
+ stack&.pop
61
+ end
62
+
63
+ # Run the block with scope enforcement disabled. Queries return results
64
+ # across all scopes. Use for admin tools, migrations, and tests.
65
+ def unscoped
66
+ prev = Thread.current[THREAD_UNSCOPED]
67
+ Thread.current[THREAD_UNSCOPED] = true
68
+ yield
69
+ ensure
70
+ Thread.current[THREAD_UNSCOPED] = prev
71
+ end
72
+
73
+ # True when inside an `unscoped { }` block.
74
+ def unscoped?
75
+ !!Thread.current[THREAD_UNSCOPED]
76
+ end
77
+
78
+ # Coerce resolver/with_scope/explicit-kwarg inputs into the string shape
79
+ # stored in the `scope` column. Accepts raw scalars or AR records.
80
+ def normalize_scope(value)
81
+ return nil if value.nil?
82
+
83
+ value.respond_to?(:id) ? value.id.to_s : value.to_s
84
+ end
85
+ end
86
+ end
metadata ADDED
@@ -0,0 +1,146 @@
1
+ --- !ruby/object:Gem::Specification
2
+ name: typed_eav
3
+ version: !ruby/object:Gem::Version
4
+ version: 0.1.0
5
+ platform: ruby
6
+ authors:
7
+ - Darrin Chuk
8
+ bindir: bin
9
+ cert_chain: []
10
+ date: 1980-01-02 00:00:00.000000000 Z
11
+ dependencies:
12
+ - !ruby/object:Gem::Dependency
13
+ name: rails
14
+ requirement: !ruby/object:Gem::Requirement
15
+ requirements:
16
+ - - ">="
17
+ - !ruby/object:Gem::Version
18
+ version: '7.1'
19
+ type: :runtime
20
+ prerelease: false
21
+ version_requirements: !ruby/object:Gem::Requirement
22
+ requirements:
23
+ - - ">="
24
+ - !ruby/object:Gem::Version
25
+ version: '7.1'
26
+ description: Add dynamic custom fields to ActiveRecord models at runtime using native
27
+ database typed columns instead of jsonb blobs. Hybrid EAV with real indexes, real
28
+ types, real query performance.
29
+ email:
30
+ - me@dchuk.com
31
+ executables: []
32
+ extensions: []
33
+ extra_rdoc_files: []
34
+ files:
35
+ - CHANGELOG.md
36
+ - MIT-LICENSE
37
+ - README.md
38
+ - Rakefile
39
+ - app/models/typed_eav/application_record.rb
40
+ - app/models/typed_eav/field/base.rb
41
+ - app/models/typed_eav/field/boolean.rb
42
+ - app/models/typed_eav/field/color.rb
43
+ - app/models/typed_eav/field/date.rb
44
+ - app/models/typed_eav/field/date_array.rb
45
+ - app/models/typed_eav/field/date_time.rb
46
+ - app/models/typed_eav/field/decimal.rb
47
+ - app/models/typed_eav/field/decimal_array.rb
48
+ - app/models/typed_eav/field/email.rb
49
+ - app/models/typed_eav/field/integer.rb
50
+ - app/models/typed_eav/field/integer_array.rb
51
+ - app/models/typed_eav/field/json.rb
52
+ - app/models/typed_eav/field/long_text.rb
53
+ - app/models/typed_eav/field/multi_select.rb
54
+ - app/models/typed_eav/field/select.rb
55
+ - app/models/typed_eav/field/text.rb
56
+ - app/models/typed_eav/field/text_array.rb
57
+ - app/models/typed_eav/field/url.rb
58
+ - app/models/typed_eav/option.rb
59
+ - app/models/typed_eav/section.rb
60
+ - app/models/typed_eav/value.rb
61
+ - db/migrate/20260330000000_create_typed_eav_tables.rb
62
+ - lib/generators/typed_eav/install/install_generator.rb
63
+ - lib/generators/typed_eav/scaffold/scaffold_generator.rb
64
+ - lib/generators/typed_eav/scaffold/templates/config/initializers/typed_eav.rb
65
+ - lib/generators/typed_eav/scaffold/templates/controllers/concerns/typed_eav_controller_concern.rb
66
+ - lib/generators/typed_eav/scaffold/templates/controllers/typed_eav_controller.rb
67
+ - lib/generators/typed_eav/scaffold/templates/helpers/typed_eav_helper.rb
68
+ - lib/generators/typed_eav/scaffold/templates/javascript/controllers/array_field_controller.js
69
+ - lib/generators/typed_eav/scaffold/templates/javascript/controllers/typed_eav_form_controller.js
70
+ - lib/generators/typed_eav/scaffold/templates/views/shared/_array_field.html.erb
71
+ - lib/generators/typed_eav/scaffold/templates/views/typed_eav/edit.html.erb
72
+ - lib/generators/typed_eav/scaffold/templates/views/typed_eav/finders/_form.html.erb
73
+ - lib/generators/typed_eav/scaffold/templates/views/typed_eav/forms/_boolean.html.erb
74
+ - lib/generators/typed_eav/scaffold/templates/views/typed_eav/forms/_color.html.erb
75
+ - lib/generators/typed_eav/scaffold/templates/views/typed_eav/forms/_common_fields.html.erb
76
+ - lib/generators/typed_eav/scaffold/templates/views/typed_eav/forms/_date.html.erb
77
+ - lib/generators/typed_eav/scaffold/templates/views/typed_eav/forms/_date_array.html.erb
78
+ - lib/generators/typed_eav/scaffold/templates/views/typed_eav/forms/_date_time.html.erb
79
+ - lib/generators/typed_eav/scaffold/templates/views/typed_eav/forms/_decimal.html.erb
80
+ - lib/generators/typed_eav/scaffold/templates/views/typed_eav/forms/_decimal_array.html.erb
81
+ - lib/generators/typed_eav/scaffold/templates/views/typed_eav/forms/_email.html.erb
82
+ - lib/generators/typed_eav/scaffold/templates/views/typed_eav/forms/_integer.html.erb
83
+ - lib/generators/typed_eav/scaffold/templates/views/typed_eav/forms/_integer_array.html.erb
84
+ - lib/generators/typed_eav/scaffold/templates/views/typed_eav/forms/_json.html.erb
85
+ - lib/generators/typed_eav/scaffold/templates/views/typed_eav/forms/_long_text.html.erb
86
+ - lib/generators/typed_eav/scaffold/templates/views/typed_eav/forms/_multi_select.html.erb
87
+ - lib/generators/typed_eav/scaffold/templates/views/typed_eav/forms/_select.html.erb
88
+ - lib/generators/typed_eav/scaffold/templates/views/typed_eav/forms/_text.html.erb
89
+ - lib/generators/typed_eav/scaffold/templates/views/typed_eav/forms/_text_array.html.erb
90
+ - lib/generators/typed_eav/scaffold/templates/views/typed_eav/forms/_url.html.erb
91
+ - lib/generators/typed_eav/scaffold/templates/views/typed_eav/index.html.erb
92
+ - lib/generators/typed_eav/scaffold/templates/views/typed_eav/new.html.erb
93
+ - lib/generators/typed_eav/scaffold/templates/views/typed_eav/show.html.erb
94
+ - lib/generators/typed_eav/scaffold/templates/views/typed_eav/values/inputs/_boolean.html.erb
95
+ - lib/generators/typed_eav/scaffold/templates/views/typed_eav/values/inputs/_color.html.erb
96
+ - lib/generators/typed_eav/scaffold/templates/views/typed_eav/values/inputs/_date.html.erb
97
+ - lib/generators/typed_eav/scaffold/templates/views/typed_eav/values/inputs/_date_array.html.erb
98
+ - lib/generators/typed_eav/scaffold/templates/views/typed_eav/values/inputs/_date_time.html.erb
99
+ - lib/generators/typed_eav/scaffold/templates/views/typed_eav/values/inputs/_decimal.html.erb
100
+ - lib/generators/typed_eav/scaffold/templates/views/typed_eav/values/inputs/_decimal_array.html.erb
101
+ - lib/generators/typed_eav/scaffold/templates/views/typed_eav/values/inputs/_email.html.erb
102
+ - lib/generators/typed_eav/scaffold/templates/views/typed_eav/values/inputs/_integer.html.erb
103
+ - lib/generators/typed_eav/scaffold/templates/views/typed_eav/values/inputs/_integer_array.html.erb
104
+ - lib/generators/typed_eav/scaffold/templates/views/typed_eav/values/inputs/_json.html.erb
105
+ - lib/generators/typed_eav/scaffold/templates/views/typed_eav/values/inputs/_long_text.html.erb
106
+ - lib/generators/typed_eav/scaffold/templates/views/typed_eav/values/inputs/_multi_select.html.erb
107
+ - lib/generators/typed_eav/scaffold/templates/views/typed_eav/values/inputs/_select.html.erb
108
+ - lib/generators/typed_eav/scaffold/templates/views/typed_eav/values/inputs/_text.html.erb
109
+ - lib/generators/typed_eav/scaffold/templates/views/typed_eav/values/inputs/_text_array.html.erb
110
+ - lib/generators/typed_eav/scaffold/templates/views/typed_eav/values/inputs/_url.html.erb
111
+ - lib/typed_eav.rb
112
+ - lib/typed_eav/column_mapping.rb
113
+ - lib/typed_eav/config.rb
114
+ - lib/typed_eav/engine.rb
115
+ - lib/typed_eav/has_typed_eav.rb
116
+ - lib/typed_eav/query_builder.rb
117
+ - lib/typed_eav/registry.rb
118
+ - lib/typed_eav/version.rb
119
+ homepage: https://github.com/dchuk/typed_eav
120
+ licenses:
121
+ - MIT
122
+ metadata:
123
+ homepage_uri: https://github.com/dchuk/typed_eav
124
+ source_code_uri: https://github.com/dchuk/typed_eav
125
+ changelog_uri: https://github.com/dchuk/typed_eav/blob/main/CHANGELOG.md
126
+ bug_tracker_uri: https://github.com/dchuk/typed_eav/issues
127
+ allowed_push_host: https://rubygems.org
128
+ rubygems_mfa_required: 'true'
129
+ rdoc_options: []
130
+ require_paths:
131
+ - lib
132
+ required_ruby_version: !ruby/object:Gem::Requirement
133
+ requirements:
134
+ - - ">="
135
+ - !ruby/object:Gem::Version
136
+ version: '3.1'
137
+ required_rubygems_version: !ruby/object:Gem::Requirement
138
+ requirements:
139
+ - - ">="
140
+ - !ruby/object:Gem::Version
141
+ version: '0'
142
+ requirements: []
143
+ rubygems_version: 3.7.1
144
+ specification_version: 4
145
+ summary: Typed custom fields for ActiveRecord models
146
+ test_files: []