nrser 0.0.18 → 0.0.19
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +4 -4
- data/lib/nrser/binding.rb +14 -0
- data/lib/nrser/enumerable.rb +3 -3
- data/lib/nrser/exception.rb +7 -0
- data/lib/nrser/meta/class_attrs.rb +76 -0
- data/lib/nrser/meta/props/base.rb +15 -0
- data/lib/nrser/meta/props.rb +363 -0
- data/lib/nrser/meta.rb +2 -0
- data/lib/nrser/no_arg.rb +15 -0
- data/lib/nrser/refinements/binding.rb +7 -0
- data/lib/nrser/refinements/exception.rb +7 -0
- data/lib/nrser/refinements/types.rb +9 -0
- data/lib/nrser/refinements.rb +2 -14
- data/lib/nrser/string.rb +109 -0
- data/lib/nrser/types/any.rb +1 -1
- data/lib/nrser/types/array.rb +4 -5
- data/lib/nrser/types/combinators.rb +3 -8
- data/lib/nrser/types/hash.rb +34 -4
- data/lib/nrser/types/symbol.rb +23 -0
- data/lib/nrser/types.rb +3 -1
- data/lib/nrser/version.rb +1 -1
- data/lib/nrser.rb +8 -116
- data/spec/nrser/meta/class_attrs_spec.rb +241 -0
- data/spec/nrser/meta/props_spec.rb +67 -0
- metadata +18 -2
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA1:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: f34385ea491972db4f43263773d9e9655ddabf85
|
4
|
+
data.tar.gz: af6e560b27729d1d16e8aa007515eb2d4eee354f
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: 8b9e68cefda2494539cd0f44efdee84ce70ddba6bbaccbe989e1cae0de30356cfbac0c5ede7df901489317a970cc3bec8dd38a720654344497e7dc3b47e66d3f
|
7
|
+
data.tar.gz: c400d4be2fed9d8120453f049dd86904c9bd8a2283525dee076318054d690f66b36f838453ef5a0c650f37323c57df86ee0acf33bf303893959f02d27c8342cc
|
@@ -0,0 +1,14 @@
|
|
1
|
+
require_relative './string'
|
2
|
+
|
3
|
+
module NRSER
|
4
|
+
class << self
|
5
|
+
|
6
|
+
def erb bnd, str
|
7
|
+
require 'erb'
|
8
|
+
filter_repeated_blank_lines ERB.new(dedent(str)).result(bnd)
|
9
|
+
end # erb
|
10
|
+
|
11
|
+
alias_method :template, :erb
|
12
|
+
|
13
|
+
end # class << self
|
14
|
+
end # module NRSER
|
data/lib/nrser/enumerable.rb
CHANGED
@@ -2,7 +2,7 @@ module NRSER
|
|
2
2
|
# Maps an enumerable object to a *new* hash with the same keys and values
|
3
3
|
# obtained by calling `block` with the current key and value.
|
4
4
|
#
|
5
|
-
# If `enumerable` *does not*
|
5
|
+
# If `enumerable` *does not* successfully respond to `#to_h` then it's
|
6
6
|
# treated as a hash where it's elements are the keys and all the values
|
7
7
|
# are `nil`.
|
8
8
|
#
|
@@ -13,14 +13,14 @@ module NRSER
|
|
13
13
|
# next step, it's going to probably be *the* most common argument type,
|
14
14
|
# and there's no reason to do the tests and set up the exception
|
15
15
|
# handler if we already know what's up with it.
|
16
|
-
return NRSER.map_hash_values(enumerable) if enumerable.is_a? Hash
|
16
|
+
return NRSER.map_hash_values(enumerable, &block) if enumerable.is_a? Hash
|
17
17
|
|
18
18
|
if enumerable.respond_to? :to_h
|
19
19
|
begin
|
20
20
|
hash = enumerable.to_h
|
21
21
|
rescue TypeError => e
|
22
22
|
else
|
23
|
-
return NRSER.map_hash_values hash
|
23
|
+
return NRSER.map_hash_values hash, &block
|
24
24
|
end
|
25
25
|
end
|
26
26
|
|
@@ -0,0 +1,76 @@
|
|
1
|
+
module NRSER
|
2
|
+
module Meta
|
3
|
+
|
4
|
+
# Mixin to provide methods to define and access class attributes - variables
|
5
|
+
# that act like instance variables with regards to inheritance but for the
|
6
|
+
# class itself.
|
7
|
+
#
|
8
|
+
# The motivation is to create a easy-to-use class instance variables that
|
9
|
+
# resolve like regular instance variables by looking up the inheritance
|
10
|
+
# hierarchy - meaning that:
|
11
|
+
#
|
12
|
+
# 1. When the value is set, it is set on the class in which the operation
|
13
|
+
# happens.
|
14
|
+
#
|
15
|
+
# 2. That value is read for that class and any subclasses.
|
16
|
+
# - Class 'self' attr_accessor values are not visible to subclasses.
|
17
|
+
#
|
18
|
+
# 3. But that value is not visible to any classes further up the inheritance
|
19
|
+
# chain.
|
20
|
+
# - Class variables (`@@` variables) are global to the *entire class
|
21
|
+
# hierarchy* rooted at the definition point.
|
22
|
+
#
|
23
|
+
# The tests in `spec/nrser/class_attrs_spec.rb` provide detailed walk-through
|
24
|
+
# of usage and differences from other approaches.
|
25
|
+
#
|
26
|
+
module ClassAttrs
|
27
|
+
|
28
|
+
# Class methods to extend the receiver with when {NRSER::Meta::ClassAttrs}
|
29
|
+
# is included.
|
30
|
+
module ClassMethods
|
31
|
+
def instance_variable_lookup name
|
32
|
+
instance_variable_get(name) || if (
|
33
|
+
superclass.respond_to? :instance_variable_lookup
|
34
|
+
)
|
35
|
+
superclass.instance_variable_lookup name
|
36
|
+
else
|
37
|
+
raise NoMethodError.new NRSER.squish <<-END
|
38
|
+
#{ name.inspect } is not defined anywhere in the class hierarchy
|
39
|
+
END
|
40
|
+
end
|
41
|
+
end
|
42
|
+
|
43
|
+
def class_attr_accessor attr
|
44
|
+
var_name = "@#{ attr }".to_sym
|
45
|
+
|
46
|
+
singleton_class.class_eval do
|
47
|
+
define_method(attr) do |*args|
|
48
|
+
case args.length
|
49
|
+
when 0
|
50
|
+
instance_variable_lookup var_name
|
51
|
+
when 1
|
52
|
+
instance_variable_set var_name, args[0]
|
53
|
+
else
|
54
|
+
raise ArgumentError.new NRSER.squish <<-END
|
55
|
+
wrong number of arguments
|
56
|
+
(given #{ args.length }, expected 0 or 1)
|
57
|
+
END
|
58
|
+
end
|
59
|
+
end
|
60
|
+
|
61
|
+
define_method("#{ attr }=") do |value|
|
62
|
+
instance_variable_set var_name, value
|
63
|
+
end
|
64
|
+
end
|
65
|
+
end
|
66
|
+
end # module ClassMethods
|
67
|
+
|
68
|
+
# Extend the including class with {NRSER::Meta::ClassAttrs::ClassMethods}
|
69
|
+
def self.included base
|
70
|
+
base.extend ClassMethods
|
71
|
+
end
|
72
|
+
|
73
|
+
end # module ClassAttrs
|
74
|
+
|
75
|
+
end # module Meta
|
76
|
+
end # module NRSER
|
@@ -0,0 +1,363 @@
|
|
1
|
+
module NRSER
|
2
|
+
module Meta
|
3
|
+
|
4
|
+
T = NRSER::Types
|
5
|
+
|
6
|
+
#
|
7
|
+
module Props
|
8
|
+
PROPS_VARIABLE_NAME = :@__NRSER_props
|
9
|
+
PROP_VALUES_VARIABLE_NAME = :@__NRSER_prop_values
|
10
|
+
|
11
|
+
class Prop
|
12
|
+
attr_accessor :defined_in,
|
13
|
+
:name,
|
14
|
+
:type,
|
15
|
+
:source
|
16
|
+
|
17
|
+
|
18
|
+
def initialize defined_in,
|
19
|
+
name,
|
20
|
+
type: T.any,
|
21
|
+
default: NRSER::NO_ARG,
|
22
|
+
source: nil
|
23
|
+
|
24
|
+
@defined_in = defined_in
|
25
|
+
@name = name
|
26
|
+
@type = NRSER::Types.make type
|
27
|
+
@source = source
|
28
|
+
@default = default
|
29
|
+
|
30
|
+
if @source.nil?
|
31
|
+
@instance_variable_source = false
|
32
|
+
else
|
33
|
+
source_str = source.to_s
|
34
|
+
@instance_variable_source = source_str[0] == '@'
|
35
|
+
end
|
36
|
+
end
|
37
|
+
|
38
|
+
|
39
|
+
# @todo Document default? method.
|
40
|
+
#
|
41
|
+
# @param [type] arg_name
|
42
|
+
# @todo Add name param description.
|
43
|
+
#
|
44
|
+
# @return [return_type]
|
45
|
+
# @todo Document return value.
|
46
|
+
#
|
47
|
+
def default?
|
48
|
+
@default != NRSER::NO_ARG
|
49
|
+
end # #default?
|
50
|
+
|
51
|
+
|
52
|
+
def default
|
53
|
+
if default?
|
54
|
+
@default
|
55
|
+
else
|
56
|
+
raise NameError.new NRSER.squish <<-END
|
57
|
+
Prop #{ self } has no default value.
|
58
|
+
END
|
59
|
+
end
|
60
|
+
end
|
61
|
+
|
62
|
+
|
63
|
+
# @todo Document source? method.
|
64
|
+
#
|
65
|
+
# @param [type] arg_name
|
66
|
+
# @todo Add name param description.
|
67
|
+
#
|
68
|
+
# @return [return_type]
|
69
|
+
# @todo Document return value.
|
70
|
+
#
|
71
|
+
def source?
|
72
|
+
!@source.nil?
|
73
|
+
end # #source?
|
74
|
+
|
75
|
+
|
76
|
+
# @todo Document instance_variable_source? method.
|
77
|
+
#
|
78
|
+
# @param [type] arg_name
|
79
|
+
# @todo Add name param description.
|
80
|
+
#
|
81
|
+
# @return [return_type]
|
82
|
+
# @todo Document return value.
|
83
|
+
#
|
84
|
+
def instance_variable_source?
|
85
|
+
@instance_variable_source
|
86
|
+
end # #instance_variable_source?
|
87
|
+
|
88
|
+
|
89
|
+
# @todo Document primary? method.
|
90
|
+
#
|
91
|
+
# @param [type] arg_name
|
92
|
+
# @todo Add name param description.
|
93
|
+
#
|
94
|
+
# @return [return_type]
|
95
|
+
# @todo Document return value.
|
96
|
+
#
|
97
|
+
def primary?
|
98
|
+
!source?
|
99
|
+
end # #primary?
|
100
|
+
|
101
|
+
|
102
|
+
# @todo Document get method.
|
103
|
+
#
|
104
|
+
# @param [type] arg_name
|
105
|
+
# @todo Add name param description.
|
106
|
+
#
|
107
|
+
# @return [return_type]
|
108
|
+
# @todo Document return value.
|
109
|
+
#
|
110
|
+
def get instance
|
111
|
+
if source?
|
112
|
+
if instance_variable_source?
|
113
|
+
instance.instance_variable_get source
|
114
|
+
else
|
115
|
+
instance.send source
|
116
|
+
end
|
117
|
+
else
|
118
|
+
values(instance)[name]
|
119
|
+
end
|
120
|
+
end # #get
|
121
|
+
|
122
|
+
|
123
|
+
# @todo Document set method.
|
124
|
+
#
|
125
|
+
# @param [type] arg_name
|
126
|
+
# @todo Add name param description.
|
127
|
+
#
|
128
|
+
# @return [return_type]
|
129
|
+
# @todo Document return value.
|
130
|
+
#
|
131
|
+
def set instance, value
|
132
|
+
unless type.test value
|
133
|
+
raise TypeError.new NRSER.squish <<-END
|
134
|
+
#{ defined_in }##{ name } must be of type #{ type };
|
135
|
+
found #{ value.inspect }
|
136
|
+
END
|
137
|
+
end
|
138
|
+
|
139
|
+
values(instance)[name] = value
|
140
|
+
end # #set
|
141
|
+
|
142
|
+
|
143
|
+
|
144
|
+
# @todo Document set_from_hash method.
|
145
|
+
#
|
146
|
+
# @param [type] arg_name
|
147
|
+
# @todo Add name param description.
|
148
|
+
#
|
149
|
+
# @return [return_type]
|
150
|
+
# @todo Document return value.
|
151
|
+
#
|
152
|
+
def set_from_values_hash instance, **values
|
153
|
+
if values.key? name
|
154
|
+
set instance, values[name]
|
155
|
+
else
|
156
|
+
if default?
|
157
|
+
set instance, default.dup
|
158
|
+
else
|
159
|
+
raise TypeError.new NRSER.squish <<-END
|
160
|
+
Prop #{ name } has no default value and no value was provided in
|
161
|
+
values #{ values.inspect }.
|
162
|
+
END
|
163
|
+
end
|
164
|
+
end
|
165
|
+
end # #set_from_hash
|
166
|
+
|
167
|
+
|
168
|
+
private
|
169
|
+
|
170
|
+
# @todo Document values method.
|
171
|
+
#
|
172
|
+
# @param [type] arg_name
|
173
|
+
# @todo Add name param description.
|
174
|
+
#
|
175
|
+
# @return [return_type]
|
176
|
+
# @todo Document return value.
|
177
|
+
#
|
178
|
+
def values instance
|
179
|
+
unless instance.instance_variable_defined? PROP_VALUES_VARIABLE_NAME
|
180
|
+
instance.instance_variable_set PROP_VALUES_VARIABLE_NAME, {}
|
181
|
+
end
|
182
|
+
|
183
|
+
instance.instance_variable_get PROP_VALUES_VARIABLE_NAME
|
184
|
+
end # #value
|
185
|
+
|
186
|
+
end # class Prop
|
187
|
+
|
188
|
+
|
189
|
+
# @todo Document get_props_ref method.
|
190
|
+
#
|
191
|
+
# @param [type] arg_name
|
192
|
+
# @todo Add name param description.
|
193
|
+
#
|
194
|
+
# @return [return_type]
|
195
|
+
# @todo Document return value.
|
196
|
+
#
|
197
|
+
def self.get_props_ref klass
|
198
|
+
unless klass.instance_variable_defined? PROPS_VARIABLE_NAME
|
199
|
+
klass.instance_variable_set PROPS_VARIABLE_NAME, {}
|
200
|
+
end
|
201
|
+
|
202
|
+
klass.instance_variable_get PROPS_VARIABLE_NAME
|
203
|
+
end # .get_props_ref
|
204
|
+
|
205
|
+
|
206
|
+
module ClassMethods
|
207
|
+
|
208
|
+
|
209
|
+
# @todo Document props method.
|
210
|
+
#
|
211
|
+
# @param [type] arg_name
|
212
|
+
# @todo Add name param description.
|
213
|
+
#
|
214
|
+
# @return [return_type]
|
215
|
+
# @todo Document return value.
|
216
|
+
#
|
217
|
+
def props own: false, primary: false
|
218
|
+
result = if !own && superclass.respond_to?(:props)
|
219
|
+
superclass.props own: own, primary: primary
|
220
|
+
else
|
221
|
+
{}
|
222
|
+
end
|
223
|
+
|
224
|
+
own_props = NRSER::Meta::Props.get_props_ref self
|
225
|
+
|
226
|
+
if primary
|
227
|
+
own_props.each {|name, prop|
|
228
|
+
if prop.primary?
|
229
|
+
result[name] = prop
|
230
|
+
end
|
231
|
+
}
|
232
|
+
else
|
233
|
+
result.merge! own_props
|
234
|
+
end
|
235
|
+
|
236
|
+
result
|
237
|
+
end # #own_props
|
238
|
+
|
239
|
+
|
240
|
+
# @todo Document prop method.
|
241
|
+
#
|
242
|
+
# @param [type] arg_name
|
243
|
+
# @todo Add name param description.
|
244
|
+
#
|
245
|
+
# @return [return_type]
|
246
|
+
# @todo Document return value.
|
247
|
+
#
|
248
|
+
def prop name, **opts
|
249
|
+
ref = NRSER::Meta::Props.get_props_ref self
|
250
|
+
|
251
|
+
T.sym.check name
|
252
|
+
|
253
|
+
if ref.key? name
|
254
|
+
raise ArgumentError.new NRSER.squish <<-END
|
255
|
+
Prop #{ name.inspect } already set for #{ self }:
|
256
|
+
#{ ref[name].inspect }
|
257
|
+
END
|
258
|
+
end
|
259
|
+
|
260
|
+
prop = Prop.new self, name, **opts
|
261
|
+
ref[name] = prop
|
262
|
+
|
263
|
+
unless prop.source?
|
264
|
+
class_eval do
|
265
|
+
define_method(name) do
|
266
|
+
prop.get self
|
267
|
+
end
|
268
|
+
|
269
|
+
# protected
|
270
|
+
# define_method("#{ name }=") do |value|
|
271
|
+
# prop.set self, value
|
272
|
+
# end
|
273
|
+
end
|
274
|
+
end
|
275
|
+
end # #prop
|
276
|
+
|
277
|
+
|
278
|
+
|
279
|
+
# @todo Document from_h method.
|
280
|
+
#
|
281
|
+
# @param [type] arg_name
|
282
|
+
# @todo Add name param description.
|
283
|
+
#
|
284
|
+
# @return [return_type]
|
285
|
+
# @todo Document return value.
|
286
|
+
#
|
287
|
+
def from_h hash
|
288
|
+
self.new(
|
289
|
+
NRSER.slice_keys(
|
290
|
+
NRSER.symbolize_keys(hash),
|
291
|
+
*self.props(primary: true).keys
|
292
|
+
)
|
293
|
+
)
|
294
|
+
end # #from_h
|
295
|
+
|
296
|
+
|
297
|
+
end # module ClassMethods
|
298
|
+
|
299
|
+
|
300
|
+
# Extend the including class with {NRSER::Meta::Props:ClassMethods}
|
301
|
+
def self.included base
|
302
|
+
base.extend ClassMethods
|
303
|
+
end
|
304
|
+
|
305
|
+
|
306
|
+
# Instance Methods
|
307
|
+
# =====================================================================
|
308
|
+
|
309
|
+
|
310
|
+
# @todo Document initialize_props method.
|
311
|
+
#
|
312
|
+
# @param [type] arg_name
|
313
|
+
# @todo Add name param description.
|
314
|
+
#
|
315
|
+
# @return [return_type]
|
316
|
+
# @todo Document return value.
|
317
|
+
#
|
318
|
+
def initialize_props values
|
319
|
+
self.class.props(primary: true).each { |name, prop|
|
320
|
+
prop.set_from_values_hash self, values
|
321
|
+
}
|
322
|
+
end # #initialize_props
|
323
|
+
|
324
|
+
|
325
|
+
# @todo Document to_h method.
|
326
|
+
#
|
327
|
+
# @param [type] arg_name
|
328
|
+
# @todo Add name param description.
|
329
|
+
#
|
330
|
+
# @return [return_type]
|
331
|
+
# @todo Document return value.
|
332
|
+
#
|
333
|
+
def to_h primary: false, own: false
|
334
|
+
NRSER.map_values(
|
335
|
+
self.class.props own: own, primary: primary
|
336
|
+
) { |name, prop| prop.get self }
|
337
|
+
end # #to_h
|
338
|
+
|
339
|
+
|
340
|
+
# @todo Document to_json method.
|
341
|
+
#
|
342
|
+
# @param [type] arg_name
|
343
|
+
# @todo Add name param description.
|
344
|
+
#
|
345
|
+
# @return [return_type]
|
346
|
+
# @todo Document return value.
|
347
|
+
#
|
348
|
+
def to_json *args
|
349
|
+
to_h.to_json *args
|
350
|
+
end # #to_json
|
351
|
+
|
352
|
+
|
353
|
+
def to_yaml *args
|
354
|
+
to_h.to_yaml *args
|
355
|
+
end
|
356
|
+
|
357
|
+
|
358
|
+
end # module Props
|
359
|
+
|
360
|
+
end # module Meta
|
361
|
+
end # module NRSER
|
362
|
+
|
363
|
+
require_relative './props/base'
|
data/lib/nrser/meta.rb
ADDED
data/lib/nrser/no_arg.rb
ADDED
@@ -0,0 +1,15 @@
|
|
1
|
+
require 'singleton'
|
2
|
+
|
3
|
+
module NRSER
|
4
|
+
# A singleton class who's instance is used to denote the lack of value
|
5
|
+
# for an argument when used as the default.
|
6
|
+
#
|
7
|
+
# For situations where an argument is optional and `nil` is a legitimate
|
8
|
+
# value.
|
9
|
+
#
|
10
|
+
class NoArg
|
11
|
+
include Singleton
|
12
|
+
end
|
13
|
+
|
14
|
+
NO_ARG = NoArg.instance
|
15
|
+
end # module NRSER
|
data/lib/nrser/refinements.rb
CHANGED
@@ -3,17 +3,5 @@ require_relative './refinements/string'
|
|
3
3
|
require_relative './refinements/array'
|
4
4
|
require_relative './refinements/hash'
|
5
5
|
require_relative './refinements/pathname'
|
6
|
-
|
7
|
-
|
8
|
-
refine Exception do
|
9
|
-
def format
|
10
|
-
NRSER.format_exception self
|
11
|
-
end
|
12
|
-
end
|
13
|
-
|
14
|
-
refine Binding do
|
15
|
-
def erb str
|
16
|
-
NRSER.template self, str
|
17
|
-
end
|
18
|
-
end
|
19
|
-
end # NRSER
|
6
|
+
require_relative './refinements/exception'
|
7
|
+
require_relative './refinements/binding'
|
data/lib/nrser/string.rb
ADDED
@@ -0,0 +1,109 @@
|
|
1
|
+
module NRSER
|
2
|
+
class << self
|
3
|
+
# Functions the operate on strings.
|
4
|
+
|
5
|
+
# turn a multi-line string into a single line, collapsing whitespace
|
6
|
+
# to a single space.
|
7
|
+
#
|
8
|
+
# same as ActiveSupport's String.squish, adapted from there.
|
9
|
+
def squish str
|
10
|
+
str.gsub(/[[:space:]]+/, ' ').strip
|
11
|
+
end # squish
|
12
|
+
|
13
|
+
alias_method :unblock, :squish
|
14
|
+
|
15
|
+
|
16
|
+
def common_prefix strings
|
17
|
+
raise ArgumentError.new("argument can't be empty") if strings.empty?
|
18
|
+
sorted = strings.sort.reject {|line| line == "\n"}
|
19
|
+
i = 0
|
20
|
+
while sorted.first[i] == sorted.last[i] &&
|
21
|
+
i < [sorted.first.length, sorted.last.length].min
|
22
|
+
i = i + 1
|
23
|
+
end
|
24
|
+
strings.first[0...i]
|
25
|
+
end # common_prefix
|
26
|
+
|
27
|
+
|
28
|
+
def dedent str
|
29
|
+
return str if str.empty?
|
30
|
+
lines = str.lines
|
31
|
+
indent = common_prefix(lines).match(/^\s*/)[0]
|
32
|
+
return str if indent.empty?
|
33
|
+
lines.map {|line|
|
34
|
+
line = line[indent.length..line.length] if line.start_with? indent
|
35
|
+
}.join
|
36
|
+
end # dedent
|
37
|
+
|
38
|
+
# I like dedent better, but other libs seems to call it deindent
|
39
|
+
alias_method :deindent, :dedent
|
40
|
+
|
41
|
+
|
42
|
+
def filter_repeated_blank_lines str
|
43
|
+
out = []
|
44
|
+
lines = str.lines
|
45
|
+
skipping = false
|
46
|
+
str.lines.each do |line|
|
47
|
+
if line =~ /^\s*$/
|
48
|
+
unless skipping
|
49
|
+
out << line
|
50
|
+
end
|
51
|
+
skipping = true
|
52
|
+
else
|
53
|
+
skipping = false
|
54
|
+
out << line
|
55
|
+
end
|
56
|
+
end
|
57
|
+
out.join
|
58
|
+
end # filter_repeated_blank_lines
|
59
|
+
|
60
|
+
|
61
|
+
# adapted from acrive_support 4.2.0
|
62
|
+
#
|
63
|
+
# <https://github.com/rails/rails/blob/7847a19f476fb9bee287681586d872ea43785e53/activesupport/lib/active_support/core_ext/string/indent.rb>
|
64
|
+
#
|
65
|
+
def indent str, amount = 2, indent_string=nil, indent_empty_lines=false
|
66
|
+
indent_string = indent_string || str[/^[ \t]/] || ' '
|
67
|
+
re = indent_empty_lines ? /^/ : /^(?!$)/
|
68
|
+
str.gsub(re, indent_string * amount)
|
69
|
+
end
|
70
|
+
|
71
|
+
# Truncates a given +text+ after a given <tt>length</tt> if +text+ is longer than <tt>length</tt>:
|
72
|
+
#
|
73
|
+
# 'Once upon a time in a world far far away'.truncate(27)
|
74
|
+
# # => "Once upon a time in a wo..."
|
75
|
+
#
|
76
|
+
# Pass a string or regexp <tt>:separator</tt> to truncate +text+ at a natural break:
|
77
|
+
#
|
78
|
+
# 'Once upon a time in a world far far away'.truncate(27, separator: ' ')
|
79
|
+
# # => "Once upon a time in a..."
|
80
|
+
#
|
81
|
+
# 'Once upon a time in a world far far away'.truncate(27, separator: /\s/)
|
82
|
+
# # => "Once upon a time in a..."
|
83
|
+
#
|
84
|
+
# The last characters will be replaced with the <tt>:omission</tt> string (defaults to "...")
|
85
|
+
# for a total length not exceeding <tt>length</tt>:
|
86
|
+
#
|
87
|
+
# 'And they found that many people were sleeping better.'.truncate(25, omission: '... (continued)')
|
88
|
+
# # => "And they f... (continued)"
|
89
|
+
#
|
90
|
+
# adapted from
|
91
|
+
#
|
92
|
+
# <https://github.com/rails/rails/blob/7847a19f476fb9bee287681586d872ea43785e53/activesupport/lib/active_support/core_ext/string/filters.rb#L46>
|
93
|
+
#
|
94
|
+
def truncate(str, truncate_at, options = {})
|
95
|
+
return str.dup unless str.length > truncate_at
|
96
|
+
|
97
|
+
omission = options[:omission] || '...'
|
98
|
+
length_with_room_for_omission = truncate_at - omission.length
|
99
|
+
stop = \
|
100
|
+
if options[:separator]
|
101
|
+
str.rindex(options[:separator], length_with_room_for_omission) || length_with_room_for_omission
|
102
|
+
else
|
103
|
+
length_with_room_for_omission
|
104
|
+
end
|
105
|
+
|
106
|
+
"#{str[0, stop]}#{omission}"
|
107
|
+
end
|
108
|
+
end # class << self
|
109
|
+
end # module NRSER
|
data/lib/nrser/types/any.rb
CHANGED
data/lib/nrser/types/array.rb
CHANGED
@@ -10,13 +10,13 @@ module NRSER::Types
|
|
10
10
|
|
11
11
|
attr_reader :item_type
|
12
12
|
|
13
|
-
def initialize item_type = NRSER::Types
|
13
|
+
def initialize item_type = NRSER::Types::ANY, **options
|
14
14
|
super ::Array, **options
|
15
15
|
@item_type = NRSER::Types.make item_type
|
16
16
|
end
|
17
17
|
|
18
18
|
def test value
|
19
|
-
super(value) && if @item_type == NRSER::Types
|
19
|
+
super(value) && if @item_type == NRSER::Types::ANY
|
20
20
|
true
|
21
21
|
else
|
22
22
|
value.all? {|v| @item_type.test v}
|
@@ -55,7 +55,6 @@ module NRSER::Types
|
|
55
55
|
Array.new *args
|
56
56
|
end
|
57
57
|
|
58
|
-
|
59
|
-
|
60
|
-
end
|
58
|
+
singleton_class.send :alias_method, :list, :array
|
59
|
+
|
61
60
|
end # NRSER::Types
|
@@ -63,10 +63,7 @@ module NRSER::Types
|
|
63
63
|
NRSER::Types::Union.new *types, **options
|
64
64
|
end
|
65
65
|
|
66
|
-
|
67
|
-
def self.one_of *types, **options
|
68
|
-
union *types, **options
|
69
|
-
end
|
66
|
+
singleton_class.send :alias_method, :one_of, :union
|
70
67
|
|
71
68
|
class Intersection < Combinator
|
72
69
|
def test value
|
@@ -79,8 +76,6 @@ module NRSER::Types
|
|
79
76
|
Intersection.new *types, **options
|
80
77
|
end
|
81
78
|
|
82
|
-
|
83
|
-
|
84
|
-
intersection *types, **options
|
85
|
-
end
|
79
|
+
singleton_class.send :alias_method, :all_of, :intersection
|
80
|
+
|
86
81
|
end # NRSER::Types
|
data/lib/nrser/types/hash.rb
CHANGED
@@ -5,11 +5,41 @@ using NRSER
|
|
5
5
|
|
6
6
|
module NRSER::Types
|
7
7
|
|
8
|
-
class
|
9
|
-
attr_reader :keys, :values
|
8
|
+
class HashType < IsA
|
9
|
+
attr_reader :keys, :values #, :including, :exactly, :min, :max
|
10
10
|
|
11
|
-
def initialize
|
11
|
+
def initialize keys: NRSER::Types::ANY,
|
12
|
+
values: NRSER::Types::ANY,
|
13
|
+
**options
|
14
|
+
super ::Hash, **options
|
12
15
|
|
16
|
+
@keys = NRSER::Types.make keys
|
17
|
+
@values = NRSER::Types.make keys
|
18
|
+
|
19
|
+
end
|
20
|
+
|
21
|
+
def test value
|
22
|
+
return false unless super(value)
|
23
|
+
|
24
|
+
if keys == NRSER::Types::ALL && values == NRSER::Types::ALL
|
25
|
+
return true
|
26
|
+
end
|
27
|
+
|
28
|
+
value.all? { |k, v|
|
29
|
+
keys.test(k) && values.test(v)
|
30
|
+
}
|
13
31
|
end
|
14
|
-
end #
|
32
|
+
end # HashType
|
33
|
+
|
34
|
+
HASH = HashType.new.freeze
|
35
|
+
|
36
|
+
def self.hash_ *args
|
37
|
+
if args.empty?
|
38
|
+
HASH
|
39
|
+
else
|
40
|
+
HashType.new *args
|
41
|
+
end
|
42
|
+
end
|
43
|
+
|
44
|
+
singleton_class.send :alias_method, :dict, :hash_
|
15
45
|
end
|
@@ -0,0 +1,23 @@
|
|
1
|
+
require 'nrser/refinements'
|
2
|
+
require 'nrser/types/is'
|
3
|
+
require 'nrser/types/is_a'
|
4
|
+
|
5
|
+
using NRSER
|
6
|
+
|
7
|
+
module NRSER::Types
|
8
|
+
SYM = IsA.new Symbol, name: 'Sym', from_s: ->(s) { s.to_sym }
|
9
|
+
|
10
|
+
def self.sym **options
|
11
|
+
if options.empty?
|
12
|
+
# if there are no options can point to the constant for efficiency
|
13
|
+
SYM
|
14
|
+
else
|
15
|
+
raise "Not Implemented"
|
16
|
+
end
|
17
|
+
end # string
|
18
|
+
|
19
|
+
def self.symbol *args
|
20
|
+
sym *args
|
21
|
+
end
|
22
|
+
|
23
|
+
end # NRSER::Types
|
data/lib/nrser/types.rb
CHANGED
data/lib/nrser/version.rb
CHANGED
data/lib/nrser.rb
CHANGED
@@ -1,122 +1,14 @@
|
|
1
|
+
module NRSER; end
|
2
|
+
|
1
3
|
require_relative './nrser/version'
|
4
|
+
require_relative './nrser/no_arg'
|
2
5
|
require_relative './nrser/collection'
|
3
6
|
require_relative './nrser/truthy'
|
4
|
-
|
5
|
-
|
6
|
-
|
7
|
-
|
8
|
-
# turn a multi-line string into a single line, collapsing whitespace
|
9
|
-
# to a single space.
|
10
|
-
#
|
11
|
-
# same as ActiveSupport's String.squish, adapted from there.
|
12
|
-
def squish str
|
13
|
-
str.gsub(/[[:space:]]+/, ' ').strip
|
14
|
-
end # squish
|
15
|
-
|
16
|
-
alias_method :unblock, :squish
|
17
|
-
|
18
|
-
def common_prefix strings
|
19
|
-
raise ArgumentError.new("argument can't be empty") if strings.empty?
|
20
|
-
sorted = strings.sort.reject {|line| line == "\n"}
|
21
|
-
i = 0
|
22
|
-
while sorted.first[i] == sorted.last[i] &&
|
23
|
-
i < [sorted.first.length, sorted.last.length].min
|
24
|
-
i = i + 1
|
25
|
-
end
|
26
|
-
strings.first[0...i]
|
27
|
-
end # common_prefix
|
28
|
-
|
29
|
-
def dedent str
|
30
|
-
return str if str.empty?
|
31
|
-
lines = str.lines
|
32
|
-
indent = common_prefix(lines).match(/^\s*/)[0]
|
33
|
-
return str if indent.empty?
|
34
|
-
lines.map {|line|
|
35
|
-
line = line[indent.length..line.length] if line.start_with? indent
|
36
|
-
}.join
|
37
|
-
end # dedent
|
38
|
-
|
39
|
-
def filter_repeated_blank_lines str
|
40
|
-
out = []
|
41
|
-
lines = str.lines
|
42
|
-
skipping = false
|
43
|
-
str.lines.each do |line|
|
44
|
-
if line =~ /^\s*$/
|
45
|
-
unless skipping
|
46
|
-
out << line
|
47
|
-
end
|
48
|
-
skipping = true
|
49
|
-
else
|
50
|
-
skipping = false
|
51
|
-
out << line
|
52
|
-
end
|
53
|
-
end
|
54
|
-
out.join
|
55
|
-
end # filter_repeated_blank_lines
|
56
|
-
|
57
|
-
def erb bnd, str
|
58
|
-
require 'erb'
|
59
|
-
filter_repeated_blank_lines ERB.new(dedent(str)).result(bnd)
|
60
|
-
end # erb
|
61
|
-
|
62
|
-
alias_method :template, :erb
|
63
|
-
|
64
|
-
def format_exception e
|
65
|
-
"#{ e.message } (#{ e.class }):\n #{ e.backtrace.join("\n ") }"
|
66
|
-
end
|
67
|
-
|
68
|
-
# adapted from acrive_support 4.2.0
|
69
|
-
#
|
70
|
-
# <https://github.com/rails/rails/blob/7847a19f476fb9bee287681586d872ea43785e53/activesupport/lib/active_support/core_ext/string/indent.rb>
|
71
|
-
#
|
72
|
-
def indent str, amount = 2, indent_string=nil, indent_empty_lines=false
|
73
|
-
indent_string = indent_string || str[/^[ \t]/] || ' '
|
74
|
-
re = indent_empty_lines ? /^/ : /^(?!$)/
|
75
|
-
str.gsub(re, indent_string * amount)
|
76
|
-
end
|
77
|
-
|
78
|
-
# Truncates a given +text+ after a given <tt>length</tt> if +text+ is longer than <tt>length</tt>:
|
79
|
-
#
|
80
|
-
# 'Once upon a time in a world far far away'.truncate(27)
|
81
|
-
# # => "Once upon a time in a wo..."
|
82
|
-
#
|
83
|
-
# Pass a string or regexp <tt>:separator</tt> to truncate +text+ at a natural break:
|
84
|
-
#
|
85
|
-
# 'Once upon a time in a world far far away'.truncate(27, separator: ' ')
|
86
|
-
# # => "Once upon a time in a..."
|
87
|
-
#
|
88
|
-
# 'Once upon a time in a world far far away'.truncate(27, separator: /\s/)
|
89
|
-
# # => "Once upon a time in a..."
|
90
|
-
#
|
91
|
-
# The last characters will be replaced with the <tt>:omission</tt> string (defaults to "...")
|
92
|
-
# for a total length not exceeding <tt>length</tt>:
|
93
|
-
#
|
94
|
-
# 'And they found that many people were sleeping better.'.truncate(25, omission: '... (continued)')
|
95
|
-
# # => "And they f... (continued)"
|
96
|
-
#
|
97
|
-
# adapted from
|
98
|
-
#
|
99
|
-
# <https://github.com/rails/rails/blob/7847a19f476fb9bee287681586d872ea43785e53/activesupport/lib/active_support/core_ext/string/filters.rb#L46>
|
100
|
-
#
|
101
|
-
def truncate(str, truncate_at, options = {})
|
102
|
-
return str.dup unless str.length > truncate_at
|
103
|
-
|
104
|
-
omission = options[:omission] || '...'
|
105
|
-
length_with_room_for_omission = truncate_at - omission.length
|
106
|
-
stop = \
|
107
|
-
if options[:separator]
|
108
|
-
str.rindex(options[:separator], length_with_room_for_omission) || length_with_room_for_omission
|
109
|
-
else
|
110
|
-
length_with_room_for_omission
|
111
|
-
end
|
112
|
-
|
113
|
-
"#{str[0, stop]}#{omission}"
|
114
|
-
end
|
115
|
-
|
116
|
-
end # class << self
|
117
|
-
end
|
118
|
-
|
7
|
+
require_relative './nrser/string'
|
8
|
+
require_relative './nrser/binding'
|
9
|
+
require_relative './nrser/exception'
|
119
10
|
require_relative './nrser/enumerable'
|
120
11
|
require_relative './nrser/hash'
|
121
12
|
require_relative './nrser/array'
|
122
|
-
require_relative
|
13
|
+
require_relative './nrser/types'
|
14
|
+
require_relative './nrser/meta'
|
@@ -0,0 +1,241 @@
|
|
1
|
+
require 'spec_helper'
|
2
|
+
|
3
|
+
describe NRSER::Meta::ClassAttrs do
|
4
|
+
# I'm writing this as I'm developing the module, so I'm going to walk through
|
5
|
+
# some of the motivation, features and differences from other approaches
|
6
|
+
# as notes to self/others...
|
7
|
+
#
|
8
|
+
|
9
|
+
|
10
|
+
# Setup
|
11
|
+
# =====================================================================
|
12
|
+
#
|
13
|
+
# Because any changes would global and RSpec seems to do things some-what
|
14
|
+
# asynchronously we need to use dynamic class creation, which is sub-optimal
|
15
|
+
# because it has subtle difference with normal definitions (@@ variable
|
16
|
+
# declaration being the major one I've hit do far), but it seems like the
|
17
|
+
# most reasonable way to go about it at the moment.
|
18
|
+
#
|
19
|
+
|
20
|
+
let (:base) {
|
21
|
+
Class.new do
|
22
|
+
include NRSER::Meta::ClassAttrs
|
23
|
+
|
24
|
+
# To compare, I'll also work with standard '@@' class variables and
|
25
|
+
# an standard attr_accessors added to the classes themselves.
|
26
|
+
|
27
|
+
# Normally this would be
|
28
|
+
#
|
29
|
+
# @@base_class_var = :base_class_var_value
|
30
|
+
#
|
31
|
+
# but that is different for dynamic classes, to we use
|
32
|
+
class_variable_set :@@base_class_var, :base_class_var_value
|
33
|
+
|
34
|
+
class << self
|
35
|
+
attr_accessor :base_self_attr_accessor
|
36
|
+
end
|
37
|
+
|
38
|
+
# Set the self attr_accessor value
|
39
|
+
self.base_self_attr_accessor = :base_self_attr_accessor_value
|
40
|
+
|
41
|
+
# And now some variables using the mixin.
|
42
|
+
|
43
|
+
# I won't ever set this
|
44
|
+
class_attr_accessor :never_set
|
45
|
+
|
46
|
+
# I'll define this here, but won't set it from base
|
47
|
+
class_attr_accessor :def_in_base
|
48
|
+
|
49
|
+
# These will be set from here
|
50
|
+
class_attr_accessor :set_in_base_1
|
51
|
+
class_attr_accessor :set_in_base_2
|
52
|
+
|
53
|
+
# There are two ways of setting values:
|
54
|
+
#
|
55
|
+
# 1. self assignment:
|
56
|
+
|
57
|
+
self.set_in_base_1 = :set_in_base_1_value
|
58
|
+
|
59
|
+
# Not that `x = value` will not work - the `self` is required.
|
60
|
+
# I haven't found any way to get `x = value` to trigger the setter
|
61
|
+
# methods.
|
62
|
+
#
|
63
|
+
# 2. "DSL-style":
|
64
|
+
|
65
|
+
set_in_base_2 :set_in_base_2_value
|
66
|
+
|
67
|
+
# This is the style i started with, because it made it really easy
|
68
|
+
# to have nice-feeling DSL-like functionality with minimal work.
|
69
|
+
#
|
70
|
+
# I'm unsure at the moment if it's something I want to keep (probably),
|
71
|
+
# and if so if it should be optional via a flag on
|
72
|
+
# `class_attr_accessor` (seems like a good idea).
|
73
|
+
#
|
74
|
+
# It's "nice", but it's perhaps kind-of counter-intuitive in the Ruby
|
75
|
+
# world?
|
76
|
+
#
|
77
|
+
|
78
|
+
end
|
79
|
+
}
|
80
|
+
|
81
|
+
let(:child_1) {
|
82
|
+
Class.new(base) do
|
83
|
+
end
|
84
|
+
}
|
85
|
+
|
86
|
+
let(:child_1_1) {
|
87
|
+
Class.new(child_1) do
|
88
|
+
end
|
89
|
+
}
|
90
|
+
|
91
|
+
let(:child_2) {
|
92
|
+
Class.new(base) do
|
93
|
+
end
|
94
|
+
}
|
95
|
+
|
96
|
+
let(:child_2_1) {
|
97
|
+
Class.new(child_2) do
|
98
|
+
end
|
99
|
+
}
|
100
|
+
|
101
|
+
|
102
|
+
# Tests
|
103
|
+
# =====================================================================
|
104
|
+
|
105
|
+
describe "Class (@@) Variables and Their Problems" do
|
106
|
+
# ---------------------------------------------------------------------
|
107
|
+
#
|
108
|
+
# Examples of the desired features that work (OK examples) and those that
|
109
|
+
# don't (PROBLEM! examples) for class (@@) variables.
|
110
|
+
#
|
111
|
+
|
112
|
+
it "can access @@ variables from base and any subclass (OK)" do
|
113
|
+
[base, child_1, child_1_1, child_2, child_2_1].each do |klass|
|
114
|
+
expect(
|
115
|
+
klass.class_variable_get :@@base_class_var
|
116
|
+
).to be :base_class_var_value
|
117
|
+
end
|
118
|
+
end
|
119
|
+
|
120
|
+
|
121
|
+
context "@@ variable changed in base" do
|
122
|
+
let(:new_value) { :new_base_class_var_value }
|
123
|
+
|
124
|
+
before {
|
125
|
+
base.class_variable_set :@@base_class_var, new_value
|
126
|
+
}
|
127
|
+
|
128
|
+
it "can access new value from base and any subclass (OK)" do
|
129
|
+
[base, child_1, child_1_1, child_2, child_2_1].each do |klass|
|
130
|
+
expect(
|
131
|
+
klass.class_variable_get :@@base_class_var
|
132
|
+
).to be new_value
|
133
|
+
end
|
134
|
+
end
|
135
|
+
end # @@ variable changed in base
|
136
|
+
|
137
|
+
|
138
|
+
context "@@ variable changed in subclass" do
|
139
|
+
let(:new_value) { :new_child_1_class_var_value }
|
140
|
+
|
141
|
+
before {
|
142
|
+
child_1.class_variable_set :@@base_class_var, new_value
|
143
|
+
}
|
144
|
+
|
145
|
+
# This is the problem: changes to the value anywhere in the hierarchy
|
146
|
+
# are global to the hierarchy
|
147
|
+
it "reads that value from all classes (PROBLEM!)" do
|
148
|
+
[base, child_1, child_1_1, child_2, child_2_1].each do |klass|
|
149
|
+
expect(
|
150
|
+
klass.class_variable_get :@@base_class_var
|
151
|
+
).to be new_value
|
152
|
+
end
|
153
|
+
end
|
154
|
+
end # @@ variable changed in base
|
155
|
+
|
156
|
+
end # Class (@@) Variables and Their Problems
|
157
|
+
|
158
|
+
|
159
|
+
describe "Class 'Self' attr_accessor and Their Problems" do
|
160
|
+
# ---------------------------------------------------------------------
|
161
|
+
|
162
|
+
it "can access from base (OK)" do
|
163
|
+
expect(base.base_self_attr_accessor).to be :base_self_attr_accessor_value
|
164
|
+
end
|
165
|
+
|
166
|
+
it "can't access from any subclasses (PROBLEM!)" do
|
167
|
+
[child_1, child_1_1, child_2, child_2_1].each do |klass|
|
168
|
+
expect(klass.base_self_attr_accessor).to be nil
|
169
|
+
end
|
170
|
+
end
|
171
|
+
|
172
|
+
end # Class 'Self' Attribute Accessors and Their Problems
|
173
|
+
|
174
|
+
|
175
|
+
|
176
|
+
describe "NRSER::ClassAttrs Solution" do
|
177
|
+
# ---------------------------------------------------------------------
|
178
|
+
#
|
179
|
+
# Examples of the desired features that work (OK examples) and those that
|
180
|
+
# don't (PROBLEM! examples) for class 'self' attr_accessor.
|
181
|
+
#
|
182
|
+
|
183
|
+
it "raises NoMethodError when accessing unset class attrs" do
|
184
|
+
expect { base.never_set }.to raise_error NoMethodError
|
185
|
+
expect { base.def_in_base }.to raise_error NoMethodError
|
186
|
+
end
|
187
|
+
|
188
|
+
|
189
|
+
it "reads values set in base from base and any subclass" do
|
190
|
+
[base, child_1, child_1_1, child_2, child_2_1].each do |klass|
|
191
|
+
expect(klass.set_in_base_1).to be :set_in_base_1_value
|
192
|
+
expect(klass.set_in_base_2).to be :set_in_base_2_value
|
193
|
+
end
|
194
|
+
end
|
195
|
+
|
196
|
+
|
197
|
+
context "class_attr_accessor value changed in base" do
|
198
|
+
# With class_attr_accessor, the new value will be visible to base and
|
199
|
+
# all subclasses.
|
200
|
+
|
201
|
+
let(:new_value) { :new_set_in_base_1_value }
|
202
|
+
|
203
|
+
before {
|
204
|
+
base.set_in_base_1 = new_value
|
205
|
+
}
|
206
|
+
|
207
|
+
it "can access new value from base and any subclass" do
|
208
|
+
[base, child_1, child_1_1, child_2, child_2_1].each do |klass|
|
209
|
+
expect(klass.set_in_base_1).to be new_value
|
210
|
+
end
|
211
|
+
end
|
212
|
+
end # class_attr_accessor value changed in base
|
213
|
+
|
214
|
+
|
215
|
+
context "class_attr_accessor value changed in subclass" do
|
216
|
+
# With class_attr_accessor, the new value will be visible to the class
|
217
|
+
# it is set in and any subclasses without affecting any others.
|
218
|
+
|
219
|
+
let(:new_value) { :set_in_child_1 }
|
220
|
+
|
221
|
+
before {
|
222
|
+
child_1.set_in_base_1 = new_value
|
223
|
+
}
|
224
|
+
|
225
|
+
it "reads the new value from that class and any subclasses" do
|
226
|
+
[child_1, child_1_1].each do |klass|
|
227
|
+
expect(klass.set_in_base_1).to be new_value
|
228
|
+
end
|
229
|
+
end
|
230
|
+
|
231
|
+
it "reads the old value from any other classes" do
|
232
|
+
[base, child_2, child_2_1].each do |klass|
|
233
|
+
expect(klass.set_in_base_1).to be :set_in_base_1_value
|
234
|
+
end
|
235
|
+
end
|
236
|
+
|
237
|
+
end # class_attr_accessor value changed in subclass
|
238
|
+
|
239
|
+
end # NRSER::ClassAttrs Solution
|
240
|
+
|
241
|
+
end # NRSER::ClassAttrs
|
@@ -0,0 +1,67 @@
|
|
1
|
+
require 'spec_helper'
|
2
|
+
|
3
|
+
T = NRSER::Types
|
4
|
+
|
5
|
+
describe NRSER::Meta::Props do
|
6
|
+
|
7
|
+
# Setup
|
8
|
+
# =====================================================================
|
9
|
+
|
10
|
+
let(:point) {
|
11
|
+
Class.new(NRSER::Meta::Props::Base) do
|
12
|
+
# include NRSER::Meta::Props
|
13
|
+
|
14
|
+
prop :x, type: T.int
|
15
|
+
prop :y, type: T.int
|
16
|
+
prop :blah, type: T.str, source: :blah
|
17
|
+
|
18
|
+
def blah
|
19
|
+
"blah!"
|
20
|
+
end
|
21
|
+
end
|
22
|
+
}
|
23
|
+
|
24
|
+
it "has the props" do
|
25
|
+
props = point.props
|
26
|
+
|
27
|
+
expect(props).to be_a Hash
|
28
|
+
|
29
|
+
[:x, :y, :blah].each do |name|
|
30
|
+
expect(props[name]).to be_a NRSER::Meta::Props::Prop
|
31
|
+
end
|
32
|
+
|
33
|
+
[:x, :y].each do |name|
|
34
|
+
expect(props[name].source?).to be false
|
35
|
+
expect(props[name].primary?).to be true
|
36
|
+
end
|
37
|
+
|
38
|
+
expect(props[:blah].source?).to be true
|
39
|
+
expect(props[:blah].primary?).to be false
|
40
|
+
|
41
|
+
primary_props = point.props primary: true
|
42
|
+
|
43
|
+
expect(primary_props.key? :blah).to be false
|
44
|
+
|
45
|
+
p = point.new x: 1, y: 2
|
46
|
+
|
47
|
+
expect(p.x).to be 1
|
48
|
+
expect(p.y).to be 2
|
49
|
+
|
50
|
+
expect(p.to_h).to eq({x: 1, y: 2, blah: "blah!"})
|
51
|
+
expect(p.to_h(primary: true)).to eq({x: 1, y: 2})
|
52
|
+
|
53
|
+
expect { point.new x: 1, y: 'why?' }.to raise_error TypeError
|
54
|
+
expect { p.x = 3 }.to raise_error NoMethodError
|
55
|
+
|
56
|
+
p_hash = p.to_h
|
57
|
+
|
58
|
+
p2 = point.from_h p_hash
|
59
|
+
|
60
|
+
expect(p2.x).to be 1
|
61
|
+
expect(p2.y).to be 2
|
62
|
+
|
63
|
+
expect(p2.to_h).to eq({x: 1, y: 2, blah: "blah!"})
|
64
|
+
end
|
65
|
+
|
66
|
+
end # NRSER::Meta::Props
|
67
|
+
|
metadata
CHANGED
@@ -1,14 +1,14 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: nrser
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 0.0.
|
4
|
+
version: 0.0.19
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- nrser
|
8
8
|
autorequire:
|
9
9
|
bindir: bin
|
10
10
|
cert_chain: []
|
11
|
-
date: 2017-08-
|
11
|
+
date: 2017-08-20 00:00:00.000000000 Z
|
12
12
|
dependencies:
|
13
13
|
- !ruby/object:Gem::Dependency
|
14
14
|
name: bundler
|
@@ -91,16 +91,27 @@ files:
|
|
91
91
|
- README.md
|
92
92
|
- lib/nrser.rb
|
93
93
|
- lib/nrser/array.rb
|
94
|
+
- lib/nrser/binding.rb
|
94
95
|
- lib/nrser/collection.rb
|
95
96
|
- lib/nrser/enumerable.rb
|
97
|
+
- lib/nrser/exception.rb
|
96
98
|
- lib/nrser/hash.rb
|
97
99
|
- lib/nrser/logger.rb
|
100
|
+
- lib/nrser/meta.rb
|
101
|
+
- lib/nrser/meta/class_attrs.rb
|
102
|
+
- lib/nrser/meta/props.rb
|
103
|
+
- lib/nrser/meta/props/base.rb
|
104
|
+
- lib/nrser/no_arg.rb
|
98
105
|
- lib/nrser/refinements.rb
|
99
106
|
- lib/nrser/refinements/array.rb
|
107
|
+
- lib/nrser/refinements/binding.rb
|
108
|
+
- lib/nrser/refinements/exception.rb
|
100
109
|
- lib/nrser/refinements/hash.rb
|
101
110
|
- lib/nrser/refinements/object.rb
|
102
111
|
- lib/nrser/refinements/pathname.rb
|
103
112
|
- lib/nrser/refinements/string.rb
|
113
|
+
- lib/nrser/refinements/types.rb
|
114
|
+
- lib/nrser/string.rb
|
104
115
|
- lib/nrser/truthy.rb
|
105
116
|
- lib/nrser/types.rb
|
106
117
|
- lib/nrser/types/any.rb
|
@@ -115,6 +126,7 @@ files:
|
|
115
126
|
- lib/nrser/types/maybe.rb
|
116
127
|
- lib/nrser/types/numbers.rb
|
117
128
|
- lib/nrser/types/strings.rb
|
129
|
+
- lib/nrser/types/symbol.rb
|
118
130
|
- lib/nrser/types/type.rb
|
119
131
|
- lib/nrser/types/where.rb
|
120
132
|
- lib/nrser/version.rb
|
@@ -133,6 +145,8 @@ files:
|
|
133
145
|
- spec/nrser/logger/level_sym_spec.rb
|
134
146
|
- spec/nrser/logger/send_log_spec.rb
|
135
147
|
- spec/nrser/logger/use_spec.rb
|
148
|
+
- spec/nrser/meta/class_attrs_spec.rb
|
149
|
+
- spec/nrser/meta/props_spec.rb
|
136
150
|
- spec/nrser/refinements/array_spec.rb
|
137
151
|
- spec/nrser/refinements/erb_spec.rb
|
138
152
|
- spec/nrser/refinements/format_exception_spec.rb
|
@@ -189,6 +203,8 @@ test_files:
|
|
189
203
|
- spec/nrser/logger/level_sym_spec.rb
|
190
204
|
- spec/nrser/logger/send_log_spec.rb
|
191
205
|
- spec/nrser/logger/use_spec.rb
|
206
|
+
- spec/nrser/meta/class_attrs_spec.rb
|
207
|
+
- spec/nrser/meta/props_spec.rb
|
192
208
|
- spec/nrser/refinements/array_spec.rb
|
193
209
|
- spec/nrser/refinements/erb_spec.rb
|
194
210
|
- spec/nrser/refinements/format_exception_spec.rb
|