rom 3.0.3 → 3.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.
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA1:
3
- metadata.gz: 21823f588a092ca791957a4d3bdb1ca3a78fed7e
4
- data.tar.gz: b779976efe0eb2d4ec2e7af273cd67782def6120
3
+ metadata.gz: 60084563a6e8cbda433df877fa5ceeaf6e342551
4
+ data.tar.gz: 9cf468362a1adbc5b64e10d4459af3c1b05d31d6
5
5
  SHA512:
6
- metadata.gz: e5f1e53320f6da430fed29b9c0619847c47b6e34808445852c362216a04a5354384af47b07b946410aa4fbd51c4925f753ef372c9d1485454c00337fc5ff973f
7
- data.tar.gz: 610cc7feec934fb6550b371218d5ebbc901417bfcd25b7d1d6709f8ef91684435b16b4d1da5d5e5405284ed14d0ad8a968a1e0f61e5d30582b17a0afea17d098
6
+ metadata.gz: bd60b6cc80d21d168fe472b4f6b52cd854e7afb368c930ae652ecb0306e9c3946d9d64d9dee7e7cdfa9e9fd66593ec8820f8ea49117cac1317c7e099f8a72a26
7
+ data.tar.gz: 5db668afb63edb340bd298314eede1a5cf33f6703e5777a5f9654e62eaba8789717147f322cabbdb7d911407ec66159f18d64191a03b1ccdd9d30e5fc1dd0206
data/CHANGELOG.md CHANGED
@@ -1,3 +1,15 @@
1
+ # v3.0.4 2017-03-01
2
+
3
+ ## Added
4
+
5
+ * New configuration DSL for configuring plugins (solnic)
6
+ * Instrumentation plugin for relations (solnic)
7
+ * New `ROM::Relation::Loaded#empty?` method (solnic)
8
+ * New `ROM::Relation::Graph#with_nodes` which returns a new graph with new nodes (solnic)
9
+ * New `ROM::Schema#empty` which returns an empty schema (solnic)
10
+
11
+ [Compare v3.0.3...v3.0.4](https://github.com/rom-rb/rom/compare/v3.0.3...v3.0.4)
12
+
1
13
  # v3.0.3 2017-02-24
2
14
 
3
15
  ## Fixed
data/Gemfile CHANGED
@@ -2,9 +2,6 @@ source 'https://rubygems.org'
2
2
 
3
3
  gemspec
4
4
 
5
- gem 'rom-mapper', git: 'https://github.com/rom-rb/rom-mapper.git', branch: 'master'
6
- gem 'transproc', git: 'https://github.com/solnic/transproc.git', branch: 'master'
7
-
8
5
  group :console do
9
6
  gem 'pry'
10
7
  gem 'pg', platforms: [:mri]
@@ -47,6 +44,7 @@ group :benchmarks do
47
44
  gem 'activerecord', '~> 5.0'
48
45
  gem 'benchmark-ips', '~> 2.2.0'
49
46
  gem 'rom-repository', git: 'https://github.com/rom-rb/rom-repository.git', branch: 'master'
47
+ gem 'hotch', platform: :mri
50
48
  end
51
49
 
52
50
  group :tools do
data/lib/rom.rb CHANGED
@@ -30,6 +30,7 @@ require 'rom/create_container'
30
30
  # register core plugins
31
31
  require 'rom/plugins/configuration/configuration_dsl'
32
32
  require 'rom/plugins/relation/registry_reader'
33
+ require 'rom/plugins/relation/instrumentation'
33
34
  require 'rom/plugins/command/schema'
34
35
 
35
36
  module ROM
@@ -38,6 +39,7 @@ module ROM
38
39
  plugins do
39
40
  register :macros, ROM::ConfigurationPlugins::ConfigurationDSL, type: :configuration
40
41
  register :registry_reader, ROM::Plugins::Relation::RegistryReader, type: :relation
42
+ register :instrumentation, ROM::Plugins::Relation::Instrumentation, type: :relation
41
43
  register :schema, ROM::Plugins::Command::Schema, type: :command
42
44
  end
43
45
  end
@@ -13,7 +13,7 @@ module ROM
13
13
 
14
14
  attr_reader :environment, :setup
15
15
 
16
- def_delegators :@setup, :register_relation, :register_command, :register_mapper,
16
+ def_delegators :@setup, :register_relation, :register_command, :register_mapper, :register_plugin,
17
17
  :relation_classes, :command_classes, :mapper_classes,
18
18
  :auto_registration
19
19
 
@@ -70,5 +70,38 @@ module ROM
70
70
  def commands(name, &block)
71
71
  register_command(*CommandDSL.new(name, default_adapter, &block).command_classes)
72
72
  end
73
+
74
+ # Configures a plugin for a specific adapter to be enabled for all relations
75
+ #
76
+ # @example
77
+ # config = ROM::Configuration.new(:sql, 'sqlite::memory')
78
+ #
79
+ # config.plugin(:sql, relations: :instrumentation) do |p|
80
+ # p.notifications = MyNotificationsBackend
81
+ # end
82
+ #
83
+ # config.plugin(:sql, relations: :pagination)
84
+ #
85
+ # @param [Symbol] adapter The adapter identifier
86
+ # @param [Hash<Symbol=>Symbol>] spec Component identifier => plugin identifier
87
+ #
88
+ # @return [Plugin]
89
+ #
90
+ # @api public
91
+ def plugin(adapter, spec, &block)
92
+ type, name = spec.flatten(1)
93
+ plugin = plugin_registry.send(type).adapter(adapter).fetch(name) { plugin_registry.send(type).fetch(name) }
94
+
95
+ if block
96
+ register_plugin(plugin.configure(&block))
97
+ else
98
+ register_plugin(plugin)
99
+ end
100
+ end
101
+
102
+ # @api private
103
+ def plugin_registry
104
+ ROM.plugin_registry
105
+ end
73
106
  end
74
107
  end
@@ -27,6 +27,7 @@ module ROM
27
27
  relation_classes: setup.relation_classes,
28
28
  command_classes: setup.command_classes,
29
29
  mappers: setup.mapper_classes,
30
+ plugins: setup.plugins,
30
31
  config: environment.config.dup.freeze
31
32
  )
