forest_admin_datasource_mongoid 1.0.1

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 (28) hide show
  1. checksums.yaml +7 -0
  2. data/.rspec +3 -0
  3. data/LICENSE +674 -0
  4. data/Rakefile +12 -0
  5. data/forest_admin_datasource_mongoid.gemspec +38 -0
  6. data/lib/forest_admin_datasource_mongoid/collection.rb +135 -0
  7. data/lib/forest_admin_datasource_mongoid/datasource.rb +125 -0
  8. data/lib/forest_admin_datasource_mongoid/options_parser.rb +79 -0
  9. data/lib/forest_admin_datasource_mongoid/parser/column.rb +86 -0
  10. data/lib/forest_admin_datasource_mongoid/parser/relation.rb +18 -0
  11. data/lib/forest_admin_datasource_mongoid/parser/validation.rb +87 -0
  12. data/lib/forest_admin_datasource_mongoid/utils/add_null_values.rb +56 -0
  13. data/lib/forest_admin_datasource_mongoid/utils/helpers.rb +151 -0
  14. data/lib/forest_admin_datasource_mongoid/utils/mongoid_serializer.rb +38 -0
  15. data/lib/forest_admin_datasource_mongoid/utils/pipeline/condition_generator.rb +30 -0
  16. data/lib/forest_admin_datasource_mongoid/utils/pipeline/filter_generator.rb +218 -0
  17. data/lib/forest_admin_datasource_mongoid/utils/pipeline/group_generator.rb +86 -0
  18. data/lib/forest_admin_datasource_mongoid/utils/pipeline/lookup_generator.rb +97 -0
  19. data/lib/forest_admin_datasource_mongoid/utils/pipeline/projection_generator.rb +20 -0
  20. data/lib/forest_admin_datasource_mongoid/utils/pipeline/reparent_generator.rb +97 -0
  21. data/lib/forest_admin_datasource_mongoid/utils/pipeline/virtual_field_generator.rb +78 -0
  22. data/lib/forest_admin_datasource_mongoid/utils/schema/fields_generator.rb +87 -0
  23. data/lib/forest_admin_datasource_mongoid/utils/schema/mongoid_schema.rb +196 -0
  24. data/lib/forest_admin_datasource_mongoid/utils/schema/relation_generator.rb +51 -0
  25. data/lib/forest_admin_datasource_mongoid/utils/version_manager.rb +13 -0
  26. data/lib/forest_admin_datasource_mongoid/version.rb +3 -0
  27. data/lib/forest_admin_datasource_mongoid.rb +11 -0
  28. metadata +119 -0
