sinclair 1.3.0 → 1.3.1
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/.rubocop.yml +7 -2
- data/Dockerfile +1 -1
- data/README.md +168 -15
- data/config/yardstick.yml +3 -0
- data/lib/sinclair.rb +56 -1
- data/lib/sinclair/method_definition.rb +27 -77
- data/lib/sinclair/method_definition/block_definition.rb +102 -0
- data/lib/sinclair/method_definition/string_definition.rb +82 -0
- data/lib/sinclair/version.rb +1 -1
- data/spec/integration/readme/sinclair/configurable_spec.rb +44 -0
- data/spec/integration/readme/sinclair_spec.rb +70 -0
- data/spec/integration/yard/sinclair/configurable_spec.rb +4 -0
- data/spec/integration/yard/sinclair/method_definition/block_definition_spec.rb +25 -0
- data/spec/integration/yard/sinclair/method_definition/string_definition_spec.rb +32 -0
- data/spec/integration/yard/sinclair/method_definition_spec.rb +2 -2
- data/spec/integration/yard/sinclair_spec.rb +30 -0
- data/spec/lib/sinclair/matchers/add_method_to_spec.rb +10 -0
- data/spec/lib/sinclair/method_definition/block_definition_spec.rb +30 -0
- data/spec/lib/sinclair/method_definition/string_definition_spec.rb +28 -0
- data/spec/lib/sinclair/method_definition_spec.rb +22 -63
- data/spec/support/models/default_value_builder.rb +21 -0
- data/spec/support/models/default_valueable.rb +11 -0
- data/spec/support/models/http_json_model.rb +27 -0
- data/spec/support/models/http_person.rb +11 -0
- data/spec/support/models/server.rb +18 -0
- data/spec/support/shared_examples/method_definition.rb +95 -0
- metadata +15 -2
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA256:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: c574eec3a6df9fca413808866c33a03da39ac3aea5a55ad588ea0532fdcb8058
|
4
|
+
data.tar.gz: ccdf5e26711abfeb0c9a2eee9f59cabc8a4ae912b624b976720b228b7be7adbd
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: 807968f16b1be5c991e7377aa888fc70548c8058945e7f137a6cfa53ef4a6359c5be19305a5184f8d2b1d7ed8343d815a8595b9deef8b1f266c7e5fbca4bc94f
|
7
|
+
data.tar.gz: 2dc1e2cbf9e95829d488e49ee6835775d99cc9743313b0b58ea50fc2ac739a471ecd98da35c9193dc631bcbfdfabe6cb665fa190a97afacf4b4d4ecfec9dde95
|
data/.rubocop.yml
CHANGED
@@ -7,6 +7,7 @@ AllCops:
|
|
7
7
|
Metrics/BlockLength:
|
8
8
|
Exclude:
|
9
9
|
- 'spec/**/*_spec.rb'
|
10
|
+
- 'spec/support/**/*.rb'
|
10
11
|
|
11
12
|
Metrics/LineLength:
|
12
13
|
Max: 100
|
@@ -20,12 +21,16 @@ RSpec/AlignLeftLetBrace:
|
|
20
21
|
|
21
22
|
RSpec/InstanceVariable:
|
22
23
|
Exclude:
|
23
|
-
-
|
24
|
-
-
|
24
|
+
- spec/integration/yard/sinclair/method_definition_spec.rb
|
25
|
+
- spec/integration/yard/sinclair/method_definition/block_definition_spec.rb
|
26
|
+
- spec/lib/sinclair/method_definition/block_definition_spec.rb
|
27
|
+
- spec/lib/sinclair/method_definition_spec.rb
|
25
28
|
|
26
29
|
RSpec/MultipleExpectations:
|
27
30
|
Exclude:
|
28
31
|
- spec/integration/yard/sinclair/method_definition_spec.rb
|
32
|
+
- spec/integration/yard/sinclair/method_definition/block_definition_spec.rb
|
33
|
+
- spec/integration/yard/sinclair/method_definition/string_definition_spec.rb
|
29
34
|
- spec/lib/sinclair_spec.rb
|
30
35
|
- spec/integration/readme/my_model_spec.rb
|
31
36
|
|
data/Dockerfile
CHANGED
data/README.md
CHANGED
@@ -8,11 +8,13 @@ Sinclair
|
|
8
8
|
|
9
9
|