32
33
 
data/lib/rom/plugin.rb CHANGED
@@ -1,10 +1,13 @@
1
1
  require 'rom/plugin_base'
2
+ require 'rom/support/configurable'
2
3
 
3
4
  module ROM
4
5
  # Plugin is a simple object used to store plugin configurations
5
6
  #
6
7
  # @private
7
8
  class Plugin < PluginBase
9
+ include Configurable
10
+
8
11
  # Apply this plugin to the provided class
9
12
  #
10
13
  # @param [Class] klass
@@ -13,10 +13,19 @@ module ROM
13
13
  # @api private
14
14
  attr_reader :options
15
15
 
16
+ # @api private
17
+ attr_reader :type
18
+
16
19
  # @api private
17
20
  def initialize(mod, options)
18
21
  @mod = mod
19
22
  @options = options
23
+ @type = options.fetch(:type)
24
+ end
25
+
26
+ # @api private
27
+ def relation?
28
+ type == :relation
20
29
  end
21
30
 
22
31
  # Apply this plugin to the provided class
@@ -0,0 +1,43 @@
1
+ module ROM
2
+ module Plugins
3
+ module Relation
4
+ # Experimental plugin for configuring relations with an external
5
+ # instrumentation system like dry-monitor or ActiveSupport::Notifications
6
+ #
7
+ # @api public
8
+ module Instrumentation
9
+ # This hooks sets up a relation class with injectible notifications object
10
+ #
11
+ # @api private
12
+ def self.included(klass)
13
+ super
14
+ klass.option :notifications, reader: true
15
+ klass.extend(ClassInterface)
16
+ klass.instrument(:to_a)
17
+ end
18
+
19
+ module ClassInterface
20
+ def instrument(*methods)
21
+ methods.each do |meth|
22
+ define_method(meth) do
23
+ instrument { super() }
24
+ end
25
+ end
26
+ end
27
+ end
28
+
29
+ # @api public
30
+ def instrument(&block)
31
+ notifications.instrument(self.class.adapter, { name: name.relation }.merge(notification_payload(self)), &block)
32
+ end
33
+
34
+ private
35
+
36
+ # @api private
37
+ def notification_payload(relation)
38
+ EMPTY_HASH
39
+ end
40
+ end
41
+ end
42
+ end
43
+ end
@@ -61,6 +61,11 @@ module ROM
61
61
  @nodes = nodes
