forest_admin_datasource_active_record 1.0.0.pre.beta.21
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +7 -0
- data/.rspec +3 -0
- data/README.md +31 -0
- data/Rakefile +12 -0
- data/forest_admin_datasource_active_record.gemspec +37 -0
- data/lib/forest_admin_datasource_active_record/collection.rb +117 -0
- data/lib/forest_admin_datasource_active_record/datasource.rb +29 -0
- data/lib/forest_admin_datasource_active_record/parser/column.rb +71 -0
- data/lib/forest_admin_datasource_active_record/parser/relation.rb +21 -0
- data/lib/forest_admin_datasource_active_record/parser/validation.rb +108 -0
- data/lib/forest_admin_datasource_active_record/utils/query.rb +118 -0
- data/lib/forest_admin_datasource_active_record/version.rb +3 -0
- data/lib/forest_admin_datasource_active_record.rb +11 -0
- data/sig/forest_admin_datasource_active_record/collection.rbs +11 -0
- data/sig/forest_admin_datasource_active_record.rbs +4 -0
- metadata +93 -0
checksums.yaml
ADDED
@@ -0,0 +1,7 @@
|
|
1
|
+
---
|
2
|
+
SHA256:
|
3
|
+
metadata.gz: aace6865ce55eb722690ad3a410c31144c81a2ac7aa3f9db0d5dcb7d706e4544
|
4
|
+
data.tar.gz: 1ced7531287ce44c3d834875980471ccfaab5a7f2ea401ab84405f9547da96c8
|
5
|
+
SHA512:
|
6
|
+
metadata.gz: 9c6f07ba223bcbe79dac1bd5ab787378e4b68e9e0bc75576e67cc91d7d5f7de6cac8b2d60c94d66716b7140a93c8d5b19206336e0fa6efae8d419b93a07f0565
|
7
|
+
data.tar.gz: fd9d4172f74352ff3226513a2bc7b59f04f26b10ba04948c6d5ee87f8cc73e9f14575fe657cb36ea1892a2d5239c893cb7e7d7fc86a5683b81c0edd959d691ca
|
data/.rspec
ADDED
data/README.md
ADDED
@@ -0,0 +1,31 @@
|
|
1
|
+
# ForestAdminDatasourceActiveRecord
|
2
|
+
|
3
|
+
TODO: Delete this and the text below, and describe your gem
|
4
|
+
|
5
|
+
Welcome to your new gem! In this directory, you'll find the files you need to be able to package up your Ruby library into a gem. Put your Ruby code in the file `lib/forest_admin_datasource_active_record`. To experiment with that code, run `bin/console` for an interactive prompt.
|
6
|
+
|
7
|
+
## Installation
|
8
|
+
|
9
|
+
TODO: Replace `UPDATE_WITH_YOUR_GEM_NAME_PRIOR_TO_RELEASE_TO_RUBYGEMS_ORG` with your gem name right after releasing it to RubyGems.org. Please do not do it earlier due to security reasons. Alternatively, replace this section with instructions to install your gem from git if you don't plan to release to RubyGems.org.
|
10
|
+
|
11
|
+
Install the gem and add to the application's Gemfile by executing:
|
12
|
+
|
13
|
+
$ bundle add UPDATE_WITH_YOUR_GEM_NAME_PRIOR_TO_RELEASE_TO_RUBYGEMS_ORG
|
14
|
+
|
15
|
+
If bundler is not being used to manage dependencies, install the gem by executing:
|
16
|
+
|
17
|
+
$ gem install UPDATE_WITH_YOUR_GEM_NAME_PRIOR_TO_RELEASE_TO_RUBYGEMS_ORG
|
18
|
+
|
19
|
+
## Usage
|
20
|
+
|
21
|
+
TODO: Write usage instructions here
|
22
|
+
|
23
|
+
## Development
|
24
|
+
|
25
|
+
After checking out the repo, run `bin/setup` to install dependencies. Then, run `rake spec` to run the tests. You can also run `bin/console` for an interactive prompt that will allow you to experiment.
|
26
|
+
|
27
|
+
To install this gem onto your local machine, run `bundle exec rake install`. To release a new version, update the version number in `version.rb`, and then run `bundle exec rake release`, which will create a git tag for the version, push git commits and the created tag, and push the `.gem` file to [rubygems.org](https://rubygems.org).
|
28
|
+
|
29
|
+
## Contributing
|
30
|
+
|
31
|
+
Bug reports and pull requests are welcome on GitHub at https://github.com/[USERNAME]/forest_admin_datasource_active_record.
|
data/Rakefile
ADDED
@@ -0,0 +1,37 @@
|
|
1
|
+
lib = File.expand_path('lib', __dir__)
|
2
|
+
$LOAD_PATH.unshift lib unless $LOAD_PATH.include?(lib)
|
3
|
+
|
4
|
+
require_relative "lib/forest_admin_datasource_active_record/version"
|
5
|
+
|
6
|
+
Gem::Specification.new do |spec|
|
7
|
+
spec.name = "forest_admin_datasource_active_record"
|
8
|
+
spec.version = ForestAdminDatasourceActiveRecord::VERSION
|
9
|
+
spec.authors = ["Matthieu", "Nicolas"]
|
10
|
+
spec.email = ["matthv@gmail.com", "nicolasalexandre9@gmail.com"]
|
11
|
+
spec.homepage = "https://www.forestadmin.com"
|
12
|
+
spec.summary = "Ruby agent for Forest Admin."
|
13
|
+
spec.description = "Forest is a modern admin interface that works on all major web frameworks. This gem makes Forest
|
14
|
+
admin work on any Ruby application."
|
15
|
+
spec.license = "MIT"
|
16
|
+
spec.required_ruby_version = ">= 3.0.0"
|
17
|
+
|
18
|
+
spec.metadata["homepage_uri"] = spec.homepage
|
19
|
+
spec.metadata["source_code_uri"] = "https://github.com/ForestAdmin/agent-ruby"
|
20
|
+
spec.metadata["changelog_uri"] = "https://github.com/ForestAdmin/agent-ruby/CHANGELOG.md"
|
21
|
+
spec.metadata["rubygems_mfa_required"] = "false"
|
22
|
+
|
23
|
+
# Specify which files should be added to the gem when it is released.
|
24
|
+
# The `git ls-files -z` loads the files in the RubyGem that have been added into git.
|
25
|
+
spec.files = Dir.chdir(__dir__) do
|
26
|
+
`git ls-files -z`.split("\x0").reject do |f|
|
27
|
+
(File.expand_path(f) == __FILE__) ||
|
28
|
+
f.start_with?(*%w[bin/ test/ spec/ features/ .git .circleci appveyor Gemfile])
|
29
|
+
end
|
30
|
+
end
|
31
|
+
spec.bindir = "exe"
|
32
|
+
spec.executables = spec.files.grep(%r{\Aexe/}) { |f| File.basename(f) }
|
33
|
+
spec.require_paths = ["lib"]
|
34
|
+
|
35
|
+
spec.add_dependency "activerecord", ">= 6.1"
|
36
|
+
spec.add_dependency "zeitwerk", "~> 2.3"
|
37
|
+
end
|
@@ -0,0 +1,117 @@
|
|
1
|
+
module ForestAdminDatasourceActiveRecord
|
2
|
+
class Collection < ForestAdminDatasourceToolkit::Collection
|
3
|
+
include Parser::Column
|
4
|
+
include Parser::Relation
|
5
|
+
include ForestAdminDatasourceToolkit::Components::Query
|
6
|
+
|
7
|
+
attr_reader :model
|
8
|
+
|
9
|
+
def initialize(datasource, model)
|
10
|
+
@model = model
|
11
|
+
name = model.name.demodulize.underscore
|
12
|
+
super(datasource, name)
|
13
|
+
fetch_fields
|
14
|
+
fetch_associations
|
15
|
+
end
|
16
|
+
|
17
|
+
def list(_caller, filter, projection)
|
18
|
+
query = Utils::Query.new(self, projection, filter).build
|
19
|
+
|
20
|
+
query.all
|
21
|
+
end
|
22
|
+
|
23
|
+
def aggregate(_caller, _filter, aggregation, _limit = nil)
|
24
|
+
field = aggregation.field || '*'
|
25
|
+
|
26
|
+
[
|
27
|
+
{
|
28
|
+
value: @model.send(aggregation.operation.downcase, field),
|
29
|
+
group: []
|
30
|
+
}
|
31
|
+
]
|
32
|
+
end
|
33
|
+
|
34
|
+
def create(_caller, data)
|
35
|
+
@model.create(data)
|
36
|
+
end
|
37
|
+
|
38
|
+
def update(_caller, filter, data)
|
39
|
+
entity = Utils::Query.new(self, nil, filter).build.first
|
40
|
+
entity.update(data)
|
41
|
+
end
|
42
|
+
|
43
|
+
def delete(_caller, filter)
|
44
|
+
entities = Utils::Query.new(self, nil, filter).build
|
45
|
+
entities&.each(&:destroy)
|
46
|
+
end
|
47
|
+
|
48
|
+
private
|
49
|
+
|
50
|
+
def fetch_fields
|
51
|
+
@model.columns_hash.each do |column_name, column|
|
52
|
+
# TODO: check is not sti column
|
53
|
+
field = ForestAdminDatasourceToolkit::Schema::ColumnSchema.new(
|
54
|
+
column_type: get_column_type(@model, column),
|
55
|
+
filter_operators: operators_for_column_type(get_column_type(@model, column)),
|
56
|
+
is_primary_key: column_name == @model.primary_key,
|
57
|
+
is_read_only: false,
|
58
|
+
is_sortable: true,
|
59
|
+
default_value: column.default,
|
60
|
+
enum_values: get_enum_values(@model, column),
|
61
|
+
# validations: get_validations(column)
|
62
|
+
validations: []
|
63
|
+
)
|
64
|
+
|
65
|
+
add_field(column_name, field)
|
66
|
+
end
|
67
|
+
end
|
68
|
+
|
69
|
+
def fetch_associations
|
70
|
+
associations(@model).each do |association|
|
71
|
+
case association.macro
|
72
|
+
when :has_one
|
73
|
+
add_field(
|
74
|
+
association.name.to_s,
|
75
|
+
ForestAdminDatasourceToolkit::Schema::Relations::OneToOneSchema.new(
|
76
|
+
foreign_collection: association.class_name.demodulize.underscore,
|
77
|
+
origin_key: association.foreign_key,
|
78
|
+
origin_key_target: association.association_primary_key
|
79
|
+
)
|
80
|
+
)
|
81
|
+
when :belongs_to
|
82
|
+
add_field(
|
83
|
+
association.name.to_s,
|
84
|
+
ForestAdminDatasourceToolkit::Schema::Relations::ManyToOneSchema.new(
|
85
|
+
foreign_collection: association.class_name.demodulize.underscore,
|
86
|
+
foreign_key: association.foreign_key,
|
87
|
+
foreign_key_target: association.association_primary_key
|
88
|
+
)
|
89
|
+
)
|
90
|
+
when :has_many
|
91
|
+
if association.through_reflection?
|
92
|
+
add_field(
|
93
|
+
association.name.to_s,
|
94
|
+
ForestAdminDatasourceToolkit::Schema::Relations::ManyToManySchema.new(
|
95
|
+
foreign_collection: association.class_name.demodulize.underscore,
|
96
|
+
origin_key: association.through_reflection.foreign_key,
|
97
|
+
origin_key_target: association.through_reflection.join_foreign_key,
|
98
|
+
foreign_key: association.join_foreign_key,
|
99
|
+
foreign_key_target: association.association_primary_key,
|
100
|
+
through_collection: association.through_reflection.class_name.demodulize.underscore
|
101
|
+
)
|
102
|
+
)
|
103
|
+
else
|
104
|
+
add_field(
|
105
|
+
association.name.to_s,
|
106
|
+
ForestAdminDatasourceToolkit::Schema::Relations::OneToManySchema.new(
|
107
|
+
foreign_collection: association.class_name.demodulize.underscore,
|
108
|
+
origin_key: association.foreign_key,
|
109
|
+
origin_key_target: association.association_primary_key
|
110
|
+
)
|
111
|
+
)
|
112
|
+
end
|
113
|
+
end
|
114
|
+
end
|
115
|
+
end
|
116
|
+
end
|
117
|
+
end
|
@@ -0,0 +1,29 @@
|
|
1
|
+
require 'active_record'
|
2
|
+
|
3
|
+
module ForestAdminDatasourceActiveRecord
|
4
|
+
class Datasource < ForestAdminDatasourceToolkit::Datasource
|
5
|
+
def initialize(db_config = {})
|
6
|
+
super()
|
7
|
+
@models = []
|
8
|
+
init_orm(db_config)
|
9
|
+
generate
|
10
|
+
end
|
11
|
+
|
12
|
+
private
|
13
|
+
|
14
|
+
def generate
|
15
|
+
ActiveRecord::Base.descendants.each { |model| fetch_model(model) }
|
16
|
+
@models.each do |model|
|
17
|
+
add_collection(Collection.new(self, model))
|
18
|
+
end
|
19
|
+
end
|
20
|
+
|
21
|
+
def fetch_model(model)
|
22
|
+
@models << model unless model.abstract_class? || @models.include?(model) || !model.table_exists?
|
23
|
+
end
|
24
|
+
|
25
|
+
def init_orm(db_config)
|
26
|
+
ActiveRecord::Base.establish_connection(db_config)
|
27
|
+
end
|
28
|
+
end
|
29
|
+
end
|
@@ -0,0 +1,71 @@
|
|
1
|
+
module ForestAdminDatasourceActiveRecord
|
2
|
+
module Parser
|
3
|
+
module Column
|
4
|
+
include ForestAdminDatasourceToolkit::Components::Query::ConditionTree
|
5
|
+
|
6
|
+
TYPES = {
|
7
|
+
boolean: 'Boolean',
|
8
|
+
datetime: 'Date',
|
9
|
+
date: 'Dateonly',
|
10
|
+
integer: 'Number',
|
11
|
+
float: 'Number',
|
12
|
+
decimal: 'Number',
|
13
|
+
json: 'Json',
|
14
|
+
jsonb: 'Json',
|
15
|
+
hstore: 'Json',
|
16
|
+
string: 'String',
|
17
|
+
text: 'String',
|
18
|
+
citext: 'String',
|
19
|
+
time: 'Time',
|
20
|
+
uuid: 'Uuid',
|
21
|
+
binary: 'Binary'
|
22
|
+
}.freeze
|
23
|
+
|
24
|
+
def get_column_type(model, column)
|
25
|
+
if model.respond_to?(:defined_enums) &&
|
26
|
+
model.defined_enums.key?(column.name)
|
27
|
+
return 'Enum'
|
28
|
+
end
|
29
|
+
|
30
|
+
is_array = (column.respond_to?(:array) && column.array == true)
|
31
|
+
is_array ? "[#{TYPES[column.type]}]" : TYPES[column.type]
|
32
|
+
end
|
33
|
+
|
34
|
+
def get_enum_values(model, column)
|
35
|
+
enum_values = []
|
36
|
+
if get_column_type(model, column) == 'Enum'
|
37
|
+
if sti_column?(model, column)
|
38
|
+
model.descendants.each { |sti_model| enum_values << sti_model.name }
|
39
|
+
else
|
40
|
+
model.defined_enums[column.name].each { |name, _value| enum_values << name }
|
41
|
+
end
|
42
|
+
end
|
43
|
+
enum_values
|
44
|
+
end
|
45
|
+
|
46
|
+
def sti_column?(model, column)
|
47
|
+
model.inheritance_column && column.name == model.inheritance_column
|
48
|
+
end
|
49
|
+
|
50
|
+
def operators_for_column_type(type)
|
51
|
+
result = [Operators::PRESENT, Operators::MISSING]
|
52
|
+
equality = [Operators::EQUAL, Operators::NOT_EQUAL, Operators::IN, Operators::NOT_IN]
|
53
|
+
|
54
|
+
if type.is_a? String
|
55
|
+
orderables = [Operators::LESS_THAN, Operators::GREATER_THAN]
|
56
|
+
strings = [Operators::LIKE, Operators::I_LIKE, Operators::NOT_CONTAINS]
|
57
|
+
|
58
|
+
result += equality if %w[Boolean Binary Enum Uuid].include?(type)
|
59
|
+
|
60
|
+
result = result + equality + orderables if %w[Date Dateonly Number].include?(type)
|
61
|
+
|
62
|
+
result = result + equality + orderables + strings if %w[String].include?(type)
|
63
|
+
end
|
64
|
+
|
65
|
+
result = result + equality + ['Includes_All'] if type.is_a? Array
|
66
|
+
|
67
|
+
result
|
68
|
+
end
|
69
|
+
end
|
70
|
+
end
|
71
|
+
end
|
@@ -0,0 +1,21 @@
|
|
1
|
+
module ForestAdminDatasourceActiveRecord
|
2
|
+
module Parser
|
3
|
+
module Relation
|
4
|
+
def associations(model)
|
5
|
+
model.reflect_on_all_associations.select do |association|
|
6
|
+
!polymorphic?(association) && !active_type?(association.klass)
|
7
|
+
end
|
8
|
+
end
|
9
|
+
|
10
|
+
def polymorphic?(association)
|
11
|
+
association.options[:polymorphic]
|
12
|
+
end
|
13
|
+
|
14
|
+
# NOTICE: Ignores ActiveType::Object association during introspection and interactions.
|
15
|
+
# See the gem documentation: https://github.com/makandra/active_type
|
16
|
+
def active_type?(model)
|
17
|
+
Object.const_defined?('ActiveType::Object') && model < ActiveType::Object
|
18
|
+
end
|
19
|
+
end
|
20
|
+
end
|
21
|
+
end
|
@@ -0,0 +1,108 @@
|
|
1
|
+
module ForestAdminDatasourceActiveRecord
|
2
|
+
module Parser
|
3
|
+
module Validation
|
4
|
+
def get_validations(column)
|
5
|
+
validations = []
|
6
|
+
# NOTICE: Do not consider validations if a before_validation Active Records
|
7
|
+
# Callback is detected.
|
8
|
+
return validations if @model._validation_callbacks.map(&:kind).include? :before
|
9
|
+
|
10
|
+
if @model._validators? && @model._validators[column.name.to_sym].size.positive?
|
11
|
+
@model._validators[column.name.to_sym].each do |validator|
|
12
|
+
# NOTICE: Do not consider conditional validations
|
13
|
+
next if validator.options[:if] || validator.options[:unless] || validator.options[:on]
|
14
|
+
|
15
|
+
case validator
|
16
|
+
when ActiveRecord::Validations::PresenceValidator
|
17
|
+
validations << {
|
18
|
+
type: 'is present',
|
19
|
+
message: validator.options[:message]
|
20
|
+
}
|
21
|
+
when ActiveModel::Validations::NumericalityValidator
|
22
|
+
validations = parse_numericality_validator(validator, validations)
|
23
|
+
when ActiveModel::Validations::LengthValidator
|
24
|
+
validations = parse_length_validator(validator, validations)
|
25
|
+
when ActiveModel::Validations::FormatValidator
|
26
|
+
validations = parse_format_validator(validator, validations)
|
27
|
+
end
|
28
|
+
end
|
29
|
+
end
|
30
|
+
|
31
|
+
validations
|
32
|
+
end
|
33
|
+
|
34
|
+
def parse_numericality_validator(validator, parsed_validations)
|
35
|
+
validator.options.each do |option, value|
|
36
|
+
case option
|
37
|
+
when :greater_than, :greater_than_or_equal_to
|
38
|
+
parsed_validations << {
|
39
|
+
type: 'is greater than',
|
40
|
+
value: value,
|
41
|
+
message: validator.options[:message]
|
42
|
+
}
|
43
|
+
when :less_than, :less_than_or_equal_to
|
44
|
+
parsed_validations << {
|
45
|
+
type: 'is less than',
|
46
|
+
value: value,
|
47
|
+
message: validator.options[:message]
|
48
|
+
}
|
49
|
+
end
|
50
|
+
end
|
51
|
+
end
|
52
|
+
|
53
|
+
def parse_length_validator(validator, parsed_validations)
|
54
|
+
return unless get_column_type(column) == 'String'
|
55
|
+
|
56
|
+
validator.options.each do |option, value|
|
57
|
+
case option
|
58
|
+
when :minimum
|
59
|
+
parsed_validations << {
|
60
|
+
type: 'is longer than',
|
61
|
+
value: value,
|
62
|
+
message: validator.options[:message]
|
63
|
+
}
|
64
|
+
when :maximum
|
65
|
+
parsed_validations << {
|
66
|
+
type: 'is shorter than',
|
67
|
+
value: value,
|
68
|
+
message: validator.options[:message]
|
69
|
+
}
|
70
|
+
when :is
|
71
|
+
parsed_validations << {
|
72
|
+
type: 'is longer than',
|
73
|
+
value: value,
|
74
|
+
message: validator.options[:message]
|
75
|
+
}
|
76
|
+
parsed_validations << {
|
77
|
+
type: 'is shorter than',
|
78
|
+
value: value,
|
79
|
+
message: validator.options[:message]
|
80
|
+
}
|
81
|
+
end
|
82
|
+
end
|
83
|
+
end
|
84
|
+
|
85
|
+
def parse_format_validator(validator, parsed_validations)
|
86
|
+
validator.options.each do |option, value|
|
87
|
+
case option
|
88
|
+
when :with
|
89
|
+
options = /\?([imx]){0,3}/.match(validator.options[:with].to_s)
|
90
|
+
options = options && options[1] ? options[1] : ''
|
91
|
+
regex = value.source
|
92
|
+
|
93
|
+
# NOTICE: Transform a Ruby regex into a JS one
|
94
|
+
regex = regex.sub('\\A', '^').sub('\\Z', '$').sub('\\z', '$').gsub(/\n+|\s+/, '')
|
95
|
+
|
96
|
+
parsed_validations << {
|
97
|
+
type: 'is like',
|
98
|
+
value: "/#{regex}/#{options}",
|
99
|
+
message: validator.options[:message]
|
100
|
+
}
|
101
|
+
end
|
102
|
+
end
|
103
|
+
|
104
|
+
parsed_validations
|
105
|
+
end
|
106
|
+
end
|
107
|
+
end
|
108
|
+
end
|
@@ -0,0 +1,118 @@
|
|
1
|
+
module ForestAdminDatasourceActiveRecord
|
2
|
+
module Utils
|
3
|
+
class Query
|
4
|
+
include ForestAdminDatasourceToolkit::Components::Query::ConditionTree
|
5
|
+
|
6
|
+
def initialize(collection, projection, filter)
|
7
|
+
@collection = collection
|
8
|
+
@query = @collection.model
|
9
|
+
@projection = projection
|
10
|
+
@filter = filter
|
11
|
+
@arel_table = @collection.model.arel_table
|
12
|
+
end
|
13
|
+
|
14
|
+
def build
|
15
|
+
@query = select
|
16
|
+
@query = apply_filter
|
17
|
+
|
18
|
+
@query
|
19
|
+
end
|
20
|
+
|
21
|
+
def apply_filter
|
22
|
+
@query = apply_condition_tree(@filter.condition_tree) unless @filter.condition_tree.nil?
|
23
|
+
@query = apply_pagination(@filter.page) unless @filter.page.nil?
|
24
|
+
|
25
|
+
@query
|
26
|
+
end
|
27
|
+
|
28
|
+
def apply_pagination(page)
|
29
|
+
@query.offset(page.offset).limit(page.limit)
|
30
|
+
|
31
|
+
@query
|
32
|
+
end
|
33
|
+
|
34
|
+
def apply_condition_tree(condition_tree, aggregator = nil)
|
35
|
+
if condition_tree.is_a? Nodes::ConditionTreeBranch
|
36
|
+
aggregator = condition_tree.aggregator.downcase
|
37
|
+
condition_tree.conditions.each do |condition|
|
38
|
+
query = apply_condition_tree(condition, aggregator)
|
39
|
+
@query = @query.send(aggregator, query)
|
40
|
+
end
|
41
|
+
|
42
|
+
@query
|
43
|
+
else
|
44
|
+
compute_main_operator(condition_tree, aggregator || 'and')
|
45
|
+
end
|
46
|
+
end
|
47
|
+
|
48
|
+
def compute_main_operator(condition_tree, aggregator)
|
49
|
+
field = format_field(condition_tree.field)
|
50
|
+
value = condition_tree.value
|
51
|
+
|
52
|
+
case condition_tree.operator
|
53
|
+
when Operators::PRESENT
|
54
|
+
@query = @query.send(aggregator, @query.where.not({ field => nil }))
|
55
|
+
when Operators::EQUAL, Operators::IN
|
56
|
+
@query = @query.send(aggregator, @query.where({ field => value }))
|
57
|
+
when Operators::NOT_EQUAL, Operators::NOT_IN
|
58
|
+
@query = @query.send(aggregator, @query.where.not({ field => value }))
|
59
|
+
when Operators::GREATER_THAN
|
60
|
+
@query = @query.send(aggregator, @query.where(@arel_table[field.to_sym].gt(value)))
|
61
|
+
when Operators::LESS_THAN
|
62
|
+
@query = @query.send(aggregator, @query.where(@arel_table[field.to_sym].lt(value)))
|
63
|
+
when Operators::NOT_CONTAINS
|
64
|
+
@query = @query.send(aggregator, @query.where.not(@arel_table[field.to_sym].matches("%#{value}%")))
|
65
|
+
when Operators::CONTAINS
|
66
|
+
@query = @query.send(aggregator, @query.where(@arel_table[field.to_sym].matches("%#{value}%")))
|
67
|
+
when Operators::INCLUDES_ALL
|
68
|
+
# TODO: to implement
|
69
|
+
end
|
70
|
+
|
71
|
+
@query
|
72
|
+
end
|
73
|
+
|
74
|
+
def select
|
75
|
+
unless @projection.nil?
|
76
|
+
query_select = @projection.columns.map { |field| "#{@collection.model.table_name}.#{field}" }
|
77
|
+
@projection.relations.each do |relation, _fields|
|
78
|
+
relation_schema = @collection.fields[relation]
|
79
|
+
if relation_schema.type == 'OneToOne'
|
80
|
+
query_select.push("#{@collection.model.table_name}.#{relation_schema.origin_key_target}")
|
81
|
+
else
|
82
|
+
query_select.push("#{@collection.model.table_name}.#{relation_schema.foreign_key}")
|
83
|
+
end
|
84
|
+
end
|
85
|
+
|
86
|
+
@query = @query.select(query_select.join(', '))
|
87
|
+
@query = @query.eager_load(@projection.relations.keys.map(&:to_sym))
|
88
|
+
# TODO: replace eager_load by joins because eager_load select ALL columns of relation
|
89
|
+
# @query = @query.joins(@projection.relations.keys.map(&:to_sym))
|
90
|
+
end
|
91
|
+
|
92
|
+
@query
|
93
|
+
end
|
94
|
+
|
95
|
+
def add_join_relation(relation, relation_name)
|
96
|
+
if relation.type == 'ManyToMany'
|
97
|
+
# TODO: to implement
|
98
|
+
else
|
99
|
+
@query = @query.joins(relation_name.to_sym)
|
100
|
+
end
|
101
|
+
|
102
|
+
@query
|
103
|
+
end
|
104
|
+
|
105
|
+
def format_field(field)
|
106
|
+
if field.include?(':')
|
107
|
+
relation_name, field = field.split(':')
|
108
|
+
relation = @collection.fields[relation_name]
|
109
|
+
table_name = @collection.datasource.collection(relation.foreign_collection).model.table_name
|
110
|
+
add_join_relation(relation, relation_name)
|
111
|
+
return "#{table_name}.#{field}"
|
112
|
+
end
|
113
|
+
|
114
|
+
field
|
115
|
+
end
|
116
|
+
end
|
117
|
+
end
|
118
|
+
end
|
@@ -0,0 +1,11 @@
|
|
1
|
+
require_relative 'forest_admin_datasource_active_record/version'
|
2
|
+
# require 'forest_admin_datasource_toolkit'
|
3
|
+
require 'zeitwerk'
|
4
|
+
|
5
|
+
loader = Zeitwerk::Loader.for_gem
|
6
|
+
loader.setup
|
7
|
+
|
8
|
+
module ForestAdminDatasourceActiveRecord
|
9
|
+
class Error < StandardError; end
|
10
|
+
# Your code goes here...
|
11
|
+
end
|
metadata
ADDED
@@ -0,0 +1,93 @@
|
|
1
|
+
--- !ruby/object:Gem::Specification
|
2
|
+
name: forest_admin_datasource_active_record
|
3
|
+
version: !ruby/object:Gem::Version
|
4
|
+
version: 1.0.0.pre.beta.21
|
5
|
+
platform: ruby
|
6
|
+
authors:
|
7
|
+
- Matthieu
|
8
|
+
- Nicolas
|
9
|
+
autorequire:
|
10
|
+
bindir: exe
|
11
|
+
cert_chain: []
|
12
|
+
date: 2023-11-20 00:00:00.000000000 Z
|
13
|
+
dependencies:
|
14
|
+
- !ruby/object:Gem::Dependency
|
15
|
+
name: activerecord
|
16
|
+
requirement: !ruby/object:Gem::Requirement
|
17
|
+
requirements:
|
18
|
+
- - ">="
|
19
|
+
- !ruby/object:Gem::Version
|
20
|
+
version: '6.1'
|
21
|
+
type: :runtime
|
22
|
+
prerelease: false
|
23
|
+
version_requirements: !ruby/object:Gem::Requirement
|
24
|
+
requirements:
|
25
|
+
- - ">="
|
26
|
+
- !ruby/object:Gem::Version
|
27
|
+
version: '6.1'
|
28
|
+
- !ruby/object:Gem::Dependency
|
29
|
+
name: zeitwerk
|
30
|
+
requirement: !ruby/object:Gem::Requirement
|
31
|
+
requirements:
|
32
|
+
- - "~>"
|
33
|
+
- !ruby/object:Gem::Version
|
34
|
+
version: '2.3'
|
35
|
+
type: :runtime
|
36
|
+
prerelease: false
|
37
|
+
version_requirements: !ruby/object:Gem::Requirement
|
38
|
+
requirements:
|
39
|
+
- - "~>"
|
40
|
+
- !ruby/object:Gem::Version
|
41
|
+
version: '2.3'
|
42
|
+
description: |-
|
43
|
+
Forest is a modern admin interface that works on all major web frameworks. This gem makes Forest
|
44
|
+
admin work on any Ruby application.
|
45
|
+
email:
|
46
|
+
- matthv@gmail.com
|
47
|
+
- nicolasalexandre9@gmail.com
|
48
|
+
executables: []
|
49
|
+
extensions: []
|
50
|
+
extra_rdoc_files: []
|
51
|
+
files:
|
52
|
+
- ".rspec"
|
53
|
+
- README.md
|
54
|
+
- Rakefile
|
55
|
+
- forest_admin_datasource_active_record.gemspec
|
56
|
+
- lib/forest_admin_datasource_active_record.rb
|
57
|
+
- lib/forest_admin_datasource_active_record/collection.rb
|
58
|
+
- lib/forest_admin_datasource_active_record/datasource.rb
|
59
|
+
- lib/forest_admin_datasource_active_record/parser/column.rb
|
60
|
+
- lib/forest_admin_datasource_active_record/parser/relation.rb
|
61
|
+
- lib/forest_admin_datasource_active_record/parser/validation.rb
|
62
|
+
- lib/forest_admin_datasource_active_record/utils/query.rb
|
63
|
+
- lib/forest_admin_datasource_active_record/version.rb
|
64
|
+
- sig/forest_admin_datasource_active_record.rbs
|
65
|
+
- sig/forest_admin_datasource_active_record/collection.rbs
|
66
|
+
homepage: https://www.forestadmin.com
|
67
|
+
licenses:
|
68
|
+
- MIT
|
69
|
+
metadata:
|
70
|
+
homepage_uri: https://www.forestadmin.com
|
71
|
+
source_code_uri: https://github.com/ForestAdmin/agent-ruby
|
72
|
+
changelog_uri: https://github.com/ForestAdmin/agent-ruby/CHANGELOG.md
|
73
|
+
rubygems_mfa_required: 'false'
|
74
|
+
post_install_message:
|
75
|
+
rdoc_options: []
|
76
|
+
require_paths:
|
77
|
+
- lib
|
78
|
+
required_ruby_version: !ruby/object:Gem::Requirement
|
79
|
+
requirements:
|
80
|
+
- - ">="
|
81
|
+
- !ruby/object:Gem::Version
|
82
|
+
version: 3.0.0
|
83
|
+
required_rubygems_version: !ruby/object:Gem::Requirement
|
84
|
+
requirements:
|
85
|
+
- - ">"
|
86
|
+
- !ruby/object:Gem::Version
|
87
|
+
version: 1.3.1
|
88
|
+
requirements: []
|
89
|
+
rubygems_version: 3.3.5
|
90
|
+
signing_key:
|
91
|
+
specification_version: 4
|
92
|
+
summary: Ruby agent for Forest Admin.
|
93
|
+
test_files: []
|