nrser 0.0.18 → 0.0.19
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/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
|