62
62
  end
63
63
 
64
+ # @api public
65
+ def with_nodes(nodes)
66
+ self.class.new(root, nodes)
67
+ end
68
+
64
69
  # Return if this is a graph relation
65
70
  #
66
71
  # @return [true]
@@ -90,10 +95,10 @@ module ROM
90
95
  left = root.call(*args)
91
96
 
92
97
  right =
93
- if left.count > 0
94
- nodes.map { |node| node.call(left) }
98
+ if left.empty?
99
+ nodes.map { |node| Loaded.new(node, EMPTY_ARRAY) }
95
100
  else
96
- nodes.map { |node| Loaded.new(node, []) }
101
+ nodes.map { |node| node.call(left) }
97
102
  end
98
103
 
99
104
  Loaded.new(self, [left, right])
@@ -107,6 +107,15 @@ module ROM
107
107
  pluck(source.primary_key)
108
108
  end
109
109
 
110
+ # Return if loaded relation is empty
111
+ #
112
+ # @return [TrueClass,FalseClass]
113
+ #
114
+ # @api public
115
+ def empty?
116
+ collection.empty?
117
+ end
118
+
110
119
  # Return a loaded relation with a new collection
111
120
  #
112
121
  # @api public
data/lib/rom/setup.rb CHANGED
@@ -17,11 +17,16 @@ module ROM
17
17
  # @api private
18
18
  attr_reader :command_classes
19
19
 
20
+
21
+ # @api private
22
+ attr_reader :plugins
23
+
20
24
  # @api private
21
25
  def initialize
22
26
  @relation_classes = []
23
27
  @command_classes = []
24
28
  @mapper_classes = []
29
+ @plugins = []
25
30
  end
26
31
 
27
32
  # Relation sub-classes are being registered with this method during setup
@@ -45,6 +50,11 @@ module ROM
45
50
  klasses.reduce(@command_classes, :<<)
46
51
  end
47
52
 
53
+ # @api private
54
+ def register_plugin(plugin)
55
+ plugins << plugin
56
+ end
57
+
48
58
  def auto_registration(directory, options = {})
49
59
  auto_registration = AutoRegistration.new(directory, options)
50
60
  auto_registration.relations.map { |r| register_relation(r) }
@@ -21,7 +21,7 @@ module ROM
21
21
  # @private
22
22
  class Finalize
23
23
  attr_reader :gateways, :repo_adapter, :datasets, :gateway_map,
24
- :relation_classes, :mapper_classes, :mapper_objects, :command_classes, :config
24
+ :relation_classes, :mapper_classes, :mapper_objects, :command_classes, :plugins, :config
25
25
 
26
26
  # @api private
27
27
  def initialize(options)
@@ -37,6 +37,8 @@ module ROM
37
37
 
38
38
  @config = options.fetch(:config)
39
39
 
40
+ @plugins = options.fetch(:plugins)
41
+
40
42
  initialize_datasets
41
43
  end
42
44
 
@@ -90,7 +92,7 @@ module ROM
90
92
  #