@@ -0,0 +1,97 @@
1
+ module ForestAdminDatasourceMongoid
2
+ module Utils
3
+ module Pipeline
4
+ # Generate pipeline to query submodels.
5
+ # The operations make rotations in the documents so that the root is changed to the submodel
6
+ # without loosing the parent (which may be needed later on).
7
+ class ReparentGenerator
8
+ include Utils::Schema
9
+
10
+ def self.reparent(model, stack)
11
+ schema = MongoidSchema.from_model(model)
12
+
13
+ stack.flat_map.with_index do |step, index|
14
+ # If this is the first step in the stack and there are no fields to flatten, return an empty list
15
+ next [] if index.zero? && step[:as_fields].empty?
16
+ # If this is the first step in the stack, only flatten the provided fields without reparenting
17
+ next unflatten(step[:as_fields]) if index.zero?
18
+
19
+ local_schema = schema.get_sub_schema(step[:prefix])
20
+ relative_prefix = if stack[index - 1][:prefix].nil?
21
+ step[:prefix]
22
+ else
23
+ step[:prefix][(stack[index - 1][:prefix].length + 1)..]
24
+ end
25
+
26
+ result = if local_schema.is_array
27
+ reparent_array(relative_prefix, local_schema.is_leaf)
28
+ else
29
+ reparent_object(relative_prefix, local_schema.is_leaf)
30
+ end
31
+
32
+ [*result, *unflatten(step[:as_fields])]
33
+ end
34
+ end
35
+
36
+ def self.reparent_array(prefix, in_doc)
37
+ [
38
+ { '$unwind' => { 'path' => "$#{prefix}", 'includeArrayIndex' => 'index' } },
39
+ {
40
+ '$replaceRoot' => {
41
+ 'newRoot' => {
42
+ '$mergeObjects' => [
43
+ in_doc ? { 'content' => "$#{prefix}" } : "$#{prefix}",
44
+ ConditionGenerator.tag_record_if_not_exist(
45
+ prefix,
46
+ {
47
+ '_id' => { '$concat' => [{ '$toString' => '$_id' }, ".#{prefix}.",
48
+ { '$toString' => '$index' }] },
49
+ 'parent_id' => '$_id',
50
+ 'parent' => '$$ROOT'
51
+ }
52
+ )
53
+ ]
54
+ }
55
+ }
56
+ }
57
+ ]
58
+ end
59
+
60
+ def self.reparent_object(prefix, in_doc)
61
+ [
62
+ {
63
+ '$replaceRoot' => {
64
+ 'newRoot' => {
65
+ '$mergeObjects' => [
66
+ in_doc ? { 'content' => "$#{prefix}" } : "$#{prefix}",
67
+ ConditionGenerator.tag_record_if_not_exist(
68
+ prefix,
69
+ {
70
+ '_id' => { '$concat' => [{ '$toString' => '$_id' }, ".#{prefix}"] },
71
+ 'parent_id' => '$_id',
72
+ 'parent' => '$$ROOT'
73
+ }
74
+ )
75
+ ]
76
+ }
77
+ }
78
+ }
79
+ ]
80
+ end
81
+
82
+ def self.unflatten(as_fields)
83
+ return [] if as_fields.empty?
84
+
85
+ chunk_size = 30
86
+ add_fields = as_fields.map { |f| [f.gsub('.', '@@@'), "$#{f}"] }
87
+
88
+ # MongoDB (DocumentDB) enforces a limit of 30 fields per $addFields stage.
89
+ # We split the list into chunks of 30 to prevent errors.
90
+ unflatten_results = add_fields.each_slice(chunk_size).map { |chunk| { '$addFields' => chunk.to_h } }
91
+
92
+ unflatten_results << { '$project' => as_fields.to_h { |f| [f, 0] } }
93
+ end
94
+ end
95
+ end
96
+ end
97
+ end
@@ -0,0 +1,78 @@
1
+ module ForestAdminDatasourceMongoid
2
+ module Utils
3
+ module Pipeline
4
+ # When using the `asModel` options, users can request/filter on the virtual _id and parentId fields
5
+ # of children (using the generated OneToOne relation).
6
+ #
7
+ # As those fields are not written to mongo, they are injected here so that they can be used like
8
+ # any other field.
9
+ #
10
+ # This could be also be done by preprocessing the filter, and postprocessing the records, but this
11
+ # solution seemed simpler, at the cost of additional pipeline stages when making queries.
12
+ #
13
+ # Note that a projection is taken as a parameter so that only fields which are actually used are
14
+ # injected to save resources.
15
+ class VirtualFieldGenerator
16
+ def self.add_virtual(_model, stack, projection)
17
+ set = {}
18
+
19
+ projection.each do |colon_field|
20
+ field = colon_field.tr(':', '.')
21
+ is_from_one_to_one = stack.last[:as_models].any? { |f| field.start_with?("#{f}.") }
22
+
23
+ set[field] = get_path(field) if is_from_one_to_one
24
+ end
25
+
26
+ set.keys.empty? ? [] : [{ '$addFields' => set }]
27
+ end
28
+
29
+ def self.get_path(field)
30
+ id_identifier = '._id'
31
+ if field.end_with?(id_identifier)
32
+ # ... dots to exclude the last character (ex: 'author.' => 'author')
33
+ suffix = field[0...(field.length - id_identifier.length)]
34
+
35
+ return ConditionGenerator.tag_record_if_not_exist_by_value(
36
+ suffix,
37
+ { '$concat' => [{ '$toString' => '$_id' }, (suffix.empty? ? '' : ".#{suffix}")] }
38
+ )
39
+ end
40
+
41
+ parent_id_identifier = '.parent_id'
42
+ if field.end_with?(parent_id_identifier)
43
+
44
+ if field.split('.').length > 2
45
+ # Implementing this would require us to have knowledge of the value of asModel for
46
+ # for virtual models under the current one, which the `stack` variable does not have.
47
+
48
+ # If the expcetion causes issues we could simply return
49
+ # `$${field.substring(0, field.length - 9)}._id` but that would not work if the customer
50
+ # jumped over multiple levels of nesting.
51
+
52
+ # As this is a use case that never happens from the UI, and that can be worked around when
53
+ # using the API, we decided to not implement it.
54
+ raise ForestAdminDatasourceToolkit::Exceptions::ForestException,
55
+ 'Fetching virtual parent_id deeper than 1 level is not supported.'
56
+ end
57
+ suffix = field[0...(field.length - parent_id_identifier.length)]
58
+
59
+ return ConditionGenerator.tag_record_if_not_exist_by_value(suffix, '$_id')
60
+ end
61
+
62
+ content_identifier = '.content'
63
+ if field.end_with?(content_identifier)
64
+ # FIXME: we should check that this is really a leaf field because "content" can't
65
+ # really be used as a reserved word
66
+
67
+ return "$#{field[0...(field.length - content_identifier.length)]}"
68
+ end
69
+
70
+ parent = field[0..field.rindex('.')]
71
+ parent = parent.gsub(/\.+$/, '') # Remove trailing dots
72
+
73
+ ConditionGenerator.tag_record_if_not_exist_by_value(parent, "$#{field}")
74
+ end
75
+ end
76
+ end
77
+ end
78
+ end
@@ -0,0 +1,87 @@
1
+ module ForestAdminDatasourceMongoid
2
+ module Utils
3
+ module Schema
4
+ class FieldsGenerator
5
+ include ForestAdminDatasourceToolkit::Components::Query::ConditionTree
6
+ extend Utils::Helpers
7
+ extend Parser::Column
8
+ extend Parser::Validation
9
+
10
+ def self.build_fields_schema(model, stack)
11
+ our_schema = {}
12
+ child_schema = MongoidSchema.from_model(model).apply_stack(stack)
13
+
14
+ child_schema.fields.each do |name, field|
15
+ next unless name != 'parent'
16
+
17
+ default_value = if field.respond_to?(:object_id_field?) && field.object_id_field?
18
+ nil
19
+ else
20
+ get_default_value(field)
21
+ end
22
+
23
+ our_schema[name] = ForestAdminDatasourceToolkit::Schema::ColumnSchema.new(
24
+ column_type: get_column_type(field),
25
+ filter_operators: operators_for_column_type(get_column_type(field)),
26
+ is_primary_key: name == '_id',
27
+ is_read_only: false,
28
+ is_sortable: get_column_type(field) != 'Json',
29
+ default_value: default_value,
30
+ enum_values: [],
31
+ validations: get_validations(model, field)
32
+ )
33
+
34
+ if !field.is_a?(Hash) && field.foreign_key? && field.type != Array && !field.association.polymorphic?
35
+ our_schema["#{name}__many_to_one"] = build_many_to_one(field)
36
+ end
37
+ end
38
+
39
+ return our_schema unless stack.length > 1
40
+
41
+ parent_prefix = stack[stack.length - 2][:prefix]
42
+
43
+ our_schema['_id'] = build_virtual_primary_key
44
+ parent_id = child_schema.fields['parent']['_id']
45
+ our_schema['parent_id'] = ForestAdminDatasourceToolkit::Schema::ColumnSchema.new(
46
+ column_type: get_column_type(parent_id),
47
+ filter_operators: operators_for_column_type(get_column_type(parent_id)),
48
+ is_primary_key: false,
49
+ is_read_only: false,
50
+ is_sortable: get_column_type(parent_id) != 'Json',
51
+ default_value: parent_id.object_id_field? ? nil : get_default_value(parent_id),
52
+ enum_values: [],
53
+ validations: [{ operator: 'Present' }]
54
+ )
55
+
56
+ model_name = model.name.gsub('::', '__')
57
+ our_schema['parent'] = ForestAdminDatasourceToolkit::Schema::Relations::ManyToOneSchema.new(
58
+ foreign_collection: escape(parent_prefix.nil? ? model_name : "#{model_name}.#{parent_prefix}"),
59
+ foreign_key: 'parent_id',
60
+ foreign_key_target: '_id'
61
+ )
62
+
63
+ our_schema
64
+ end
65
+
66
+ def self.build_virtual_primary_key
67
+ ForestAdminDatasourceToolkit::Schema::ColumnSchema.new(
68
+ column_type: 'String',
69
+ filter_operators: operators_for_column_type('String'),
70
+ is_primary_key: true,
71
+ is_read_only: true,
72
+ is_sortable: true
73
+ )
74
+ end
75
+
76
+ def self.build_many_to_one(field)
77
+ association = field.options[:association]
78
+ ForestAdminDatasourceToolkit::Schema::Relations::ManyToOneSchema.new(
79
+ foreign_collection: association.klass.name.gsub('::', '__'),
80
+ foreign_key: association.foreign_key,
81
+ foreign_key_target: '_id'
82
+ )
83
+ end
84
+ end
85
+ end
86
+ end
87
+ end
@@ -0,0 +1,196 @@
1
+ module ForestAdminDatasourceMongoid
2
+ module Utils
3
+ module Schema
4
+ class MongoidSchema
5
+ include ForestAdminDatasourceToolkit::Exceptions
6
+ include Utils::Helpers
7
+ attr_reader :is_array, :is_leaf, :fields, :models
8
+
9
+ def initialize(model, fields, is_array, is_leaf)
10
+ @models = ObjectSpace.each_object(Class)
11
+ .select { |klass| klass < Mongoid::Document && klass.name && !klass.name.start_with?('Mongoid::') }
12
+ .to_h { |klass| [klass.name, klass] }
13
+ @model = model
14
+ @fields = fields
15
+ @is_array = is_array
16
+ @is_leaf = is_leaf
17
+ end
18
+
19
+ def schema_node
20
+ @is_leaf ? @fields[:content] : @fields
21
+ end
22
+
23
+ def schema_type
24
+ raise ForestException, 'Schema is not a leaf.' unless @is_leaf
25
+
26
+ @fields[:content]
27
+ end
28
+
29
+ def self.from_model(model)
30
+ fields = fields_and_embedded_relations(model)
31
+
32
+ new(model, build_fields(fields), false, false)
33
+ end
34
+
35
+ def self.fields_and_embedded_relations(model)
36
+ embedded_class = [Mongoid::Association::Embedded::EmbedsMany, Mongoid::Association::Embedded::EmbedsOne]
37
+ relations = model.relations.select { |_name, association| embedded_class.include?(association.class) }
38
+
39
+ model.fields.merge(relations)
40
+ end
41
+
42
+ def self.build_fields(schema_fields, level = 0)
43
+ targets = {}
44
+
45
+ schema_fields.each do |name, field|
46
+ next if name.start_with?('$') || name.include?('__') || (name == '_id' && level.positive?)
47
+
48
+ if VersionManager.sub_document?(field)
49
+ sub_targets = build_fields(fields_and_embedded_relations(field.klass), level + 1)
50
+ sub_targets.each { |sub_name, sub_field| recursive_set(targets, "#{name}.#{sub_name}", sub_field) }
51
+ elsif VersionManager.sub_document_array?(field)
52
+ sub_targets = build_fields(fields_and_embedded_relations(field.klass), level + 1)
53
+ sub_targets.each { |sub_name, sub_field| recursive_set(targets, "#{name}.[].#{sub_name}", sub_field) }
54
+ else
55
+ recursive_set(targets, name, field)
56
+ end
57
+ end
58
+
59
+ targets
60
+ end
61
+
62
+ def self.recursive_set(target, path, value)
63
+ index = path.index('.')
64
+ if index.nil?
65
+ target[path] = value
66
+ else
67
+ prefix = path[0, index]
68
+ suffix = path[index + 1, path.length]
69
+ target[prefix] ||= {}
70
+ recursive_set(target[prefix], suffix, value)
71
+ end
72
+ end
73
+
74
+ def list_paths_matching(handle, prefix = nil)
75
+ return [] if @is_leaf
76
+
77
+ @fields.keys
78
+ .filter(&:present?)
79
+ .flat_map do |field|
80
+ schema = get_sub_schema(field)
81
+ sub_prefix = prefix ? "#{prefix}.#{field}" : field
82
+ sub_fields = schema.list_paths_matching(handle, sub_prefix)
83
+ sub_fields.map { |sub_field| "#{field}.#{sub_field}" }
84
+ # debugger
85
+ handle.call(sub_prefix, schema) ? [sub_prefix, *sub_fields] : sub_fields
86
+ end
87
+ end
88
+
89
+ def get_sub_schema(path)
90
+ # Terminating condition
91
+ return self if path.blank?
92
+
93
+ # General case: go down the tree
94
+ prefix, suffix = path.split(/\.(.*)/)
95
+ is_leaf = false
96
+ child = @fields[prefix]
97
+ is_array = child.is_a?(Mongoid::Fields::Standard) && child.options[:type] == Array
98
+
99
+ # Traverse relations
100
+ if child.is_a?(Hash)
101
+ relation_name = @model.relations[prefix].class_name
102
+
103
+ raise ForestException, "Collection '#{relation_name}' not found." unless @models.key?(relation_name)
104
+
105
+ # Traverse arrays
106
+ if child.is_a?(Hash) && child['[]']
107
+ # (has_many embed)
108
+ child = child['[]']
109
+ is_array = true
110
+ else
111
+ # (has_one embed)
112
+ child = MongoidSchema.from_model(@models[relation_name]).fields
113
+ end
114
+
115
+ return MongoidSchema.new(@models[relation_name], child, is_array, is_leaf).get_sub_schema(suffix)
116
+ elsif child.nil?
117
+ raise ForestException, "Field '#{prefix}' not found. Available fields are: #{list_fields}"
118
+ end
119
+
120
+ # We ended up on a field => box it.
121
+ if child.is_a? Mongoid::Fields::Standard
122
+ child = { content: child }
123
+ is_leaf = true
124
+ end
125
+
126
+ MongoidSchema.new(@model, child, is_array, is_leaf).get_sub_schema(suffix)
127
+ end
128
+
129
+ def apply_stack(stack, skip_as_models: false)
130
+ raise ForestException, 'Stack can never be empty.' if stack.empty?
131
+
132
+ step = stack.pop
133
+ sub_schema = get_sub_schema(step[:prefix])
134
+
135
+ step[:as_fields].each do |field|
136
+ field_schema = sub_schema.get_sub_schema(field)
137
+ recursive_delete(sub_schema.fields, field)
138
+
139
+ sub_schema.fields[field.gsub('.', '@@@')] = if field_schema.is_array
140
+ { '[]' => field_schema.schema_node }
141
+ else
142
+ field_schema.schema_node
143
+ end
144
+ end
145
+
146
+ unless stack.empty?
147
+ sub_schema.fields['_id'] = Mongoid::Fields::Standard.new('__placeholder__', { type: String })
148
+ sub_schema.fields['parent'] = apply_stack(stack).fields
149
+ sub_schema.fields['parent_id'] = sub_schema.fields['parent']['_id']
150
+ end
151
+
152
+ if skip_as_models
153
+ # Here we actually should recurse into the subSchema and add the _id and parentId fields
154
+ # to the virtual one-to-one relations.
155
+ #
156
+ # The issue is that we can't do that because we don't know where the relations are after
157
+ # the first level of nesting (we would need to have the complete asModel / asFields like in
158
+ # the datasource.ts file).
159
+ #
160
+ # Because of that, we need to work around the missing fields in:
161
+ # - pipeline/virtual-fields.ts file: we're throwing an error when we can't guess the value
162
+ # of a given _id / parentId field.
163
+ # - pipeline/filter.ts: we're using an educated guess for the types of the _id / parentId
164
+ # fields (String or ObjectId)
165
+ else
166
+ step[:as_models].each do |field|
167
+ recursive_delete(@fields, field)
168
+ end
169
+ end
170
+
171
+ stack << step
172
+
173
+ sub_schema
174
+ end
175
+
176
+ # List leafs and arrays up to a certain level
177
+ # Arrays are never traversed
178
+ def list_fields(level = Float::INFINITY)
179
+ raise ForestException, 'Cannot list fields on a leaf schema.' if @is_leaf
180
+ raise ForestException, 'Level must be greater than 0.' if level.zero?
181
+
182
+ return @fields.keys if level == 1
183
+
184
+ @fields.keys.flat_map do |field|
185
+ schema = get_sub_schema(field)
186
+ if schema.is_leaf || schema.is_array
187
+ [field]
188
+ else
189
+ schema.list_fields(level - 1).map { |sub_field| "#{field}.#{sub_field}" }
190
+ end
191
+ end
192
+ end
193
+ end
194
+ end
195
+ end
196
+ end
@@ -0,0 +1,51 @@
1
+ module ForestAdminDatasourceMongoid
2
+ module Utils
3
+ module Schema
4
+ class RelationGenerator
5
+ include ForestAdminDatasourceToolkit::Schema::Relations
6
+ extend ForestAdminDatasourceMongoid::Utils::Helpers
7
+
8
+ def self.add_implicit_relations(collections)
9
+ collections.each_value do |collection|
10
+ many_to_ones = collection.schema[:fields].select { |_, f| f.type == 'ManyToOne' }
11
+
12
+ many_to_ones.each do |(name, field)|
13
+ add_many_to_one_inverse(collection, name, field)
14
+ end
15
+ end
16
+ end
17
+
18
+ # Given any many to one relation, generated while parsing mongoose schema, generate the
19
+ # inverse relationship on the foreignCollection.
20
+ # /!\ The inverse can be a OneToOne, or a ManyToOne
21
+ def self.add_many_to_one_inverse(collection, name, schema)
22
+ if name == 'parent'
23
+ # Create inverse of 'parent' relationship so that the relation name matches the actual name
24
+ # of the data which is stored in the database.
25
+ stack = collection.stack
26
+ prefix = stack[stack.length - 1][:prefix]
27
+ is_array = MongoidSchema.from_model(collection.model).apply_stack(stack).is_array
28
+
29
+ type = is_array ? OneToManySchema : OneToOneSchema
30
+ inverse_name = escape(prefix)
31
+
32
+ if stack.length > 2
33
+ previous_length = stack[stack.length - 2][:prefix].length + 1
34
+ inverse_name = prefix[previous_length..]
35
+ end
36
+ else
37
+ inverse_name = escape("#{collection.name}_#{name}__inverse")
38
+ type = OneToManySchema
39
+ end
40
+
41
+ other_collection = collection.datasource.get_collection(schema.foreign_collection)
42
+ other_collection.schema[:fields][inverse_name] = type.new(
43
+ foreign_collection: collection.name,
44
+ origin_key: schema.foreign_key,
45
+ origin_key_target: schema.foreign_key_target
46
+ )
47
+ end
48
+ end
49
+ end
50
+ end
51
+ end
@@ -0,0 +1,13 @@
1
+ module ForestAdminDatasourceMongoid
2
+ module Utils
3
+ class VersionManager
4
+ def self.sub_document?(field)
5
+ field.is_a?(Mongoid::Association::Embedded::EmbedsOne)
6
+ end
7
+
8
+ def self.sub_document_array?(field)
9
+ field.is_a?(Mongoid::Association::Embedded::EmbedsMany)
10
+ end
11
+ end
12
+ end
13
+ end
@@ -0,0 +1,3 @@
1
+ module ForestAdminDatasourceMongoid
2
+ VERSION = "1.0.1"
3
+ end
@@ -0,0 +1,11 @@
1
+ require_relative 'forest_admin_datasource_mongoid/version'
2
+ require 'zeitwerk'
3
+
4
+ loader = Zeitwerk::Loader.for_gem
5
+ loader.ignore("#{__dir__}/models")
6
+ loader.setup
7
+
8
+ module ForestAdminDatasourceMongoid
9
+ class Error < StandardError; end
10
+ # Your code goes here...
11
+ end
metadata ADDED
@@ -0,0 +1,119 @@
1
+ --- !ruby/object:Gem::Specification
2
+ name: forest_admin_datasource_mongoid
3
+ version: !ruby/object:Gem::Version
4
+ version: 1.0.1
5
+ platform: ruby
6
+ authors:
7
+ - Matthieu
8
+ - Nicolas
9
+ autorequire:
10
+ bindir: exe
11
+ cert_chain: []
12
+ date: 2025-09-23 00:00:00.000000000 Z
13
+ dependencies:
14
+ - !ruby/object:Gem::Dependency
15
+ name: mongoid
16
+ requirement: !ruby/object:Gem::Requirement
17
+ requirements:
18
+ - - ">="
19
+ - !ruby/object:Gem::Version
20
+ version: '9.0'
21
+ type: :runtime
22
+ prerelease: false
23
+ version_requirements: !ruby/object:Gem::Requirement
24
+ requirements:
25
+ - - ">="
26
+ - !ruby/object:Gem::Version
27
+ version: '9.0'
28
+ - !ruby/object:Gem::Dependency
29
+ name: activesupport
30
+ requirement: !ruby/object:Gem::Requirement
31
+ requirements:
32
+ - - ">="
33
+ - !ruby/object:Gem::Version
34
+ version: '6.1'
35
+ type: :runtime
36
+ prerelease: false
37
+ version_requirements: !ruby/object:Gem::Requirement
38
+ requirements:
39
+ - - ">="
40
+ - !ruby/object:Gem::Version
41
+ version: '6.1'
42
+ - !ruby/object:Gem::Dependency
43
+ name: zeitwerk
44
+ requirement: !ruby/object:Gem::Requirement
45
+ requirements:
46
+ - - "~>"
47
+ - !ruby/object:Gem::Version
48
+ version: '2.3'
49
+ type: :runtime
50
+ prerelease: false
51
+ version_requirements: !ruby/object:Gem::Requirement
52
+ requirements:
53
+ - - "~>"
54
+ - !ruby/object:Gem::Version
55
+ version: '2.3'
56
+ description: |-
57
+ Forest is a modern admin interface that works on all major web frameworks. This gem makes Forest
58
+ admin work on any Ruby application.
59
+ email:
60
+ - matthv@gmail.com
61
+ - nicolasalexandre9@gmail.com
62
+ executables: []
63
+ extensions: []
64
+ extra_rdoc_files: []
65
+ files:
66
+ - ".rspec"
67
+ - LICENSE
68
+ - Rakefile
69
+ - forest_admin_datasource_mongoid.gemspec
70
+ - lib/forest_admin_datasource_mongoid.rb
71
+ - lib/forest_admin_datasource_mongoid/collection.rb
72
+ - lib/forest_admin_datasource_mongoid/datasource.rb
73
+ - lib/forest_admin_datasource_mongoid/options_parser.rb
74
+ - lib/forest_admin_datasource_mongoid/parser/column.rb
75
+ - lib/forest_admin_datasource_mongoid/parser/relation.rb
76
+ - lib/forest_admin_datasource_mongoid/parser/validation.rb
77
+ - lib/forest_admin_datasource_mongoid/utils/add_null_values.rb
78
+ - lib/forest_admin_datasource_mongoid/utils/helpers.rb
79
+ - lib/forest_admin_datasource_mongoid/utils/mongoid_serializer.rb
80
+ - lib/forest_admin_datasource_mongoid/utils/pipeline/condition_generator.rb
81
+ - lib/forest_admin_datasource_mongoid/utils/pipeline/filter_generator.rb
82
+ - lib/forest_admin_datasource_mongoid/utils/pipeline/group_generator.rb
83
+ - lib/forest_admin_datasource_mongoid/utils/pipeline/lookup_generator.rb
84
+ - lib/forest_admin_datasource_mongoid/utils/pipeline/projection_generator.rb
85
+ - lib/forest_admin_datasource_mongoid/utils/pipeline/reparent_generator.rb
86
+ - lib/forest_admin_datasource_mongoid/utils/pipeline/virtual_field_generator.rb
87
+ - lib/forest_admin_datasource_mongoid/utils/schema/fields_generator.rb
88
+ - lib/forest_admin_datasource_mongoid/utils/schema/mongoid_schema.rb
89
+ - lib/forest_admin_datasource_mongoid/utils/schema/relation_generator.rb
90
+ - lib/forest_admin_datasource_mongoid/utils/version_manager.rb
91
+ - lib/forest_admin_datasource_mongoid/version.rb
92
+ homepage: https://www.forestadmin.com
93
+ licenses:
94
+ - GPL-3.0
95
+ metadata:
96
+ homepage_uri: https://www.forestadmin.com
97
+ source_code_uri: https://github.com/ForestAdmin/agent-ruby
98
+ changelog_uri: https://github.com/ForestAdmin/agent-ruby/blob/main/CHANGELOG.md
99
+ rubygems_mfa_required: 'false'
100
+ post_install_message:
101
+ rdoc_options: []
102
+ require_paths:
103
+ - lib
104
+ required_ruby_version: !ruby/object:Gem::Requirement
105
+ requirements:
106
+ - - ">="
107
+ - !ruby/object:Gem::Version
108
+ version: 3.0.0
109
+ required_rubygems_version: !ruby/object:Gem::Requirement
110
+ requirements:
111
+ - - ">="
112
+ - !ruby/object:Gem::Version
113
+ version: '0'
114
+ requirements: []
115
+ rubygems_version: 3.4.20
116
+ signing_key:
117
+ specification_version: 4
118
+ summary: Ruby agent for Forest Admin.
119
+ test_files: []