rom 3.0.3 → 3.1.0

Sign up to get free protection for your applications and to get access to all the features.
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