91
93
  # @api private
92
94
  def load_relations
93
- FinalizeRelations.new(gateways, relation_classes).run!
95
+ FinalizeRelations.new(gateways, relation_classes, plugins.select(&:relation?)).run!
94
96
  end
95
97
 
96
98
  # @api private
@@ -11,9 +11,10 @@ module ROM
11
11
  # @param [Array] relation_classes a list of relation descendants
12
12
  #
13
13
  # @api private
14
- def initialize(gateways, relation_classes)
14
+ def initialize(gateways, relation_classes, plugins = EMPTY_ARRAY)
15
15
  @gateways = gateways
16
16
  @relation_classes = relation_classes
17
+ @plugins = plugins
17
18
  end
18
19
 
19
20
  # @return [Hash]
@@ -53,9 +54,20 @@ module ROM
53
54
  klass.schema(infer: true) unless klass.schema
54
55
  schema = klass.schema.finalize!(gateway: gateway, relations: registry)
55
56
 
57
+ @plugins.each do |plugin|
58
+ plugin.apply_to(klass)
59
+ end
60
+
56
61
  dataset = gateway.dataset(klass.dataset).instance_exec(klass, &ds_proc)
57
62
 
58
- klass.new(dataset, __registry__: registry, schema: schema.with(relations: registry))
63
+ options = { __registry__: registry, schema: schema.with(relations: registry), **plugin_options }
64
+
65
+ klass.new(dataset, options)
66
+ end
67
+
68
+ # @api private
69
+ def plugin_options
70
+ @plugins.map(&:config).map(&:to_hash).reduce(:merge) || EMPTY_HASH
59
71
  end
60
72
  end
61
73
  end
@@ -21,6 +21,10 @@ module ROM
21
21
  settings.key?(name)
22
22
  end
23
23
 
24
+ def to_hash
25
+ settings
26
+ end
27
+
24
28
  # @api private
25
29
  def freeze
26
30
  settings.each_value(&:freeze)
@@ -31,13 +35,13 @@ module ROM
31
35
  def respond_to_missing?(_name, _include_private = false)
32
36
  true
33
37
  end
34
-
38
+
35
39
  def dup
36
40
  self.class.new(dup_settings(settings))
37
41
  end
38
-
42
+
39
43
  private
40
-
44
+
41
45
  def dup_settings(settings)
42
46
  settings.each_with_object({}) do |(key, value), new_settings|
43
47
  if value.is_a?(self.class)
@@ -75,6 +79,7 @@ module ROM
75
79
  # @api public
76
80
  def configure
77
81
  yield(config)
82
+ self
78
83
  end
79
84
  end
80
85
  end
data/lib/rom/version.rb CHANGED
@@ -1,3 +1,3 @@
1
1
  module ROM
2
- VERSION = '3.0.3'.freeze
2
+ VERSION = '3.1.0'.freeze
3
3
  end
@@ -1,4 +1,5 @@
1
1
  require 'spec_helper'
2
+ require 'rom/plugins/relation/key_inference'
2
3
  require 'dry-struct'
3
4
 
4
5
  RSpec.describe 'Configuring ROM' do
@@ -163,4 +164,27 @@ RSpec.describe 'Configuring ROM' do
163
164
  ROM.container(:memory) { |c| c.register_relation(Test::UserRelation) }
164
165
  end
165
166
  end
167
+
168
+ describe 'configuring plugins for all relations' do
169
+ it 'allows setting instrumentation for relations' do
170
+ Test::Notifications = double(:notifications)
171
+
172
+ configuration = ROM::Configuration.new(:memory)
173
+
174
+ configuration.plugin(:memory, relations: :instrumentation) do |p|
175
+ p.notifications = Test::Notifications
176
+ end
177
+
178
+ configuration.plugin(:memory, relations: :key_inference)
179
+
180
+ configuration.relation(:users)
181
+
182
+ container = ROM.container(configuration)
183
+
184
+ users = container.relations[:users]
185
+
186
+ expect(users.notifications).to be(Test::Notifications)
187
+ expect(users).to respond_to(:foreign_key)
188
+ end
189
+ end
166
190
  end