|
10
10
|
|
11
|
-
This gem helps the creation of complex
|
11
|
+
This gem helps the creation of complex gems/concerns
|
12
|
+
that enables creation of methods on the fly through class
|
13
|
+
methods
|
12
14
|
|
13
15
|
Yard Documentation
|
14
16
|
-------------------
|
15
|
-
https://www.rubydoc.info/gems/sinclair/1.3.
|
17
|
+
https://www.rubydoc.info/gems/sinclair/1.3.1
|
16
18
|
|
17
19
|
Installation
|
18
20
|
---------------
|
@@ -22,7 +24,7 @@ Installation
|
|
22
24
|
gem install sinclair
|
23
25
|
```
|
24
26
|
|
25
|
-
- Or add
|
27
|
+
- Or add Sinclair to your `Gemfile` and `bundle install`:
|
26
28
|
|
27
29
|
```ruby
|
28
30
|
gem 'sinclair'
|
@@ -34,10 +36,12 @@ Installation
|
|
34
36
|
|
35
37
|
Usage
|
36
38
|
---------------
|
37
|
-
|
38
|
-
|
39
|
+
# Sinclair
|
40
|
+
Sinclair can actully be used in several ways, as an stand alone object capable of
|
41
|
+
adding methods to your class on the fly, as a builder inside a class method
|
42
|
+
or by extending it for more complex logics
|
39
43
|
|
40
|
-
|
44
|
+
## Stand Alone usage creating methods on the fly:
|
41
45
|
|
42
46
|
```ruby
|
43
47
|
|
@@ -56,18 +60,78 @@ adding methods to your class or by extending it for more complex logics
|
|
56
60
|
puts "Eighty => #{instance.eighty}" # Eighty => 80
|
57
61
|
```
|
58
62
|
|
59
|
-
|
63
|
+
## Builder in class method:
|
60
64
|
|
61
65
|
```ruby
|
66
|
+
class HttpJsonModel
|
67
|
+
attr_reader :json
|
62
68
|
|
63
|
-
|
64
|
-
|
69
|
+
class << self
|
70
|
+
def parse(attribute, path: [])
|
71
|
+
builder = Sinclair.new(self)
|
72
|
+
|
73
|
+
keys = (path + [attribute]).map(&:to_s)
|
74
|
+
|
75
|
+
builder.add_method(attribute) do
|
76
|
+
keys.inject(hash) { |h, key| h[key] }
|
77
|
+
end
|
78
|
+
|
79
|
+
builder.build
|
80
|
+
end
|
81
|
+
end
|
65
82
|
|
66
|
-
def initialize(
|
67
|
-
|
83
|
+
def initialize(json)
|
84
|
+
@json = json
|
68
85
|
end
|
69
86
|
|
70
|
-
def
|
87
|
+
def hash
|
88
|
+
@hash ||= JSON.parse(json)
|
89
|
+
end
|
90
|
+
end
|
91
|
+
|
92
|
+
class HttpPerson < HttpJsonModel
|
93
|
+
parse :uid
|
94
|
+
parse :name, path: [:personal_information]
|
95
|
+
parse :age, path: [:personal_information]
|
96
|
+
parse :username, path: [:digital_information]
|
97
|
+
parse :email, path: [:digital_information]
|
98
|
+
end
|
99
|
+
|
100
|
+
json = <<-JSON
|
101
|
+
{
|
102
|
+
"uid": "12sof511",
|
103
|
+
"personal_information":{
|
104
|
+
"name":"Bob",
|
105
|
+
"age": 21
|
106
|
+
},
|
107
|
+
"digital_information":{
|
108
|
+
"username":"lordbob",
|
109
|
+
"email":"lord@bob.com"
|
110
|
+
}
|
111
|
+
}
|
112
|
+
JSON
|
113
|
+
|
114
|
+
person = HttpPerson.new(json)
|
115
|
+
|
116
|
+
person.uid # returns '12sof511'
|
117
|
+
person.name # returns 'Bob'
|
118
|
+
person.age # returns 21
|
119
|
+
person.username # returns 'lordbob'
|
120
|
+
person.email # returns 'lord@bob.com'
|
121
|
+
```
|
122
|
+
|
123
|
+
## Extending the builder
|
124
|
+
|
125
|
+
```ruby
|
126
|
+
|
127
|
+
class ValidationBuilder < Sinclair
|
128
|
+
delegate :expected, to: :options_object
|
129
|
+
|
130
|
+
def initialize(klass, options={})
|
131
|
+
super
|
132
|
+
end
|
133
|
+
|
134
|
+
def add_validation(field)
|
71
135
|
add_method("#{field}_valid?", "#{field}.is_a?#{expected}")
|
72
136
|
end
|
73
137
|
|
@@ -149,9 +213,12 @@ adding methods to your class or by extending it for more complex logics
|
|
149
213
|
invalid_object.valid? # returns false
|
150
214
|
```
|
151
215
|
|
152
|
-
|
153
|
-
|
154
|
-
|
216
|
+
## Caching the result
|
217
|
+
If wanted, the result of the method can be stored in an
|
218
|
+
instance variable with the same name.
|
219
|
+
|
220
|
+
When caching, you can cache with type `:full` so that even `nil`
|
221
|
+
values are cached
|
155
222
|
|
156
223
|
```ruby
|
157
224
|
class MyModel
|
@@ -178,6 +245,92 @@ adding methods to your class or by extending it for more complex logics
|
|
178
245
|
model.cached_power # returns 9 (from cache)
|
179
246
|
```
|
180
247
|
|
248
|
+
```ruby
|
249
|
+
module DefaultValueable
|
250
|
+
def default_reader(*methods, value:, accept_nil: false)
|
251
|
+
DefaultValueBuilder.new(
|
252
|
+
self, value: value, accept_nil: accept_nil
|
253
|
+
).add_default_values(*methods)
|
254
|
+
end
|
255
|
+
end
|
256
|
+
|
257
|
+
class DefaultValueBuilder < Sinclair
|
258
|
+
def add_default_values(*methods)
|
259
|
+
default_value = value
|
260
|
+
|
261
|
+
methods.each do |method|
|
262
|
+
add_method(method, cached: cache_type) { default_value }
|
263
|
+
end
|
264
|
+
|
265
|
+
build
|
266
|
+
end
|
267
|
+
|
268
|
+
private
|
269
|
+
|
270
|
+
delegate :accept_nil, :value, to: :options_object
|
271
|
+
|
272
|
+
def cache_type
|
273
|
+
accept_nil ? :full : :simple
|
274
|
+
end
|
275
|
+
end
|
276
|
+
|
277
|
+
class Server
|
278
|
+
extend DefaultValueable
|
279
|
+
|
280
|
+
attr_writer :host, :port
|
281
|
+
|
282
|
+
default_reader :host, value: 'server.com', accept_nil: false
|
283
|
+
default_reader :port, value: 80, accept_nil: true
|
284
|
+
|
285
|
+
def url
|
286
|
+
return "http://#{host}" unless port
|
287
|
+
|
288
|
+
"http://#{host}:#{port}"
|
289
|
+
end
|
290
|
+
end
|
291
|
+
|
292
|
+
server = Server.new
|
293
|
+
|
294
|
+
server.url # returns 'http://server.com:80'
|
295
|
+
|
296
|
+
server.host = 'interstella.com'
|
297
|
+
server.port = 5555
|
298
|
+
server.url # returns 'http://interstella.com:5555'
|
299
|
+
|
300
|
+
server.host = nil
|
301
|
+
server.port = nil
|
302
|
+
server.url # return 'http://server.com'
|
303
|
+
```
|
304
|
+
|
305
|
+
# Sinclair::Configurable
|
306
|
+
|
307
|
+
Configurable is a module that, when used, can add configurations
|
308
|
+
to your classes/modules.
|
309
|
+
|
310
|
+
Configurations are read-only objects that can only be set using
|
311
|
+
the `configurable#configure` method
|
312
|
+
|
313
|
+
```ruby
|
314
|
+
class MyConfigurable
|
315
|
+
extend Sinclair::Configurable
|
316
|
+
|
317
|
+
configurable_with :host, :port
|
318
|
+
end
|
319
|
+
|
320
|
+
MyConfigurable.configure do |config|
|
321
|
+
config.host 'interstella.art'
|
322
|
+
config.port 5555
|
323
|
+
end
|
324
|
+
|
325
|
+
MyConfigurable.config.host # returns 'interstella.art'
|
326
|
+
MyConfigurable.config.port # returns 5555
|
327
|
+
|
328
|
+
MyConfigurable.reset_config
|
329
|
+
|
330
|
+
MyConfigurable.config.host # returns nil
|
331
|
+
MyConfigurable.config.port # returns nil
|
332
|
+
```
|
333
|
+
|
181
334
|
RSspec matcher
|
182
335
|
---------------
|
183
336
|
|
data/config/yardstick.yml
CHANGED
@@ -41,9 +41,12 @@ rules:
|
|
41
41
|
- Sinclair::Matchers::AddMethod#method
|
42
42
|
- Sinclair::Matchers::AddMethodTo#method
|
43
43
|
- Sinclair::Matchers::AddMethodTo#instance
|
44
|
+
- Sinclair::MethodDefinition#initialize
|
44
45
|
- Sinclair::MethodDefinition#name
|
45
46
|
- Sinclair::MethodDefinition#code
|
46
47
|
- Sinclair::MethodDefinition#block
|
48
|
+
- Sinclair::MethodDefinition::BlockDefinition#initialize
|
49
|
+
- Sinclair::MethodDefinition::StringDefinition#initialize
|
47
50
|
- Sinclair::OptionsParser#options
|
48
51
|
- Sinclair::OptionsParser#options_object
|
49
52
|
- Sinclair::ConfigFactory#initialize
|
data/lib/sinclair.rb
CHANGED
@@ -24,6 +24,61 @@ require 'active_support/all'
|
|
24
24
|
# instance.value # returns 10
|
25
25
|
# instance.value = 20
|
26
26
|
# instance.value # returns 20
|
27
|
+
#
|
28
|
+
# @example Usin cache
|
29
|
+
# module DefaultValueable
|
30
|
+
# def default_reader(*methods, value:, accept_nil: false)
|
31
|
+
# DefaultValueBuilder.new(
|
32
|
+
# self, value: value, accept_nil: accept_nil
|
33
|
+
# ).add_default_values(*methods)
|
34
|
+
# end
|
35
|
+
# end
|
36
|
+
#
|
37
|
+
# class DefaultValueBuilder < Sinclair
|
38
|
+
# def add_default_values(*methods)
|
39
|
+
# default_value = value
|
40
|
+
#
|
41
|
+
# methods.each do |method|
|
42
|
+
# add_method(method, cached: cache_type) { default_value }
|
43
|
+
# end
|
44
|
+
#
|
45
|
+
# build
|
46
|
+
# end
|
47
|
+
#
|
48
|
+
# private
|
49
|
+
#
|
50
|
+
# delegate :accept_nil, :value, to: :options_object
|
51
|
+
#
|
52
|
+
# def cache_type
|
53
|
+
# accept_nil ? :full : :simple
|
54
|
+
# end
|
55
|
+
# end
|
56
|
+
#
|
57
|
+
# class Server
|
58
|
+
# extend DefaultValueable
|
59
|
+
#
|
60
|
+
# attr_writer :host, :port
|
61
|
+
#
|
62
|
+
# default_reader :host, value: 'server.com', accept_nil: false
|
63
|
+
# default_reader :port, value: 80, accept_nil: true
|
64
|
+
#
|
65
|
+
# def url
|
66
|
+
# return "http://#{host}" unless port
|
67
|
+
#
|
68
|
+
# "http://#{host}:#{port}"
|
69
|
+
# end
|
70
|
+
# end
|
71
|
+
# server = Server.new
|
72
|
+
#
|
73
|
+
# server.url # returns 'http://server.com:80'
|
74
|
+
#
|
75
|
+
# server.host = 'interstella.com'
|
76
|
+
# server.port = 5555
|
77
|
+
# server.url # returns 'http://interstella.com:5555'
|
78
|
+
#
|
79
|
+
# server.host = nil
|
80
|
+
# server.port = nil
|
81
|
+
# server.url # return 'http://server.com'
|
27
82
|
class Sinclair
|
28
83
|
require 'sinclair/options_parser'
|
29
84
|
|
@@ -149,7 +204,7 @@ class Sinclair
|
|
149
204
|
# Person.new('john', 'wick').bond_name # returns 'wick, john wick'
|
150
205
|
# @return [Array<MethodDefinition>]
|
151
206
|
def add_method(name, code = nil, **options, &block)
|
152
|
-
definitions << MethodDefinition.
|
207
|
+
definitions << MethodDefinition.from(name, code, **options, &block)
|
153
208
|
end
|
154
209
|
|
155
210
|
# Evaluetes a block which will result in a String, the method code
|
@@ -7,45 +7,50 @@ class Sinclair
|
|
7
7
|
# Definition of the code or block to be aded as method
|
8
8
|
class MethodDefinition
|
9
9
|
include Sinclair::OptionsParser
|
10
|
+
|
11
|
+
autoload :BlockDefinition, 'sinclair/method_definition/block_definition'
|
12
|
+
autoload :StringDefinition, 'sinclair/method_definition/string_definition'
|
13
|
+
|
10
14
|
# Default options of initialization
|
11
15
|
DEFAULT_OPTIONS = {
|
12
16
|
cached: false
|
13
17
|
}.freeze
|
14
18
|
|
15
|
-
#
|
16
|
-
#
|
17
|
-
# @
|
18
|
-
#
|
19
|
-
#
|
20
|
-
|
21
|
-
|
22
|
-
|
23
|
-
|
24
|
-
|
19
|
+
# Creates a new instance based on arguments
|
20
|
+
#
|
21
|
+
# @return [MethodDefinition] When block is given, a
|
22
|
+
# new instance of {BlockDefinition} is returned,
|
23
|
+
# otherwise {StringDefinition} is returned
|
24
|
+
def self.from(name, code = nil, **options, &block)
|
25
|
+
if block
|
26
|
+
BlockDefinition.new(name, **options, &block)
|
27
|
+
else
|
28
|
+
StringDefinition.new(name, code, **options)
|
29
|
+
end
|
30
|
+
end
|
31
|
+
|
25
32
|
# @param name [String,Symbol] name of the method
|
26
|
-
# @param code [String] code to be evaluated as method
|
27
|
-
# @param block [Proc] block with code to be added as method
|
28
33
|
# @param options [Hash] Options of construction
|
29
34
|
# @option options cached [Boolean] Flag telling to create
|
30
35
|
# a method with cache
|
31
|
-
def initialize(name,
|
36
|
+
def initialize(name, **options)
|
32
37
|
@name = name
|
33
|
-
@code = code
|
34
38
|
@options = DEFAULT_OPTIONS.merge(options)
|
35
|
-
@block = block
|
36
39
|
end
|
37
40
|
|
38
41
|
# Adds the method to given klass
|
39
42
|
#
|
40
|
-
#
|
43
|
+
# This should be implemented on child classes
|
41
44
|
#
|
42
|
-
# @
|
45
|
+
# @param _klass [Class] class which will receive the new method
|
46
|
+
#
|
47
|
+
# @example Using block method with no options
|
43
48
|
# class MyModel
|
44
49
|
# end
|
45
50
|
#
|
46
51
|
# instance = MyModel.new
|
47
52
|
#
|
48
|
-
# method_definition = Sinclair::MethodDefinition.
|
53
|
+
# method_definition = Sinclair::MethodDefinition.from(
|
49
54
|
# :sequence, '@x = @x.to_i ** 2 + 1'
|
50
55
|
# )
|
51
56
|
#
|
@@ -66,7 +71,7 @@ class Sinclair
|
|
66
71
|
#
|
67
72
|
# instance = MyModel.new
|
68
73
|
#
|
69
|
-
# method_definition = Sinclair::MethodDefinition.
|
74
|
+
# method_definition = Sinclair::MethodDefinition.from(:sequence) do
|
70
75
|
# @x = @x.to_i ** 2 + 1
|
71
76
|
# end
|
72
77
|
#
|
@@ -83,12 +88,9 @@ class Sinclair
|
|
83
88
|
# instance.instance_variable_get(:@x) # returns 1
|
84
89
|
#
|
85
90
|
# @return [Symbol] name of the created method
|
86
|
-
def build(
|
87
|
-
|
88
|
-
|
89
|
-
else
|
90
|
-
build_block_method(klass)
|
91
|
-
end
|
91
|
+
def build(_klass)
|
92
|
+
raise 'Build is implemented in subclasses. ' \
|
93
|
+
"Use #{self.class}.from to initialize a proper object"
|
92
94
|
end
|
93
95
|
|
94
96
|
private
|
@@ -103,57 +105,5 @@ class Sinclair
|
|
103
105
|
#
|
104
106
|
# @return [Boolean]
|
105
107
|
alias cached? cached
|
106
|
-
|
107
|
-
# @private
|
108
|
-
#
|
109
|
-
# Add method from block
|
110
|
-
#
|
111
|
-
# @return [Symbol] name of the created method
|
112
|
-
def build_block_method(klass)
|
113
|
-
klass.send(:define_method, name, method_block)
|
114
|
-
end
|
115
|
-
|
116
|
-
# @private
|
117
|
-
#
|
118
|
-
# Returns the block that will be used for method creattion
|
119
|
-
#
|
120
|
-
# @return [Proc]
|
121
|
-
def method_block
|
122
|
-
return block unless cached?
|
123
|
-
|
124
|
-
inner_block = block
|
125
|
-
method_name = name
|
126
|
-
|
127
|
-
proc do
|
128
|
-
instance_variable_get("@#{method_name}") ||
|
129
|
-
instance_variable_set(
|
130
|
-
"@#{method_name}",
|
131
|
-
instance_eval(&inner_block)
|
132
|
-
)
|
133
|
-
end
|
134
|
-
end
|
135
|
-
|
136
|
-
# @private
|
137
|
-
#
|
138
|
-
# Add method from String code
|
139
|
-
#
|
140
|
-
# @return [Symbol] name of the created method
|
141
|
-
def build_code_method(klass)
|
142
|
-
klass.module_eval(code_definition, __FILE__, __LINE__ + 1)
|
143
|
-
end
|
144
|
-
|
145
|
-
# @private
|
146
|
-
#
|
147
|
-
# Builds full code of method
|
148
|
-
#
|
149
|
-
# @return [String]
|
150
|
-
def code_definition
|
151
|
-
code_line = cached? ? "@#{name} ||= #{code}" : code
|
152
|
-
<<-CODE
|
153
|
-
def #{name}
|
154
|
-
#{code_line}
|
155
|
-
end
|
156
|
-
CODE
|
157
|
-
end
|
158
108
|
end
|
159
109
|
end
|