rom-support 0.1.0 → 1.0.0.beta1
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 +4 -4
- data/.travis.yml +1 -1
- data/CHANGELOG.md +7 -1
- data/Gemfile +1 -0
- data/lib/rom-support.rb +1 -0
- data/lib/rom/support/auto_curry.rb +24 -10
- data/lib/rom/support/class_builder.rb +2 -2
- data/lib/rom/support/data_proxy.rb +1 -3
- data/lib/rom/support/options.rb +96 -45
- data/lib/rom/support/registry.rb +1 -1
- data/lib/rom/support/version.rb +1 -1
- data/rom-support.gemspec +2 -0
- data/spec/unit/rom/support/auto_curry_spec.rb +53 -0
- data/spec/unit/rom/support/options_spec.rb +16 -0
- metadata +34 -5
- data/lib/rom/support/guarded_inheritance_hook.rb +0 -21
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA1:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: 79e147630a7e44dd323b446498ff1e39942b9c17
|
4
|
+
data.tar.gz: 27b5f1b27100413d380b2e1d8e0a389ce4bf6443
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: 680c23f519ead5ad0db2e0951f38a25eedbe63e8cb7d2cfc1dafdf56faed15361f60c2ac5b48b56b32805be68eb89caba06827e30500d9f6e796e6b6a4c5ddce
|
7
|
+
data.tar.gz: 3957e00c4f16d4bee3610772e246a630c01526dd8717010ef76489626974e45bed4b801011cbf1865c012b5ded39327ca58aca7fec19c3a3b39d0f5f1c198c9b
|
data/.travis.yml
CHANGED
data/CHANGELOG.md
CHANGED
data/Gemfile
CHANGED
data/lib/rom-support.rb
CHANGED
@@ -1,27 +1,41 @@
|
|
1
1
|
module ROM
|
2
2
|
module AutoCurry
|
3
3
|
def self.extended(klass)
|
4
|
-
busy = false
|
5
|
-
|
6
4
|
klass.define_singleton_method(:method_added) do |name|
|
7
|
-
return if
|
8
|
-
|
9
|
-
auto_curry(name)
|
10
|
-
busy = false
|
5
|
+
return if auto_curry_busy?
|
6
|
+
auto_curry_guard { auto_curry(name) }
|
11
7
|
super(name)
|
12
8
|
end
|
13
9
|
end
|
14
10
|
|
15
|
-
def
|
11
|
+
def auto_curry_guard
|
12
|
+
@__auto_curry_busy__ = true
|
13
|
+
yield
|
14
|
+
ensure
|
15
|
+
@__auto_curry_busy__ = false
|
16
|
+
end
|
17
|
+
|
18
|
+
def auto_curry_busy?
|
19
|
+
@__auto_curry_busy__ ||= false
|
20
|
+
end
|
21
|
+
|
22
|
+
def auto_curry(name, &block)
|
16
23
|
curried = self.curried
|
17
24
|
meth = instance_method(name)
|
18
25
|
arity = meth.arity
|
19
26
|
|
20
27
|
define_method(name) do |*args|
|
21
|
-
|
22
|
-
|
28
|
+
response =
|
29
|
+
if arity < 0 || arity == args.size
|
30
|
+
meth.bind(self).(*args)
|
31
|
+
else
|
32
|
+
curried.new(self, name: name, curry_args: args, arity: arity)
|
33
|
+
end
|
34
|
+
|
35
|
+
if block
|
36
|
+
response.instance_exec(&block)
|
23
37
|
else
|
24
|
-
|
38
|
+
response
|
25
39
|
end
|
26
40
|
end
|
27
41
|
end
|
@@ -8,12 +8,12 @@ module ROM
|
|
8
8
|
include Options
|
9
9
|
|
10
10
|
option :name, type: String, reader: true
|
11
|
-
option :parent, type: Class, reader: true,
|
11
|
+
option :parent, type: Class, reader: true, default: Object
|
12
12
|
|
13
13
|
# Generate a class based on options
|
14
14
|
#
|
15
15
|
# @example
|
16
|
-
# builder = ROM::
|
16
|
+
# builder = ROM::ClassBuilder.new(name: 'MyClass')
|
17
17
|
#
|
18
18
|
# klass = builder.call
|
19
19
|
# klass.name # => "MyClass"
|
@@ -1,5 +1,3 @@
|
|
1
|
-
require 'equalizer'
|
2
|
-
|
3
1
|
module ROM
|
4
2
|
# Helper module for dataset classes
|
5
3
|
#
|
@@ -33,7 +31,7 @@ module ROM
|
|
33
31
|
klass.class_eval do
|
34
32
|
extend ClassMethods
|
35
33
|
|
36
|
-
include Equalizer
|
34
|
+
include Dry::Equalizer(:data)
|
37
35
|
|
38
36
|
option :row_proc, reader: true, default: proc { |obj| obj.class.row_proc }
|
39
37
|
end
|
data/lib/rom/support/options.rb
CHANGED
@@ -1,3 +1,5 @@
|
|
1
|
+
require 'transproc'
|
2
|
+
|
1
3
|
module ROM
|
2
4
|
# Helper module for classes with a constructor accepting option hash
|
3
5
|
#
|
@@ -40,39 +42,59 @@ module ROM
|
|
40
42
|
#
|
41
43
|
# @api private
|
42
44
|
class Option
|
43
|
-
attr_reader :name, :
|
45
|
+
attr_reader :name, :reader
|
44
46
|
|
45
47
|
def initialize(name, options = {})
|
46
|
-
@name
|
47
|
-
@type = options.fetch(:type) { Object }
|
48
|
+
@name = name
|
48
49
|
@reader = options.fetch(:reader) { false }
|
49
|
-
|
50
|
-
|
51
|
-
|
50
|
+
# Prepares transformations applied by [#transform]
|
51
|
+
add_coercer options[:coercer]
|
52
|
+
add_default options[:default] if options.key? :default
|
53
|
+
add_type_checker options[:type]
|
54
|
+
add_value_checker options[:allow]
|
55
|
+
add_reader if reader
|
56
|
+
end
|
57
|
+
|
58
|
+
# Takes options of some object, applies current transformations
|
59
|
+
# and returns updated options
|
60
|
+
#
|
61
|
+
# @param [Object] object
|
62
|
+
# @param [Hash] options
|
63
|
+
#
|
64
|
+
# @return [Hash] options
|
65
|
+
#
|
66
|
+
def transform(object, options)
|
67
|
+
transformers.inject(options) { |a, e| e[object, a] }
|
52
68
|
end
|
53
69
|
|
54
|
-
|
55
|
-
|
70
|
+
private
|
71
|
+
|
72
|
+
def transformers
|
73
|
+
@transformers ||= []
|
56
74
|
end
|
57
75
|
|
58
|
-
def
|
59
|
-
|
76
|
+
def add_reader
|
77
|
+
transformers << Transformers[:reader_assigner, name]
|
60
78
|
end
|
61
79
|
|
62
|
-
def
|
63
|
-
|
80
|
+
def add_default(value)
|
81
|
+
transformer = value.respond_to?(:call) ? :default_proc : :default_value
|
82
|
+
transformers << Transformers[transformer, name, value]
|
64
83
|
end
|
65
84
|
|
66
|
-
def
|
67
|
-
|
85
|
+
def add_coercer(fn)
|
86
|
+
return unless fn.is_a?(Proc)
|
87
|
+
transformers << Transformers[:coercer, name, fn]
|
68
88
|
end
|
69
89
|
|
70
|
-
def
|
71
|
-
|
90
|
+
def add_type_checker(type)
|
91
|
+
return unless type.is_a?(Class)
|
92
|
+
transformers << Transformers[:type_checker, name, type]
|
72
93
|
end
|
73
94
|
|
74
|
-
def
|
75
|
-
|
95
|
+
def add_value_checker(values)
|
96
|
+
return unless values.respond_to?(:include?)
|
97
|
+
transformers << Transformers[:value_checker, name, values]
|
76
98
|
end
|
77
99
|
end
|
78
100
|
|
@@ -95,18 +117,7 @@ module ROM
|
|
95
117
|
|
96
118
|
def process(object, options)
|
97
119
|
ensure_known_options(options)
|
98
|
-
|
99
|
-
each do |name, option|
|
100
|
-
if option.default? && !options.key?(name)
|
101
|
-
options[name] = option.default_value(object)
|
102
|
-
end
|
103
|
-
|
104
|
-
if options.key?(name)
|
105
|
-
validate_option_value(option, name, options[name])
|
106
|
-
end
|
107
|
-
|
108
|
-
option.assign_reader_value(object, options[name]) if option.reader?
|
109
|
-
end
|
120
|
+
each { |_, option| options.update option.transform(object, options) }
|
110
121
|
end
|
111
122
|
|
112
123
|
def names
|
@@ -122,23 +133,10 @@ module ROM
|
|
122
133
|
def ensure_known_options(options)
|
123
134
|
options.each_key do |name|
|
124
135
|
@options.fetch(name) do
|
125
|
-
|
126
|
-
"#{name.inspect} is not a valid option"
|
136
|
+
fail InvalidOptionKeyError, "#{name.inspect} is not a valid option"
|
127
137
|
end
|
128
138
|
end
|
129
139
|
end
|
130
|
-
|
131
|
-
def validate_option_value(option, name, value)
|
132
|
-
unless option.type_matches?(value)
|
133
|
-
raise InvalidOptionValueError,
|
134
|
-
"#{name.inspect}:#{value.inspect} has incorrect type"
|
135
|
-
end
|
136
|
-
|
137
|
-
unless option.allow?(value)
|
138
|
-
raise InvalidOptionValueError,
|
139
|
-
"#{name.inspect}:#{value.inspect} has incorrect value"
|
140
|
-
end
|
141
|
-
end
|
142
140
|
end
|
143
141
|
|
144
142
|
# @api private
|
@@ -159,12 +157,13 @@ module ROM
|
|
159
157
|
# @option settings [Boolean] :reader Define a reader? Default: +false+
|
160
158
|
# @option settings [Array] :allow Allow certain values. Default: Allow anything
|
161
159
|
# @option settings [Object] :default Set default value for missing option
|
160
|
+
# @option settings [Proc] :coercer Set coercer for assigned option
|
162
161
|
#
|
163
162
|
# @api public
|
164
163
|
def option(name, settings = {})
|
165
164
|
option = Option.new(name, settings)
|
166
165
|
option_definitions.define(option)
|
167
|
-
attr_reader(name) if option.reader
|
166
|
+
attr_reader(name) if option.reader
|
168
167
|
end
|
169
168
|
|
170
169
|
# @api private
|
@@ -194,5 +193,57 @@ module ROM
|
|
194
193
|
self.class.option_definitions.process(self, options)
|
195
194
|
@options = options.freeze
|
196
195
|
end
|
196
|
+
|
197
|
+
# Collection of transformers for options
|
198
|
+
#
|
199
|
+
module Transformers
|
200
|
+
extend Transproc::Registry
|
201
|
+
|
202
|
+
import :identity, from: Transproc::Coercions
|
203
|
+
|
204
|
+
def self.default_value(_, options, name, value)
|
205
|
+
return options if options.key?(name)
|
206
|
+
options.merge(name => value)
|
207
|
+
end
|
208
|
+
|
209
|
+
def self.default_proc(object, options, name, fn)
|
210
|
+
return options if options.key?(name)
|
211
|
+
options.merge(name => fn.call(object))
|
212
|
+
end
|
213
|
+
|
214
|
+
def self.coercer(_, options, name, fn)
|
215
|
+
return options unless options.key?(name)
|
216
|
+
value = options[name]
|
217
|
+
options.merge name => fn[value]
|
218
|
+
end
|
219
|
+
|
220
|
+
def self.type_checker(_, options, name, type)
|
221
|
+
return options unless options.key?(name)
|
222
|
+
value = options[name]
|
223
|
+
|
224
|
+
return options if options[name].is_a?(type)
|
225
|
+
fail(
|
226
|
+
InvalidOptionValueError,
|
227
|
+
"#{name.inspect}:#{value.inspect} has incorrect type" \
|
228
|
+
" (#{type} is expected)"
|
229
|
+
)
|
230
|
+
end
|
231
|
+
|
232
|
+
def self.value_checker(_, options, name, list)
|
233
|
+
return options unless options.key?(name)
|
234
|
+
value = options[name]
|
235
|
+
|
236
|
+
return options if list.include?(options[name])
|
237
|
+
fail(
|
238
|
+
InvalidOptionValueError,
|
239
|
+
"#{name.inspect}:#{value.inspect} has incorrect value."
|
240
|
+
)
|
241
|
+
end
|
242
|
+
|
243
|
+
def self.reader_assigner(object, options, name)
|
244
|
+
object.instance_variable_set(:"@#{name}", options[name])
|
245
|
+
options
|
246
|
+
end
|
247
|
+
end
|
197
248
|
end
|
198
249
|
end
|
data/lib/rom/support/registry.rb
CHANGED
data/lib/rom/support/version.rb
CHANGED
data/rom-support.gemspec
CHANGED
@@ -15,7 +15,9 @@ Gem::Specification.new do |gem|
|
|
15
15
|
gem.test_files = `git ls-files -- {spec}/*`.split("\n")
|
16
16
|
gem.license = 'MIT'
|
17
17
|
|
18
|
+
gem.add_runtime_dependency 'dry-equalizer', '~> 0.2'
|
18
19
|
gem.add_runtime_dependency 'wisper', '~> 1.6', '>= 1.6.0'
|
20
|
+
gem.add_runtime_dependency 'transproc', '~> 0.4.0'
|
19
21
|
|
20
22
|
gem.add_development_dependency 'rake', '~> 10.3'
|
21
23
|
gem.add_development_dependency 'rspec', '~> 3.3'
|
@@ -0,0 +1,53 @@
|
|
1
|
+
require 'rom/support/auto_curry'
|
2
|
+
|
3
|
+
RSpec.describe ROM::AutoCurry do
|
4
|
+
subject(:object) { klass.new }
|
5
|
+
|
6
|
+
let(:klass) do
|
7
|
+
Class.new do
|
8
|
+
extend ROM::AutoCurry
|
9
|
+
|
10
|
+
def self.curried(*)
|
11
|
+
self
|
12
|
+
end
|
13
|
+
|
14
|
+
def initialize(*)
|
15
|
+
end
|
16
|
+
|
17
|
+
def arity_0
|
18
|
+
0
|
19
|
+
end
|
20
|
+
|
21
|
+
def arity_1(x)
|
22
|
+
x
|
23
|
+
end
|
24
|
+
|
25
|
+
def arity_2(x, y)
|
26
|
+
[x,y]
|
27
|
+
end
|
28
|
+
|
29
|
+
def arity_many(*args)
|
30
|
+
args
|
31
|
+
end
|
32
|
+
end
|
33
|
+
end
|
34
|
+
|
35
|
+
it 'auto-curries method with arity == 0' do
|
36
|
+
expect(object.arity_0).to be(0)
|
37
|
+
end
|
38
|
+
|
39
|
+
it 'auto-curries method with arity == 1' do
|
40
|
+
expect(object.arity_1).to be_instance_of(klass)
|
41
|
+
expect(object.arity_1(1)).to be(1)
|
42
|
+
end
|
43
|
+
|
44
|
+
it 'auto-curries method with arity > 0' do
|
45
|
+
expect(object.arity_2).to be_instance_of(klass)
|
46
|
+
expect(object.arity_2(1, 2)).to eql([1, 2])
|
47
|
+
end
|
48
|
+
|
49
|
+
it 'auto-curries method with arity < 0' do
|
50
|
+
expect(object.arity_many).to eql([])
|
51
|
+
expect(object.arity_many(1, 2)).to eql([1, 2])
|
52
|
+
end
|
53
|
+
end
|
@@ -88,6 +88,22 @@ describe ROM::Options do
|
|
88
88
|
expect(object.options).to eql(args: nil)
|
89
89
|
end
|
90
90
|
|
91
|
+
it 'coerces assigned values' do
|
92
|
+
klass.option :number, coercer: -> v { v.to_i }
|
93
|
+
|
94
|
+
object = klass.new(number: '1')
|
95
|
+
|
96
|
+
expect(object.options).to eql(number: 1)
|
97
|
+
end
|
98
|
+
|
99
|
+
it 'not coerces default values' do
|
100
|
+
klass.option :number, default: '1', coercer: -> v { v.to_i }
|
101
|
+
|
102
|
+
object = klass.new
|
103
|
+
|
104
|
+
expect(object.options).to eql(number: '1')
|
105
|
+
end
|
106
|
+
|
91
107
|
it 'options are frozen' do
|
92
108
|
object = klass.new
|
93
109
|
|
metadata
CHANGED
@@ -1,15 +1,29 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: rom-support
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 0.
|
4
|
+
version: 1.0.0.beta1
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- Piotr Solnica
|
8
8
|
autorequire:
|
9
9
|
bindir: bin
|
10
10
|
cert_chain: []
|
11
|
-
date: 2015-
|
11
|
+
date: 2015-11-23 00:00:00.000000000 Z
|
12
12
|
dependencies:
|
13
|
+
- !ruby/object:Gem::Dependency
|
14
|
+
name: dry-equalizer
|
15
|
+
requirement: !ruby/object:Gem::Requirement
|
16
|
+
requirements:
|
17
|
+
- - "~>"
|
18
|
+
- !ruby/object:Gem::Version
|
19
|
+
version: '0.2'
|
20
|
+
type: :runtime
|
21
|
+
prerelease: false
|
22
|
+
version_requirements: !ruby/object:Gem::Requirement
|
23
|
+
requirements:
|
24
|
+
- - "~>"
|
25
|
+
- !ruby/object:Gem::Version
|
26
|
+
version: '0.2'
|
13
27
|
- !ruby/object:Gem::Dependency
|
14
28
|
name: wisper
|
15
29
|
requirement: !ruby/object:Gem::Requirement
|
@@ -30,6 +44,20 @@ dependencies:
|
|
30
44
|
- - ">="
|
31
45
|
- !ruby/object:Gem::Version
|
32
46
|
version: 1.6.0
|
47
|
+
- !ruby/object:Gem::Dependency
|
48
|
+
name: transproc
|
49
|
+
requirement: !ruby/object:Gem::Requirement
|
50
|
+
requirements:
|
51
|
+
- - "~>"
|
52
|
+
- !ruby/object:Gem::Version
|
53
|
+
version: 0.4.0
|
54
|
+
type: :runtime
|
55
|
+
prerelease: false
|
56
|
+
version_requirements: !ruby/object:Gem::Requirement
|
57
|
+
requirements:
|
58
|
+
- - "~>"
|
59
|
+
- !ruby/object:Gem::Version
|
60
|
+
version: 0.4.0
|
33
61
|
- !ruby/object:Gem::Dependency
|
34
62
|
name: rake
|
35
63
|
requirement: !ruby/object:Gem::Requirement
|
@@ -82,7 +110,6 @@ files:
|
|
82
110
|
- lib/rom/support/data_proxy.rb
|
83
111
|
- lib/rom/support/deprecations.rb
|
84
112
|
- lib/rom/support/enumerable_dataset.rb
|
85
|
-
- lib/rom/support/guarded_inheritance_hook.rb
|
86
113
|
- lib/rom/support/inflector.rb
|
87
114
|
- lib/rom/support/inheritance_hook.rb
|
88
115
|
- lib/rom/support/options.rb
|
@@ -97,6 +124,7 @@ files:
|
|
97
124
|
- spec/support/constant_leak_finder.rb
|
98
125
|
- spec/support/mutant.rb
|
99
126
|
- spec/unit/rom/support/array_dataset_spec.rb
|
127
|
+
- spec/unit/rom/support/auto_curry_spec.rb
|
100
128
|
- spec/unit/rom/support/class_builder_spec.rb
|
101
129
|
- spec/unit/rom/support/enumerable_dataset_spec.rb
|
102
130
|
- spec/unit/rom/support/inflector_spec.rb
|
@@ -116,9 +144,9 @@ required_ruby_version: !ruby/object:Gem::Requirement
|
|
116
144
|
version: '0'
|
117
145
|
required_rubygems_version: !ruby/object:Gem::Requirement
|
118
146
|
requirements:
|
119
|
-
- - "
|
147
|
+
- - ">"
|
120
148
|
- !ruby/object:Gem::Version
|
121
|
-
version:
|
149
|
+
version: 1.3.1
|
122
150
|
requirements: []
|
123
151
|
rubyforge_project:
|
124
152
|
rubygems_version: 2.4.5
|
@@ -126,3 +154,4 @@ signing_key:
|
|
126
154
|
specification_version: 4
|
127
155
|
summary: Ruby Object Mapper - Support libraries
|
128
156
|
test_files: []
|
157
|
+
has_rdoc:
|
@@ -1,21 +0,0 @@
|
|
1
|
-
require 'rom/support/publisher'
|
2
|
-
|
3
|
-
module ROM
|
4
|
-
module Support
|
5
|
-
module GuardedInheritanceHook
|
6
|
-
def self.extended(base)
|
7
|
-
base.class_eval <<-RUBY
|
8
|
-
class << self
|
9
|
-
include ROM::Support::Publisher
|
10
|
-
|
11
|
-
def inherited(klass)
|
12
|
-
super
|
13
|
-
return if klass.superclass == #{base}
|
14
|
-
#{base}.__send__(:broadcast, :inherited, klass)
|
15
|
-
end
|
16
|
-
end
|
17
|
-
RUBY
|
18
|
-
end
|
19
|
-
end
|
20
|
-
end
|
21
|
-
end
|