rom-support 0.1.0 → 1.0.0.beta1
Sign up to get free protection for your applications and to get access to all the features.
- 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
|