sinclair 1.10.0 → 1.12.0

Sign up to get free protection for your applications and to get access to all the features.
Files changed (38) hide show
  1. checksums.yaml +4 -4
  2. data/.rubocop.yml +6 -0
  3. data/README.md +444 -324
  4. data/config/check_specs.yml +5 -5
  5. data/config/yardstick.yml +4 -0
  6. data/lib/sinclair/matchers/add_class_method.rb +0 -1
  7. data/lib/sinclair/method_builder/base.rb +32 -2
  8. data/lib/sinclair/method_builder/block_method_builder.rb +1 -12
  9. data/lib/sinclair/method_builder/call_method_builder.rb +10 -27
  10. data/lib/sinclair/method_builder/string_method_builder.rb +2 -29
  11. data/lib/sinclair/method_builder.rb +4 -20
  12. data/lib/sinclair/method_definition/block_definition.rb +2 -0
  13. data/lib/sinclair/method_definition/call_definition.rb +15 -18
  14. data/lib/sinclair/method_definition/parameter_builder.rb +89 -0
  15. data/lib/sinclair/method_definition/parameter_helper.rb +124 -0
  16. data/lib/sinclair/method_definition/string_definition.rb +26 -2
  17. data/lib/sinclair/method_definition.rb +42 -3
  18. data/lib/sinclair/method_definitions.rb +20 -22
  19. data/lib/sinclair/version.rb +1 -1
  20. data/lib/sinclair.rb +149 -62
  21. data/spec/integration/readme/sinclair/types_of_definition_spec.rb +61 -0
  22. data/spec/integration/yard/sinclair/add_class_method_spec.rb +51 -0
  23. data/spec/integration/yard/sinclair/add_method_spec.rb +60 -0
  24. data/spec/integration/yard/sinclair/eval_and_add_method_spec.rb +26 -0
  25. data/spec/integration/yard/sinclair_spec.rb +0 -83
  26. data/spec/lib/sinclair/method_builder/base_spec.rb +15 -0
  27. data/spec/lib/sinclair/method_builder/string_method_builder_spec.rb +24 -1
  28. data/spec/lib/sinclair/method_definition/call_definition_spec.rb +14 -17
  29. data/spec/lib/sinclair/method_definition/parameter_builder_spec.rb +81 -0
  30. data/spec/lib/sinclair/method_definition/string_definition_spec.rb +60 -29
  31. data/spec/lib/sinclair/method_definition_spec.rb +77 -2
  32. data/spec/lib/sinclair/method_definitions_spec.rb +15 -16
  33. data/spec/lib/sinclair_spec.rb +6 -160
  34. data/spec/support/models/dummy_builder.rb +5 -1
  35. data/spec/support/models/dummy_class_builder.rb +4 -0
  36. data/spec/support/models/person.rb +1 -1
  37. data/spec/support/shared_examples/sinclair.rb +118 -0
  38. metadata +11 -2
data/README.md CHANGED
@@ -13,22 +13,24 @@ This gem helps the creation of complex gems/concerns
13
13
  that enables creation of methods on the fly through class
14
14
  methods
15
15
 