@@ -0,0 +1,40 @@
1
+ require 'rom'
2
+ require 'rom/memory'
3
+
4
+ RSpec.describe ROM::Plugins::Relation::Instrumentation do
5
+ subject(:relation) do
6
+ relation_class.new(dataset, notifications: notifications)
7
+ end
8
+
9
+ let(:dataset) do
10
+ double(:dataset)
11
+ end
12
+
13
+ let(:relation_class) do
14
+ Class.new(ROM::Memory::Relation) do
15
+ schema(:users) do
16
+ attribute :name, ROM::Types::String
17
+ end
18
+
19
+ use :instrumentation
20
+
21
+ instrument def all
22
+ self
23
+ end
24
+ end
25
+ end
26
+
27
+ let(:notifications) { spy(:notifications) }
28
+
29
+ it 'uses notifications API when materializing a relation' do
30
+ relation.to_a
31
+
32
+ expect(notifications).to have_received(:instrument).with(:memory, name: :users)
33
+ end
34
+
35
+ it 'instruments custom methods' do
36
+ relation.all
37
+
38
+ expect(notifications).to have_received(:instrument).with(:memory, name: :users)
39
+ end
40
+ end
@@ -17,6 +17,10 @@ RSpec.describe ROM::Relation::Graph do
17
17
  def for_users(_users)
18
18
  self
19
19
  end
20
+
21
+ def by_title(title)
22
+ restrict(title: title)
23
+ end
20
24
  end.new(tasks_dataset)
21
25
  end
22
26
 
@@ -64,6 +68,15 @@ RSpec.describe ROM::Relation::Graph do
64
68
  end
65
69
  end
66
70
 
71
+ describe '#with_nodes' do
72
+ it 'returns a new graph with new nodes' do
73
+ new_tasks = tasks_relation.by_title('foo')
74
+ new_graph = graph.with_nodes([new_tasks])
75
+
76
+ expect(new_graph.nodes[0]).to be(new_tasks)
77
+ end
78
+ end
79
+
67
80
  describe '#call' do
68
81
  it 'materializes relations' do
69
82
  expect(graph.call).to match_array([
metadata CHANGED
@@ -1,14 +1,14 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: rom
3
3
  version: !ruby/object:Gem::Version
4
- version: 3.0.3
4
+ version: 3.1.0
5
5
  platform: ruby
6
6
  authors:
7
7
  - Piotr Solnica
8
8
  autorequire:
9
9
  bindir: bin
10
10
  cert_chain: []
11
- date: 2017-02-24 00:00:00.000000000 Z
11
+ date: 2017-03-01 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: concurrent-ruby
@@ -219,6 +219,7 @@ files:
219
219
  - lib/rom/plugin_registry.rb
220
220
  - lib/rom/plugins/command/schema.rb
221
221
  - lib/rom/plugins/configuration/configuration_dsl.rb
222
+ - lib/rom/plugins/relation/instrumentation.rb
222
223
  - lib/rom/plugins/relation/key_inference.rb
223
224
  - lib/rom/plugins/relation/registry_reader.rb
224
225
  - lib/rom/registry.rb
@@ -349,6 +350,7 @@ files:
349
350
  - spec/unit/rom/memory/storage_spec.rb
350
351
  - spec/unit/rom/plugin_spec.rb
351
352
  - spec/unit/rom/plugins/command/schema_spec.rb
353
+ - spec/unit/rom/plugins/relation/instrumentation_spec.rb
352
354
  - spec/unit/rom/plugins/relation/key_inference_spec.rb
353
355
  - spec/unit/rom/registry_spec.rb
354
356
  - spec/unit/rom/relation/attribute_reader_spec.rb