forest_admin_datasource_customizer 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 ADDED
@@ -0,0 +1,7 @@
1
+ ---
2
+ SHA256:
3
+ metadata.gz: a57e523c5b7c53d6a40452a6f9ea21a7a1b858ab1a39a3f5cac3fe83ec93ec27
4
+ data.tar.gz: a6cf096f51d0ed6c12fa9787e69b74c75663f1eba63981822369ba41a73bd621
5
+ SHA512:
6
+ metadata.gz: ba6b15e85b6c7bd38cf9a32b26824b52c8078a33b0001add15942a042c8d5afd8fd4d7a3e21d742cdce700338a62f1a0b928d3d40ee8af171ae92acad415d02f
7
+ data.tar.gz: 9deebebbc665d471aa7450e23df9bd12df12d0333cca89aefa1cd3cabf710a755c0821ee944d41daa8ee1437b31ce5ddb90256f53b8be8f55cd1b819618a6b2d
data/.rspec ADDED
@@ -0,0 +1,3 @@
1
+ --format documentation
2
+ --color
3
+ --require spec_helper
data/README.md ADDED
@@ -0,0 +1,31 @@
1
+ # ForestAdminDatasourceCustomizer
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_customizer`. 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_customizer.
data/Rakefile ADDED
@@ -0,0 +1,10 @@
1
+ require "bundler/gem_tasks"
2
+ require "rspec/core/rake_task"
3
+
4
+ RSpec::Core::RakeTask.new(:spec)
5
+
6
+ require "rubocop/rake_task"
7
+
8
+ RuboCop::RakeTask.new
9
+
10
+ task default: %i[spec rubocop]
@@ -0,0 +1,38 @@
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_customizer/version"
5
+
6
+ Gem::Specification.new do |spec|
7
+ spec.name = "forest_admin_datasource_customizer"
8
+ spec.version = ForestAdminDatasourceCustomizer::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/ .git Gemfile LICENSE])
29
+ end
30
+ end
31
+
32
+ spec.bindir = "exe"
33
+ spec.executables = spec.files.grep(%r{\Aexe/}) { |f| File.basename(f) }
34
+ spec.require_paths = ["lib"]
35
+
36
+ spec.add_dependency "activesupport", ">= 6.1"
37
+ spec.add_dependency "zeitwerk", "~> 2.3"
38
+ end
@@ -0,0 +1,25 @@
1
+ module ForestAdminDatasourceCustomizer
2
+ class CollectionCustomizer
3
+ attr_reader :datasource_customizer, :stack, :name
4
+
5
+ def initialize(datasource_customizer, stack, name)
6
+ @datasource_customizer = datasource_customizer
7
+ @stack = stack
8
+ @name = name
9
+ end
10
+
11
+ def schema
12
+ @stack.datasource.get_collection(@name).schema
13
+ end
14
+
15
+ def collection
16
+ @stack.datasource.get_collection(@name)
17
+ end
18
+
19
+ def use(plugin, options = [])
20
+ push_customization(
21
+ -> { plugin.run(@datasource_customizer, self, options) }
22
+ )
23
+ end
24
+ end
25
+ end
@@ -0,0 +1,51 @@
1
+ module ForestAdminDatasourceCustomizer
2
+ class DatasourceCustomizer
3
+ attr_reader :stack
4
+
5
+ def initialize(_db_config = {})
6
+ @composite_datasource = ForestAdminDatasourceToolkit::Datasource.new
7
+ @stack = Decorators::DecoratorsStack.new(@composite_datasource)
8
+ end
9
+
10
+ def schema
11
+ @stack.datasource.schema
12
+ end
13
+
14
+ def get_collection(name)
15
+ CollectionCustomizer.new(self, @stack, name)
16
+ end
17
+
18
+ def collections
19
+ @stack.datasource.collections.transform_values { |collection| get_collection(collection.name) }
20
+ end
21
+
22
+ def datasource
23
+ # TODO: call @stack.apply_queued_customizations(logger);
24
+
25
+ @stack.datasource
26
+ end
27
+
28
+ def add_datasource(datasource, _options)
29
+ # TODO: add include/exclude behavior
30
+ # TODO: add rename behavior
31
+
32
+ datasource.collections.each_value { |collection| @composite_datasource.add_collection(collection) }
33
+ end
34
+
35
+ def add_chart(name, definition)
36
+ # TODO: to implement
37
+ end
38
+
39
+ def use(plugin, options)
40
+ # TODO: to implement
41
+ end
42
+
43
+ def customize_collection(name, handle)
44
+ # TODO: to implement
45
+ end
46
+
47
+ def remove_collection(names)
48
+ # TODO: to implement
49
+ end
50
+ end
51
+ end
@@ -0,0 +1,34 @@
1
+ module ForestAdminDatasourceCustomizer
2
+ module Decorators
3
+ class DecoratorsStack
4
+ include ForestAdminDatasourceToolkit::Decorators
5
+
6
+ attr_reader :datasource, :empty
7
+
8
+ def initialize(datasource)
9
+ last = datasource
10
+ last = @empty = DatasourceDecorator.new(last, Empty::EmptyCollectionDecorator)
11
+ @datasource = last
12
+ end
13
+
14
+ def queue_customization(customization)
15
+ @customizations << customization
16
+ end
17
+
18
+ # Apply all customizations
19
+ # Plugins may queue new customizations, or call other plugins which will queue customizations.
20
+ #
21
+ # This method will be called recursively and clears the queue at each recursion to ensure
22
+ # that all customizations are applied in the right order.
23
+ def apply_queued_customizations(logger)
24
+ queued_customizations = @customizations.pop
25
+ @customizations = []
26
+
27
+ while queued_customizations.length.positive?
28
+ queued_customizations.shift.call(logger)
29
+ apply_queued_customizations(logger)
30
+ end
31
+ end
32
+ end
33
+ end
34
+ end
@@ -0,0 +1,82 @@
1
+ module ForestAdminDatasourceCustomizer
2
+ module Decorators
3
+ module Empty
4
+ class EmptyCollectionDecorator < ForestAdminDatasourceToolkit::Decorators::CollectionDecorator
5
+ include ForestAdminDatasourceToolkit::Decorators
6
+ include ForestAdminDatasourceToolkit::Components::Query::ConditionTree
7
+
8
+ def list(_caller, filter, _projection)
9
+ return super unless return_empty_set(filter.condition_tree)
10
+
11
+ []
12
+ end
13
+
14
+ def update(caller, filter, patch)
15
+ super unless return_empty_set(filter.condition_tree)
16
+ end
17
+
18
+ def delete(caller, filter)
19
+ super unless return_empty_set(filter.condition_tree)
20
+ end
21
+
22
+ def aggregate(caller, filter, aggregation, limit = nil)
23
+ return super unless return_empty_set(filter.condition_tree)
24
+
25
+ []
26
+ end
27
+
28
+ private
29
+
30
+ def return_empty_set(tree)
31
+ return leaf_return_empty_set(tree) if tree.is_a? Nodes::ConditionTreeLeaf
32
+
33
+ if tree.is_a?(Nodes::ConditionTreeBranch) && tree.aggregator == 'Or'
34
+ return or_return_empty_set(tree.conditions)
35
+ end
36
+
37
+ if tree.is_a?(Nodes::ConditionTreeBranch) && tree.aggregator == 'And'
38
+ return and_return_empty_set(tree.conditions)
39
+ end
40
+
41
+ false
42
+ end
43
+
44
+ def leaf_return_empty_set(leaf)
45
+ # Empty 'in` always return zero records.
46
+ leaf.operator == Operators::IN && leaf.value.empty?
47
+ end
48
+
49
+ def or_return_empty_set(conditions)
50
+ # Or return no records when
51
+ # - they have no conditions
52
+ # - they have only conditions which return zero records.
53
+ conditions.empty? || conditions.all? { |condition| return_empty_set(condition) }
54
+ end
55
+
56
+ def and_return_empty_set(conditions)
57
+ # There is a leaf which returns zero records
58
+ return true if conditions.one? { |condition| return_empty_set(condition) }
59
+
60
+ # Scans for mutually exclusive conditions
61
+ # (this a naive implementation, it will miss many occurrences)
62
+ values_by_field = {}
63
+ leafs = conditions.select { |condition| condition.is_a? Nodes::ConditionTreeLeaf }
64
+ leafs.each do |leaf|
65
+ field, operator, value = leaf.to_h.values_at(:field, :operator, :value)
66
+ if !values_by_field.key?(field) && operator == Operators::EQUAL
67
+ values_by_field[field] = [value]
68
+ elsif !values_by_field.key?(field) && operator == Operators::IN
69
+ values_by_field[field] = value
70
+ elsif values_by_field.key?(field) && operator == Operators::EQUAL
71
+ values_by_field[field] = values_by_field[field].include?(value) ? [value] : []
72
+ elsif values_by_field.key?(field) && operator == Operators::IN
73
+ values_by_field[field] = values_by_field[field].select { |v| value.include?(v) }
74
+ end
75
+ end
76
+
77
+ values_by_field.values.one?(&:empty?)
78
+ end
79
+ end
80
+ end
81
+ end
82
+ end
@@ -0,0 +1,9 @@
1
+ module ForestAdminDatasourceCustomizer
2
+ module Plugins
3
+ class Plugin
4
+ def run(_datasource_customizer, _collection_customizer = nil, _options = [])
5
+ raise NotImplementedError, "#{self.class} has not implemented method '#{__method__}'"
6
+ end
7
+ end
8
+ end
9
+ end
@@ -0,0 +1,3 @@
1
+ module ForestAdminDatasourceCustomizer
2
+ VERSION = "1.0.0-beta.21"
3
+ end
@@ -0,0 +1,10 @@
1
+ require_relative 'forest_admin_datasource_customizer/version'
2
+ require 'zeitwerk'
3
+
4
+ loader = Zeitwerk::Loader.for_gem
5
+ loader.setup
6
+
7
+ module ForestAdminDatasourceCustomizer
8
+ class Error < StandardError; end
9
+ # Your code goes here...
10
+ end
@@ -0,0 +1,4 @@
1
+ module ForestAdminDatasourceCustomizer
2
+ VERSION: String
3
+ # See the writing guide of rbs: https://github.com/ruby/rbs#guides
4
+ end
metadata ADDED
@@ -0,0 +1,91 @@
1
+ --- !ruby/object:Gem::Specification
2
+ name: forest_admin_datasource_customizer
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-12-18 00:00:00.000000000 Z
13
+ dependencies:
14
+ - !ruby/object:Gem::Dependency
15
+ name: activesupport
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_customizer.gemspec
56
+ - lib/forest_admin_datasource_customizer.rb
57
+ - lib/forest_admin_datasource_customizer/collection_customizer.rb
58
+ - lib/forest_admin_datasource_customizer/datasource_customizer.rb
59
+ - lib/forest_admin_datasource_customizer/decorators/decorators_stack.rb
60
+ - lib/forest_admin_datasource_customizer/decorators/empty/empty_collection_decorator.rb
61
+ - lib/forest_admin_datasource_customizer/plugins/plugin.rb
62
+ - lib/forest_admin_datasource_customizer/version.rb
63
+ - sig/forest_admin_datasource_customizer.rbs
64
+ homepage: https://www.forestadmin.com
65
+ licenses:
66
+ - MIT
67
+ metadata:
68
+ homepage_uri: https://www.forestadmin.com
69
+ source_code_uri: https://github.com/ForestAdmin/agent-ruby
70
+ changelog_uri: https://github.com/ForestAdmin/agent-ruby/CHANGELOG.md
71
+ rubygems_mfa_required: 'false'
72
+ post_install_message:
73
+ rdoc_options: []
74
+ require_paths:
75
+ - lib
76
+ required_ruby_version: !ruby/object:Gem::Requirement
77
+ requirements:
78
+ - - ">="
79
+ - !ruby/object:Gem::Version
80
+ version: 3.0.0
81
+ required_rubygems_version: !ruby/object:Gem::Requirement
82
+ requirements:
83
+ - - ">"
84
+ - !ruby/object:Gem::Version
85
+ version: 1.3.1
86
+ requirements: []
87
+ rubygems_version: 3.3.5
88
+ signing_key:
89
+ specification_version: 4
90
+ summary: Ruby agent for Forest Admin.
91
+ test_files: []