16
- Next release: [1.11.0](https://github.com/darthjee/sinclair/compare/1.10.0...master)
16
+ Current Release: [1.12.0](https://github.com/darthjee/sinclair/tree/1.12.0)
17
+
18
+ [Next release](https://github.com/darthjee/sinclair/compare/1.12.0...master)
17
19
 
18
20
  Yard Documentation
19
21
  -------------------
20
- [https://www.rubydoc.info/gems/sinclair/1.10.0](https://www.rubydoc.info/gems/sinclair/1.10.0)
22
+ [https://www.rubydoc.info/gems/sinclair/1.12.0](https://www.rubydoc.info/gems/sinclair/1.12.0)
21
23
 
22
24
  Installation
23
25
  ---------------
24
26
 
25
- - Install it
27
+ - Install it
26
28
 
27
29
  ```ruby
28
30
  gem install sinclair
29
31
  ```
30
32
 
31
- - Or add Sinclair to your `Gemfile` and `bundle install`:
33
+ - Or add Sinclair to your `Gemfile` and `bundle install`:
32
34
 
33
35
  ```ruby
34
36
  gem 'sinclair'
@@ -41,193 +43,202 @@ Installation
41
43
  Usage
42
44
  ---------------
43
45
  ### Sinclair builder
44
- Sinclair can actually be used in several ways, as a stand alone object capable of
45
- adding methods to your class on the fly, as a builder inside a class method
46
- or by extending it for more complex logics
46
+ Sinclair can actually be used in several ways
47
+ - as a stand alone object capable of adding methods to your class on the fly
48
+ - as a builder inside a class method
49
+ - extending the builder for more complex logics
47
50
 
48
- #### Stand Alone usage creating methods on the fly
49
- ```ruby
51
+ <details>
52
+ <summary>Stand Alone usage creating methods on the fly</summary>
50
53
 
51
- class Clazz
52
- end
54
+ ```ruby
55
+ class Clazz
56
+ end
53
57
 
54
- builder = Sinclair.new(Clazz)
58
+ builder = Sinclair.new(Clazz)
55
59
 
56
- builder.add_method(:twenty, '10 + 10')
57
- builder.add_method(:eighty) { 4 * twenty }
58
- builder.add_class_method(:one_hundred) { 100 }
59
- builder.add_class_method(:one_hundred_twenty, 'one_hundred + 20')
60
- builder.build
60
+ builder.add_method(:twenty, '10 + 10')
61
+ builder.add_method(:eighty) { 4 * twenty }
62
+ builder.add_class_method(:one_hundred) { 100 }
63
+ builder.add_class_method(:one_hundred_twenty, 'one_hundred + 20')
64
+ builder.build
61
65
 
62
- instance = Clazz.new
66
+ instance = Clazz.new
63
67
 
64
- puts "Twenty => #{instance.twenty}" # Twenty => 20
65
- puts "Eighty => #{instance.eighty}" # Eighty => 80
68
+ puts "Twenty => #{instance.twenty}" # Twenty => 20
69
+ puts "Eighty => #{instance.eighty}" # Eighty => 80
66
70
 
67
- puts "One Hundred => #{Clazz.one_hundred}" # One Hundred => 100
68
- puts "One Hundred => #{Clazz.one_hundred_twenty}" # One Hundred Twenty => 120
71
+ puts "One Hundred => #{Clazz.one_hundred}" # One Hundred => 100
72
+ puts "One Hundred => #{Clazz.one_hundred_twenty}" # One Hundred Twenty => 120
69
73
  ```
74
+ </details>
70
75
 
71
- #### Builder in class method
76
+ <details>
77
+ <summary>Builder in class method</summary>
72
78
 
73
79
  ```ruby
74
- class HttpJsonModel
75
- attr_reader :json
80
+ class HttpJsonModel
81
+ attr_reader :json
76
82
 
77
- class << self
78
- def parse(attribute, path: [])
79
- builder = Sinclair.new(self)
80
-
81
- keys = (path + [attribute]).map(&:to_s)
83
+ class << self
84
+ def parse(attribute, path: [])
85
+ builder = Sinclair.new(self)
82
86
 
83
- builder.add_method(attribute) do
84
- keys.inject(hash) { |h, key| h[key] }
85
- end
87
+ keys = (path + [attribute]).map(&:to_s)
86
88
 
87
- builder.build
89
+ builder.add_method(attribute) do
90
+ keys.inject(hash) { |h, key| h[key] }
88
91
  end
89
- end
90
92
 
91
- def initialize(json)
92
- @json = json
93
- end
94
-
95
- def hash
96
- @hash ||= JSON.parse(json)
93
+ builder.build
97
94
  end
98
95
  end
99
96
 
100
- class HttpPerson < HttpJsonModel
101
- parse :uid
102
- parse :name, path: [:personal_information]
103
- parse :age, path: [:personal_information]
104
- parse :username, path: [:digital_information]
105
- parse :email, path: [:digital_information]
97
+ def initialize(json)
98
+ @json = json
106
99
  end
107
100
 
108
- json = <<-JSON
109
- {
110
- "uid": "12sof511",
111
- "personal_information":{
112
- "name":"Bob",
113
- "age": 21
114
- },
115
- "digital_information":{
116
- "username":"lordbob",
117
- "email":"lord@bob.com"
118
- }
101
+ def hash
102
+ @hash ||= JSON.parse(json)
103
+ end
104
+ end
105
+
106
+ class HttpPerson < HttpJsonModel
107
+ parse :uid
108
+ parse :name, path: [:personal_information]
109
+ parse :age, path: [:personal_information]
110
+ parse :username, path: [:digital_information]
111
+ parse :email, path: [:digital_information]
112
+ end
113
+
114
+ json = <<-JSON
115
+ {
116
+ "uid": "12sof511",
117
+ "personal_information":{
118
+ "name":"Bob",
119
+ "age": 21
120
+ },
121
+ "digital_information":{
122
+ "username":"lordbob",
123
+ "email":"lord@bob.com"
119
124
  }
120
- JSON
125
+ }
126
+ JSON
121
127
 
122
- person = HttpPerson.new(json)
128
+ person = HttpPerson.new(json)
123
129
 
124
- person.uid # returns '12sof511'
125
- person.name # returns 'Bob'
126
- person.age # returns 21
127
- person.username # returns 'lordbob'
128
- person.email # returns 'lord@bob.com'
130
+ person.uid # returns '12sof511'
131
+ person.name # returns 'Bob'
132
+ person.age # returns 21
133
+ person.username # returns 'lordbob'
134
+ person.email # returns 'lord@bob.com'
129
135
  ```
136
+ </details>
130
137
 
131
- ```ruby
132
- module EnvSettings
133
- def env_prefix(new_prefix=nil)
134
- @env_prefix = new_prefix if new_prefix
135
- @env_prefix
136
- end
138
+ <details>
139
+ <summary>Class method adding class methods</summary>
137
140
 
138
- def from_env(*method_names)
139
- builder = Sinclair.new(self)
141
+ ```ruby
142
+ module EnvSettings
143
+ def env_prefix(new_prefix=nil)
144
+ @env_prefix = new_prefix if new_prefix
145
+ @env_prefix
146
+ end
140
147
 
141
- method_names.each do |method_name|
142
- env_key = [env_prefix, method_name].compact.join('_').upcase
148
+ def from_env(*method_names)
149
+ builder = Sinclair.new(self)
143
150
 
144
- builder.add_class_method(method_name, cached: true) do
145
- ENV[env_key]
146
- end
151
+ method_names.each do |method_name|
152
+ env_key = [env_prefix, method_name].compact.join('_').upcase
147
153
 
148
- builder.build
154
+ builder.add_class_method(method_name, cached: true) do
155
+ ENV[env_key]
149
156
  end
157
+
158
+ builder.build
150
159
  end
151
160
  end
161
+ end
152
162
 
153
- class MyServerConfig
154
- extend EnvSettings
163
+ class MyServerConfig
164
+ extend EnvSettings
155
165
 
156
- env_prefix :server
166
+ env_prefix :server
157
167
 
158
- from_env :host, :port
159
- end
168
+ from_env :host, :port
169
+ end
160
170
 
161
- ENV['SERVER_HOST'] = 'myserver.com'
162
- ENV['SERVER_PORT'] = '9090'
171
+ ENV['SERVER_HOST'] = 'myserver.com'
172
+ ENV['SERVER_PORT'] = '9090'
163
173
 
164
- MyServerConfig.host # returns 'myserver.com'
165
- MyServerConfig.port # returns '9090'
174
+ MyServerConfig.host # returns 'myserver.com'
175
+ MyServerConfig.port # returns '9090'
166
176
  ```
177
+ </details>
167
178
 
168
- #### Extending the builder
179
+ <details>
180
+ <summary>Extending the builder</summary>
169
181
 
170
182
  ```ruby
183
+ class ValidationBuilder < Sinclair
184
+ delegate :expected, to: :options_object
171
185
 
172
- class ValidationBuilder < Sinclair
173
- delegate :expected, to: :options_object
174
-
175
- def initialize(klass, options={})
176
- super
177
- end
178
-
179
- def add_validation(field)
180
- add_method("#{field}_valid?", "#{field}.is_a?#{expected}")
181
- end
186
+ def initialize(klass, options={})
187
+ super
188
+ end
182
189
 
183
- def add_accessors(fields)
184
- klass.send(:attr_accessor, *fields)
185
- end
190
+ def add_validation(field)
191
+ add_method("#{field}_valid?", "#{field}.is_a?#{expected}")
186
192
  end
187
193
 
188
- module MyConcern
189
- extend ActiveSupport::Concern
194
+ def add_accessors(fields)
195
+ klass.send(:attr_accessor, *fields)
196
+ end
197
+ end
190
198
 
191
- class_methods do
192
- def validate(*fields, expected_class)
193
- builder = ::ValidationBuilder.new(self, expected: expected_class)
199
+ module MyConcern
200
+ extend ActiveSupport::Concern
194
201
 
195
- validatable_fields.concat(fields)
196
- builder.add_accessors(fields)
202
+ class_methods do
203
+ def validate(*fields, expected_class)
204
+ builder = ::ValidationBuilder.new(self, expected: expected_class)
197
205
 
198
- fields.each do |field|
199
- builder.add_validation(field)
200
- end
206
+ validatable_fields.concat(fields)
207
+ builder.add_accessors(fields)
201
208
 
202
- builder.build
209
+ fields.each do |field|
210
+ builder.add_validation(field)
203
211
  end
204
212
 
205
- def validatable_fields
206
- @validatable_fields ||= []
207
- end
213
+ builder.build
208
214
  end
209
215
 
210
- def valid?
211
- self.class.validatable_fields.all? do |field|
212
- public_send("#{field}_valid?")
213
- end
216
+ def validatable_fields
217
+ @validatable_fields ||= []
214
218
  end
215
219
  end
216
220
 
217
- class MyClass
218
- include MyConcern
219
- validate :name, :surname, String
220
- validate :age, :legs, Integer
221
-
222
- def initialize(name: nil, surname: nil, age: nil, legs: nil)
223
- @name = name
224
- @surname = surname
225
- @age = age
226
- @legs = legs
221
+ def valid?
222
+ self.class.validatable_fields.all? do |field|
223
+ public_send("#{field}_valid?")
227
224
  end
228
225
  end
226
+ end
227
+
228
+ class MyClass
229
+ include MyConcern
230
+ validate :name, :surname, String
231
+ validate :age, :legs, Integer
232
+
233
+ def initialize(name: nil, surname: nil, age: nil, legs: nil)
234
+ @name = name
235
+ @surname = surname
236
+ @age = age
237
+ @legs = legs
238
+ end
239
+ end
229
240
 
230
- instance = MyClass.new
241
+ instance = MyClass.new
231
242
  ```
232
243
 
233
244
  the instance will respond to the methods
@@ -238,25 +249,103 @@ or by extending it for more complex logics
238
249
  ```valid?```.
239
250
 
240
251
  ```ruby
241
- valid_object = MyClass.new(
242
- name: :name,
243
- surname: 'surname',
244
- age: 20,
245
- legs: 2
246
- )
247
- valid_object.valid? # returns true
252
+ valid_object = MyClass.new(
253
+ name: :name,
254
+ surname: 'surname',
255
+ age: 20,
256
+ legs: 2
257
+ )
258
+ valid_object.valid? # returns true
248
259
  ```
249
260
 
250
261
  ```ruby
262
+ invalid_object = MyClass.new(
263
+ name: 'name',
264
+ surname: 'surname',
265
+ age: 20,
266
+ legs: 2
267
+ )
268
+ invalid_object.valid? # returns false
269
+ ```
270
+ </details>
251
271
 
252
- invalid_object = MyClass.new(
253
- name: 'name',
254
- surname: 'surname',
255
- age: 20,
256
- legs: 2
257
- )
258
- invalid_object.valid? # returns false
272
+ #### Different ways of adding the methods
273
+ There are different ways to add a method, each accepting different options
274
+
275
+ <details>
276
+ <summary>Define method using block</summary>
277
+
278
+ Block methods accepts, as option
279
+ - [cache](#caching-the-result): defining the cashing of results
280
+
281
+ ```ruby
282
+ klass = Class.new
283
+ instance = klass.new
284
+
285
+ builder = Sinclair.new(klass)
286
+ builder.add_method(:random_number) { Random.rand(10..20) }
287
+ builder.build
288
+
289
+ instance.random_number # returns a number between 10 and 20
290
+ ```
291
+ </details>
292
+
293
+ <details>
294
+ <summary>Define method using string</summary>
295
+
296
+ String methods accepts, as option
297
+ - [cache](#caching-the-result): defining the cashing of results
298
+ - parameters: defining accepted parameters
299
+ - named_parameters: defining accepted named parameters
300
+
301
+ ```ruby
302
+ # Example without parameters
303
+
304
+ class MyClass
305
+ end
306
+ instance = MyClass.new
307
+
308
+ builder = Sinclair.new(MyClass)
309
+ builder.add_method(:random_number, "Random.rand(10..20)")
310
+ builder.build
311
+
312
+ instance.random_number # returns a number between 10 and 20
313
+ ```
314
+
315
+ ```ruby
316
+ # Example with parameters
317
+
318
+ class MyClass
319
+ end
320
+
321
+ builder = described_class.new(MyClass)
322
+ builder.add_class_method(
323
+ :function, 'a ** b + c', parameters: [:a], named_parameters: [:b, { c: 15 }]
324
+ )
325
+ builder.build
326
+
327
+ MyClass.function(10, b: 2) # returns 115
328
+ ```
329
+ </details>
330
+
331
+ <details>
332
+ <summary>Define method using a call to the class</summary>
333
+
334
+ Call method definitions right now have no options available
335
+
336
+ ```ruby
337
+ class MyClass
338
+ end
339
+
340
+ builder = Sinclair.new(MyClass)
341
+ builder.add_class_method(:attr_accessor, :number, type: :call)
342
+ builder.build
343
+
344
+ MyClass.number # returns nil
345
+ MyClass.number = 10
346
+ MyClass.number # returns 10
259
347
  ```
348
+ </details>
260
349
 
261
350
  #### Caching the result
262
351
  If wanted, the result of the method can be stored in an
@@ -265,87 +354,95 @@ instance variable with the same name.
265
354
  When caching, you can cache with type `:full` so that even `nil`
266
355
  values are cached
267
356
 
357
+ <details>
358
+ <summary>Example of simple cache usage</summary>
359
+
268
360
  ```ruby
269
- class MyModel
270
- attr_accessor :base, :expoent
271
- end
361
+ class MyModel
362
+ attr_accessor :base, :expoent
363
+ end
272
364
 
273
- builder = Sinclair.new(MyModel)
365
+ builder = Sinclair.new(MyModel)
274
366
 
275
- builder.add_method(:cached_power, cached: true) do
276
- base ** expoent
277
- end
367
+ builder.add_method(:cached_power, cached: true) do
368
+ base ** expoent
369
+ end
278
370
 
279
- # equivalent of builder.add_method(:cached_power) do
280
- # @cached_power ||= base ** expoent
281
- # end
371
+ # equivalent of builder.add_method(:cached_power) do
372
+ # @cached_power ||= base ** expoent
373
+ # end
282
374
 
283
- builder.build
375
+ builder.build
284
376
 
285
- model.base = 3
286
- model.expoent = 2
377
+ model.base = 3
378
+ model.expoent = 2
287
379
 
288
- model.cached_power # returns 9
289
- model.expoent = 3
290
- model.cached_power # returns 9 (from cache)
380
+ model.cached_power # returns 9
381
+ model.expoent = 3
382
+ model.cached_power # returns 9 (from cache)
291
383
  ```
384
+ </details>
385
+
386
+ <details>
387
+ <summary>Usage of different cache types</summary>
292
388
 
293
389
  ```ruby
294
- module DefaultValueable
295
- def default_reader(*methods, value:, accept_nil: false)
296
- DefaultValueBuilder.new(
297
- self, value: value, accept_nil: accept_nil
298
- ).add_default_values(*methods)
299
- end
390
+ module DefaultValueable
391
+ def default_reader(*methods, value:, accept_nil: false)
392
+ DefaultValueBuilder.new(
393
+ self, value: value, accept_nil: accept_nil
394
+ ).add_default_values(*methods)
300
395
  end
396
+ end
301
397
 
302
- class DefaultValueBuilder < Sinclair
303
- def add_default_values(*methods)
304
- default_value = value
305
-
306
- methods.each do |method|
307
- add_method(method, cached: cache_type) { default_value }
308
- end
398
+ class DefaultValueBuilder < Sinclair
399
+ def add_default_values(*methods)
400
+ default_value = value
309
401
 
310
- build
402
+ methods.each do |method|
403
+ add_method(method, cached: cache_type) { default_value }
311
404
  end
312
405
 
313
- private
406
+ build
407
+ end
314
408
 
315
- delegate :accept_nil, :value, to: :options_object
409
+ private
316
410
 
317
- def cache_type
318
- accept_nil ? :full : :simple
319
- end
411
+ delegate :accept_nil, :value, to: :options_object
412
+
413
+ def cache_type
414
+ accept_nil ? :full : :simple
320
415
  end
416
+ end
321
417
 
322
- class Server
323
- extend DefaultValueable
418
+ class Server
419
+ extend DefaultValueable
324
420
 
325
- attr_writer :host, :port
421
+ attr_writer :host, :port
326
422
 
327
- default_reader :host, value: 'server.com', accept_nil: false
328
- default_reader :port, value: 80, accept_nil: true
423
+ default_reader :host, value: 'server.com', accept_nil: false
424
+ default_reader :port, value: 80, accept_nil: true
329
425
 
330
- def url
331
- return "http://#{host}" unless port
426
+ def url
427
+ return "http://#{host}" unless port
332
428
 
333
- "http://#{host}:#{port}"
334
- end
429
+ "http://#{host}:#{port}"
335
430
  end
431
+ end
336
432
 
337
- server = Server.new
433
+ server = Server.new
338
434
 
339
- server.url # returns 'http://server.com:80'
435
+ server.url # returns 'http://server.com:80'
340
436
 
341
- server.host = 'interstella.com'
342
- server.port = 5555
343
- server.url # returns 'http://interstella.com:5555'
437
+ server.host = 'interstella.com'
438
+ server.port = 5555
439
+ server.url # returns 'http://interstella.com:5555'
344
440
 
345
- server.host = nil
346
- server.port = nil
347
- server.url # return 'http://server.com'
441
+ server.host = nil
442
+ server.port = nil
443
+ server.url # return 'http://server.com'
348
444
  ```
445
+ </details>
349
446
 
350
447
  ### Sinclair::Configurable
351
448
 
@@ -356,214 +453,236 @@ Configurations are read-only objects that can only be set using
356
453
  the `configurable#configure` method which accepts a block or
357
454
  hash
358
455
 
456
+ <details>
457
+ <summary>Using configurable</summary>
458
+
359
459
  ```ruby
360
- module MyConfigurable
361
- extend Sinclair::Configurable
460
+ module MyConfigurable
461
+ extend Sinclair::Configurable
362
462
 
363
- # port is defaulted to 80
364
- configurable_with :host, port: 80
365
- end
463
+ # port is defaulted to 80
464
+ configurable_with :host, port: 80
465
+ end
366
466
 
367
- MyConfigurable.configure(port: 5555) do |config|
368
- config.host 'interstella.art'
369
- end
467
+ MyConfigurable.configure(port: 5555) do |config|
468
+ config.host 'interstella.art'
469
+ end
370
470
 
371
- MyConfigurable.config.host # returns 'interstella.art'
372
- MyConfigurable.config.port # returns 5555
471
+ MyConfigurable.config.host # returns 'interstella.art'
472
+ MyConfigurable.config.port # returns 5555
373
473
 
374
- # Configurable enables options that can be passed
375
- MyConfigurable.as_options.host # returns 'interstella.art'
474
+ # Configurable enables options that can be passed
475
+ MyConfigurable.as_options.host # returns 'interstella.art'
376
476
 
377
- # Configurable enables options that can be passed with custom values
378
- MyConfigurable.as_options(host: 'other').host # returns 'other'
477
+ # Configurable enables options that can be passed with custom values
478
+ MyConfigurable.as_options(host: 'other').host # returns 'other'
379
479
 
380
- MyConfigurable.reset_config
480
+ MyConfigurable.reset_config
381
481
 
382
- MyConfigurable.config.host # returns nil
383
- MyConfigurable.config.port # returns 80
482
+ MyConfigurable.config.host # returns nil
483
+ MyConfigurable.config.port # returns 80
384
484
  ```
485
+ </details>
385
486
 
386
487
  Configurations can also be done through custom classes
387
488
 
388
- ```ruby
389
- class MyServerConfig < Sinclair::Config
390
- config_attributes :host, :port
489
+ <details>
490
+ <summary>Using configration class</summary>
391
491
 
392
- def url
393
- if @port
394
- "http://#{@host}:#{@port}"
395
- else
396
- "http://#{@host}"
397
- end
492
+ ```ruby
493
+ class MyServerConfig < Sinclair::Config
494
+ config_attributes :host, :port
495
+
496
+ def url
497
+ if @port
498
+ "http://#{@host}:#{@port}"
499
+ else
500
+ "http://#{@host}"
398
501
  end
399
502
  end
503
+ end
400
504
 
401
- class Client
402
- extend Sinclair::Configurable
505
+ class Client
506
+ extend Sinclair::Configurable
403
507
 
404
- configurable_by MyServerConfig
405
- end
508
+ configurable_by MyServerConfig
509
+ end
406
510
 
407
- Client.configure do
408
- host 'interstella.com'
409
- end
511
+ Client.configure do
512
+ host 'interstella.com'
513
+ end
410
514
 
411
- Client.config.url # returns 'http://interstella.com'
515
+ Client.config.url # returns 'http://interstella.com'
412
516
 
413
- Client.configure do |config|
414
- config.port 8080
415
- end
517
+ Client.configure do |config|
518
+ config.port 8080
519
+ end
416
520
 
417
- Client.config.url # returns 'http://interstella.com:8080'
521
+ Client.config.url # returns 'http://interstella.com:8080'
418
522
  ```
523
+ </details>
419
524
 
420
525
  ### Sinclair::EnvSettable
421
526
 
422
527
  Settable allows classes to extract configuration from environments through
423
528
  a simple meta-programable way
424
529
 
530
+ <details>
531
+ <summary>Using env settable example</summary>
532
+
425
533
  ```ruby
426
- class ServiceClient
427
- extend Sinclair::EnvSettable
428
- attr_reader :username, :password, :host, :port
534
+ class ServiceClient
535
+ extend Sinclair::EnvSettable
536
+ attr_reader :username, :password, :host, :port
429
537
 
430
- settings_prefix 'SERVICE'
538
+ settings_prefix 'SERVICE'
431
539
 
432
- with_settings :username, :password, port: 80, hostname: 'my-host.com'
540
+ with_settings :username, :password, port: 80, hostname: 'my-host.com'
433
541
 
434
- def self.default
435
- @default ||= new
436
- end
542
+ def self.default
543
+ @default ||= new
544
+ end
437
545
 
438
- def initialize(
439
- username: self.class.username,
440
- password: self.class.password,
441
- port: self.class.port,
442
- hostname: self.class.hostname
443
- )
444
- @username = username
445
- @password = password
446
- @port = port
447
- @hostname = hostname
448
- end
546
+ def initialize(
547
+ username: self.class.username,
548
+ password: self.class.password,
549
+ port: self.class.port,
550
+ hostname: self.class.hostname
551
+ )
552
+ @username = username
553
+ @password = password
554
+ @port = port
555
+ @hostname = hostname
449
556
  end
557
+ end
450
558
 
451
- ENV['SERVICE_USERNAME'] = 'my-login'
452
- ENV['SERVICE_HOSTNAME'] = 'host.com'
559
+ ENV['SERVICE_USERNAME'] = 'my-login'
560
+ ENV['SERVICE_HOSTNAME'] = 'host.com'
453
561
 
454
- ServiceClient.default # returns #<ServiceClient:0x0000556fa1b366e8 @username="my-login", @password=nil, @port=80, @hostname="host.com">'
562
+ ServiceClient.default # returns #<ServiceClient:0x0000556fa1b366e8 @username="my-login", @password=nil, @port=80, @hostname="host.com">'
455
563
  ```
564
+ </details>
456
565
 
457
566
  ### Sinclair::Options
458
567
  Options allows projects to have an easy to configure option object
459
568
 
569
+ <details>
570
+ <summary>Example of using Options</summary>
571
+
460
572
  ```ruby
461
- class ConnectionOptions < Sinclair::Options
462
- with_options :timeout, :retries, port: 443, protocol: 'https'
573
+ class ConnectionOptions < Sinclair::Options
574
+ with_options :timeout, :retries, port: 443, protocol: 'https'
463
575
 
464
- # skip_validation if you dont want to validate intialization arguments
465
- end
576
+ # skip_validation if you dont want to validate intialization arguments
577
+ end
466
578
 
467
- options = ConnectionOptions.new(
468
- timeout: 10,
469
- protocol: 'http'
470
- )
579
+ options = ConnectionOptions.new(
580
+ timeout: 10,
581
+ protocol: 'http'
582
+ )
471
583
 
472
- options.timeout # returns 10
473
- options.retries # returns nil
474
- options.protocol # returns 'http'
475
- options.port # returns 443
584
+ options.timeout # returns 10
585
+ options.retries # returns nil
586
+ options.protocol # returns 'http'
587
+ options.port # returns 443
476
588
 
477
- ConnectionOptions.new(invalid: 10) # raises Sinclair::Exception::InvalidOptions
589
+ ConnectionOptions.new(invalid: 10) # raises Sinclair::Exception::InvalidOptions
478
590
  ```
591
+ </details>
479
592
 
480
593
  ### Sinclair::Comparable
481
594
  Comparable allows a class to implement quickly a `==` method comparing given attributes
482
595
 
596
+ <details>
597
+ <summary>Example of Comparable usage</summary>
598
+
483
599
  ```ruby
484
- class SampleModel
485
- include Sinclair::Comparable
600
+ class SampleModel
601
+ include Sinclair::Comparable
486
602
 
487
- comparable_by :name
488
- attr_reader :name, :age
603
+ comparable_by :name
604
+ attr_reader :name, :age
489
605
 
490
- def initialize(name: nil, age: nil)
491
- @name = name
492
- @age = age
493
- end
606
+ def initialize(name: nil, age: nil)
607
+ @name = name
608
+ @age = age
494
609
  end
610
+ end
495
611
 
496
- model1 = model_class.new(name: 'jack', age: 21)
497
- model2 = model_class.new(name: 'jack', age: 23)
612
+ model1 = model_class.new(name: 'jack', age: 21)
613
+ model2 = model_class.new(name: 'jack', age: 23)
498
614
 
499
- model1 == model2 # returns true
615
+ model1 == model2 # returns true
500
616
  ```
617
+ </details>
501
618
 
502
619
  RSspec matcher
503
620
  ---------------
504
621
 
505
622
  You can use the provided matcher to check that your builder is adding a method correctly
506
623
 
624
+ <details>
625
+ <summary>Sample of specs over adding methods</summary>
626
+
507
627
  ```ruby
508
- class DefaultValue
509
- delegate :build, to: :builder
510
- attr_reader :klass, :method, :value, :class_method
511
-
512
- def initialize(klass, method, value, class_method: false)
513
- @klass = klass
514
- @method = method
515
- @value = value
516
- @class_method = class_method
517
- end
628
+ class DefaultValue
629
+ delegate :build, to: :builder
630
+ attr_reader :klass, :method, :value, :class_method
631
+
632
+ def initialize(klass, method, value, class_method: false)
633
+ @klass = klass
634
+ @method = method
635
+ @value = value
636
+ @class_method = class_method
637
+ end
518
638
 
519
- private
639
+ private
520
640
 
521
- def builder
522
- @builder ||= Sinclair.new(klass).tap do |b|
523
- if class_method
524
- b.add_class_method(method) { value }
525
- else
526
- b.add_method(method) { value }
527
- end
641
+ def builder
642
+ @builder ||= Sinclair.new(klass).tap do |b|
643
+ if class_method
644
+ b.add_class_method(method) { value }
645
+ else
646
+ b.add_method(method) { value }
528
647
  end
529
648
  end
530
649
  end
650
+ end
531
651
 
532
- RSpec.describe Sinclair::Matchers do
533
- subject(:builder_class) { DefaultValue }
652
+ RSpec.describe Sinclair::Matchers do
653
+ subject(:builder_class) { DefaultValue }
534
654
 
535
- let(:klass) { Class.new }
536
- let(:method) { :the_method }
537
- let(:value) { Random.rand(100) }
538
- let(:builder) { builder_class.new(klass, method, value) }
539
- let(:instance) { klass.new }
655
+ let(:klass) { Class.new }
656
+ let(:method) { :the_method }
657
+ let(:value) { Random.rand(100) }
658
+ let(:builder) { builder_class.new(klass, method, value) }
659
+ let(:instance) { klass.new }
540
660
 
541
- context 'when the builder runs' do
542
- it do
543
- expect { builder.build }.to add_method(method).to(instance)
544
- end
661
+ context 'when the builder runs' do
662
+ it do
663
+ expect { builder.build }.to add_method(method).to(instance)
545
664
  end
665
+ end
546
666
 
547
- context 'when the builder runs' do
548
- it do
549
- expect { builder.build }.to add_method(method).to(klass)
550
- end
667
+ context 'when the builder runs' do
668
+ it do
669
+ expect { builder.build }.to add_method(method).to(klass)
551
670
  end
671
+ end
552
672
 
553
- context 'when adding class methods' do
554
- subject(:builder) { builder_class.new(klass, method, value, class_method: true) }
673
+ context 'when adding class methods' do
674
+ subject(:builder) { builder_class.new(klass, method, value, class_method: true) }
555
675
 
556
- context 'when the builder runs' do
557
- it do
558
- expect { builder.build }.to add_class_method(method).to(klass)
559
- end
676
+ context 'when the builder runs' do
677
+ it do
678
+ expect { builder.build }.to add_class_method(method).to(klass)
560
679
  end
561
680
  end
562
681
  end
682
+ end
563
683
  ```
564
684
 
565
685
  ```bash
566
-
567
686
  > bundle exec rspec
568
687
  ```
569
688
 
@@ -577,10 +696,11 @@ Sinclair::Matchers
577
696
  when the builder runs
578
697
  should add method class_method 'the_method' to #<Class:0x000055e5d9b95d88>
579
698
  ```
699
+ </details>
580
700
 
581
701
  Projects Using
582
702
  ---------------
583
703
 
584
- - [Arstotzka](https://github.com/darthjee/arstotzka)
585
- - [Azeroth](https://github.com/darthjee/azeroth)
586
- - [Magicka](https://github.com/darthjee/magicka)
704
+ - [Arstotzka](https://github.com/darthjee/arstotzka)
705
+ - [Azeroth](https://github.com/darthjee/azeroth)
706
+ - [Magicka](https://github.com/darthjee/magicka)