sinclair 1.9.0 → 1.11.0

Sign up to get free protection for your applications and to get access to all the features.
Files changed (47) hide show
  1. checksums.yaml +4 -4
  2. data/.rubocop.yml +7 -1
  3. data/README.md +414 -324
  4. data/config/check_specs.yml +3 -4
  5. data/config/yardstick.yml +9 -1
  6. data/lib/sinclair/configurable.rb +2 -0
  7. data/lib/sinclair/equals_checker.rb +2 -0
  8. data/lib/sinclair/matchers/add_class_method.rb +26 -36
  9. data/lib/sinclair/matchers/add_instance_method.rb +59 -59
  10. data/lib/sinclair/matchers/add_method.rb +33 -35
  11. data/lib/sinclair/matchers/change_class_method.rb +22 -16
  12. data/lib/sinclair/matchers/change_instance_method.rb +46 -16
  13. data/lib/sinclair/matchers.rb +2 -8
  14. data/lib/sinclair/method_builder/base.rb +17 -2
  15. data/lib/sinclair/method_builder/call_method_builder.rb +49 -0
  16. data/lib/sinclair/method_builder.rb +5 -17
  17. data/lib/sinclair/method_definition/block_definition.rb +2 -0
  18. data/lib/sinclair/method_definition/call_definition.rb +49 -0
  19. data/lib/sinclair/method_definition/string_definition.rb +2 -2
  20. data/lib/sinclair/method_definition.rb +80 -26
  21. data/lib/sinclair/method_definitions.rb +25 -7
  22. data/lib/sinclair/options/builder.rb +8 -0
  23. data/lib/sinclair/options.rb +1 -13
  24. data/lib/sinclair/version.rb +1 -1
  25. data/lib/sinclair.rb +149 -62
  26. data/spec/integration/readme/sinclair/types_of_definition_spec.rb +47 -0
  27. data/spec/integration/yard/sinclair/add_class_method_spec.rb +51 -0
  28. data/spec/integration/yard/sinclair/add_method_spec.rb +60 -0
  29. data/spec/integration/yard/sinclair/eval_and_add_method_spec.rb +26 -0
  30. data/spec/integration/yard/sinclair/matchers/change_class_method_spec.rb +24 -0
  31. data/spec/integration/yard/sinclair/matchers/change_instance_method_spec.rb +40 -0
  32. data/spec/integration/yard/sinclair_spec.rb +0 -83
  33. data/spec/lib/sinclair/method_builder/base_spec.rb +15 -0
  34. data/spec/lib/sinclair/method_builder/call_method_builder_spec.rb +76 -0
  35. data/spec/lib/sinclair/method_builder_spec.rb +63 -20
  36. data/spec/lib/sinclair/method_definition/call_definition_spec.rb +33 -0
  37. data/spec/lib/sinclair/method_definition_spec.rb +129 -0
  38. data/spec/lib/sinclair/method_definitions_spec.rb +79 -1
  39. data/spec/lib/sinclair/options/builder_spec.rb +13 -0
  40. data/spec/lib/sinclair/options/class_methods_spec.rb +23 -8
  41. data/spec/lib/sinclair_spec.rb +6 -160
  42. data/spec/support/models/dummy_builder.rb +4 -1
  43. data/spec/support/models/dummy_class_builder.rb +3 -0
  44. data/spec/support/models/person.rb +1 -1
  45. data/spec/support/shared_examples/attribute_accessor.rb +103 -0
  46. data/spec/support/shared_examples/sinclair.rb +112 -0
  47. metadata +15 -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.10.0](https://github.com/darthjee/sinclair/compare/1.9.0...master)
16
+ Current Release: [1.11.0](https://github.com/darthjee/sinclair/tree/1.11.0)
17
+
18
+ [Next release](https://github.com/darthjee/sinclair/compare/1.11.0...master)
17
19
 
18
20
  Yard Documentation
19
21
  -------------------
20
- [https://www.rubydoc.info/gems/sinclair/1.9.0](https://www.rubydoc.info/gems/sinclair/1.9.0)
22
+ [https://www.rubydoc.info/gems/sinclair/1.11.0](https://www.rubydoc.info/gems/sinclair/1.11.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
76
-
77
- class << self
78
- def parse(attribute, path: [])
79
- builder = Sinclair.new(self)
80
+ class HttpJsonModel
81
+ attr_reader :json
80
82
 
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
-
91
- def initialize(json)
92
- @json = json
93
- end
94
92
 
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,73 @@ 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
259
+ ```
260
+
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
248
269
  ```
270
+ </details>
271
+
272
+ #### Different ways of adding the methods
273
+ There are different ways to add a method
274
+ <details>
275
+ <summary>Define method using block</summary>
249
276
 
250
277
  ```ruby
278
+ klass = Class.new
279
+ instance = klass.new
251
280
 
252
- invalid_object = MyClass.new(
253
- name: 'name',
254
- surname: 'surname',
255
- age: 20,
256
- legs: 2
257
- )
258
- invalid_object.valid? # returns false
281
+ builder = Sinclair.new(klass)
282
+ builder.add_method(:random_number) { Random.rand(10..20) }
283
+ builder.build
284
+
285
+ instance.random_number # returns a number between 10 and 20
286
+ ```
287
+ </details>
288
+
289
+ <details>
290
+ <summary>Define method using string</summary>
291
+
292
+ ```ruby
293
+ klass = Class.new
294
+ instance = klass.new
295
+
296
+ builder = Sinclair.new(klass)
297
+ builder.add_method(:random_number, "Random.rand(10..20)")
298
+ builder.build
299
+
300
+ instance.random_number # returns a number between 10 and 20
301
+ ```
302
+ </details>
303
+
304
+ <details>
305
+ <summary>Define method using a call to the class</summary>
306
+
307
+ ```ruby
308
+ klass = Class.new
309
+
310
+ builder = Sinclair.new(klass)
311
+ builder.add_class_method(:attr_accessor, :number, type: :call)
312
+ builder.build
313
+
314
+ klass.number # returns nil
315
+ klass.number = 10
316
+ klass.number # returns 10
259
317
  ```
318
+ </details>
260
319
 
261
320
  #### Caching the result
262
321
  If wanted, the result of the method can be stored in an
@@ -265,87 +324,95 @@ instance variable with the same name.
265
324
  When caching, you can cache with type `:full` so that even `nil`
266
325
  values are cached
267
326
 
327
+ <details>
328
+ <summary>Example of simple cache usage</summary>
329
+
268
330
  ```ruby
269
- class MyModel
270
- attr_accessor :base, :expoent
271
- end
331
+ class MyModel
332
+ attr_accessor :base, :expoent
333
+ end
272
334
 
273
- builder = Sinclair.new(MyModel)
335
+ builder = Sinclair.new(MyModel)
274
336
 
275
- builder.add_method(:cached_power, cached: true) do
276
- base ** expoent
277
- end
337
+ builder.add_method(:cached_power, cached: true) do
338
+ base ** expoent
339
+ end
278
340
 
279
- # equivalent of builder.add_method(:cached_power) do
280
- # @cached_power ||= base ** expoent
281
- # end
341
+ # equivalent of builder.add_method(:cached_power) do
342
+ # @cached_power ||= base ** expoent
343
+ # end
282
344
 
283
- builder.build
345
+ builder.build
284
346
 
285
- model.base = 3
286
- model.expoent = 2
347
+ model.base = 3
348
+ model.expoent = 2
287
349
 
288
- model.cached_power # returns 9
289
- model.expoent = 3
290
- model.cached_power # returns 9 (from cache)
350
+ model.cached_power # returns 9
351
+ model.expoent = 3
352
+ model.cached_power # returns 9 (from cache)
291
353
  ```
354
+ </details>
355
+
356
+ <details>
357
+ <summary>Usage of different cache types</summary>
292
358
 
293
359
  ```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
360
+ module DefaultValueable
361
+ def default_reader(*methods, value:, accept_nil: false)
362
+ DefaultValueBuilder.new(
363
+ self, value: value, accept_nil: accept_nil
364
+ ).add_default_values(*methods)
300
365
  end
366
+ end
301
367
 
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
368
+ class DefaultValueBuilder < Sinclair
369
+ def add_default_values(*methods)
370
+ default_value = value
309
371
 
310
- build
372
+ methods.each do |method|
373
+ add_method(method, cached: cache_type) { default_value }
311
374
  end
312
375
 
313
- private
376
+ build
377
+ end
314
378
 
315
- delegate :accept_nil, :value, to: :options_object
379
+ private
316
380
 
317
- def cache_type
318
- accept_nil ? :full : :simple
319
- end
381
+ delegate :accept_nil, :value, to: :options_object
382
+
383
+ def cache_type
384
+ accept_nil ? :full : :simple
320
385
  end
386
+ end
321
387
 
322
- class Server
323
- extend DefaultValueable
388
+ class Server
389
+ extend DefaultValueable
324
390
 
325
- attr_writer :host, :port
391
+ attr_writer :host, :port
326
392
 
327
- default_reader :host, value: 'server.com', accept_nil: false
328
- default_reader :port, value: 80, accept_nil: true
393
+ default_reader :host, value: 'server.com', accept_nil: false
394
+ default_reader :port, value: 80, accept_nil: true
329
395
 
330
- def url
331
- return "http://#{host}" unless port
396
+ def url
397
+ return "http://#{host}" unless port
332
398
 
333
- "http://#{host}:#{port}"
334
- end
399
+ "http://#{host}:#{port}"
335
400
  end
401
+ end
336
402
 
337
- server = Server.new
403
+ server = Server.new
338
404
 
339
- server.url # returns 'http://server.com:80'
405
+ server.url # returns 'http://server.com:80'
340
406
 
341
- server.host = 'interstella.com'
342
- server.port = 5555
343
- server.url # returns 'http://interstella.com:5555'
407
+ server.host = 'interstella.com'
408
+ server.port = 5555
409
+ server.url # returns 'http://interstella.com:5555'
344
410
 
345
- server.host = nil
346
- server.port = nil
347
- server.url # return 'http://server.com'
411
+ server.host = nil
412
+ server.port = nil
413
+ server.url # return 'http://server.com'
348
414
  ```
415
+ </details>
349
416
 
350
417
  ### Sinclair::Configurable
351
418
 
@@ -356,214 +423,236 @@ Configurations are read-only objects that can only be set using
356
423
  the `configurable#configure` method which accepts a block or
357
424
  hash
358
425
 
426
+ <details>
427
+ <summary>Using configurable</summary>
428
+
359
429
  ```ruby
360
- module MyConfigurable
361
- extend Sinclair::Configurable
430
+ module MyConfigurable
431
+ extend Sinclair::Configurable
362
432
 
363
- # port is defaulted to 80
364
- configurable_with :host, port: 80
365
- end
433
+ # port is defaulted to 80
434
+ configurable_with :host, port: 80
435
+ end
366
436
 
367
- MyConfigurable.configure(port: 5555) do |config|
368
- config.host 'interstella.art'
369
- end
437
+ MyConfigurable.configure(port: 5555) do |config|
438
+ config.host 'interstella.art'
439
+ end
370
440
 
371
- MyConfigurable.config.host # returns 'interstella.art'
372
- MyConfigurable.config.port # returns 5555
441
+ MyConfigurable.config.host # returns 'interstella.art'
442
+ MyConfigurable.config.port # returns 5555
373
443
 
374
- # Configurable enables options that can be passed
375
- MyConfigurable.as_options.host # returns 'interstella.art'
444
+ # Configurable enables options that can be passed
445
+ MyConfigurable.as_options.host # returns 'interstella.art'
376
446
 
377
- # Configurable enables options that can be passed with custom values
378
- MyConfigurable.as_options(host: 'other').host # returns 'other'
447
+ # Configurable enables options that can be passed with custom values
448
+ MyConfigurable.as_options(host: 'other').host # returns 'other'
379
449
 
380
- MyConfigurable.reset_config
450
+ MyConfigurable.reset_config
381
451
 
382
- MyConfigurable.config.host # returns nil
383
- MyConfigurable.config.port # returns 80
452
+ MyConfigurable.config.host # returns nil
453
+ MyConfigurable.config.port # returns 80
384
454
  ```
455
+ </details>
385
456
 
386
457
  Configurations can also be done through custom classes
387
458
 
388
- ```ruby
389
- class MyServerConfig < Sinclair::Config
390
- config_attributes :host, :port
459
+ <details>
460
+ <summary>Using configration class</summary>
391
461
 
392
- def url
393
- if @port
394
- "http://#{@host}:#{@port}"
395
- else
396
- "http://#{@host}"
397
- end
462
+ ```ruby
463
+ class MyServerConfig < Sinclair::Config
464
+ config_attributes :host, :port
465
+
466
+ def url
467
+ if @port
468
+ "http://#{@host}:#{@port}"
469
+ else
470
+ "http://#{@host}"
398
471
  end
399
472
  end
473
+ end
400
474
 
401
- class Client
402
- extend Sinclair::Configurable
475
+ class Client
476
+ extend Sinclair::Configurable
403
477
 
404
- configurable_by MyServerConfig
405
- end
478
+ configurable_by MyServerConfig
479
+ end
406
480
 
407
- Client.configure do
408
- host 'interstella.com'
409
- end
481
+ Client.configure do
482
+ host 'interstella.com'
483
+ end
410
484
 
411
- Client.config.url # returns 'http://interstella.com'
485
+ Client.config.url # returns 'http://interstella.com'
412
486
 
413
- Client.configure do |config|
414
- config.port 8080
415
- end
487
+ Client.configure do |config|
488
+ config.port 8080
489
+ end
416
490
 
417
- Client.config.url # returns 'http://interstella.com:8080'
491
+ Client.config.url # returns 'http://interstella.com:8080'
418
492
  ```
493
+ </details>
419
494
 
420
495
  ### Sinclair::EnvSettable
421
496
 
422
497
  Settable allows classes to extract configuration from environments through
423
498
  a simple meta-programable way
424
499
 
500
+ <details>
501
+ <summary>Using env settable example</summary>
502
+
425
503
  ```ruby
426
- class ServiceClient
427
- extend Sinclair::EnvSettable
428
- attr_reader :username, :password, :host, :port
504
+ class ServiceClient
505
+ extend Sinclair::EnvSettable
506
+ attr_reader :username, :password, :host, :port
429
507
 
430
- settings_prefix 'SERVICE'
508
+ settings_prefix 'SERVICE'
431
509
 
432
- with_settings :username, :password, port: 80, hostname: 'my-host.com'
510
+ with_settings :username, :password, port: 80, hostname: 'my-host.com'
433
511
 
434
- def self.default
435
- @default ||= new
436
- end
512
+ def self.default
513
+ @default ||= new
514
+ end
437
515
 
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
516
+ def initialize(
517
+ username: self.class.username,
518
+ password: self.class.password,
519
+ port: self.class.port,
520
+ hostname: self.class.hostname
521
+ )
522
+ @username = username
523
+ @password = password
524
+ @port = port
525
+ @hostname = hostname
449
526
  end
527
+ end
450
528
 
451
- ENV['SERVICE_USERNAME'] = 'my-login'
452
- ENV['SERVICE_HOSTNAME'] = 'host.com'
529
+ ENV['SERVICE_USERNAME'] = 'my-login'
530
+ ENV['SERVICE_HOSTNAME'] = 'host.com'
453
531
 
454
- ServiceClient.default # returns #<ServiceClient:0x0000556fa1b366e8 @username="my-login", @password=nil, @port=80, @hostname="host.com">'
532
+ ServiceClient.default # returns #<ServiceClient:0x0000556fa1b366e8 @username="my-login", @password=nil, @port=80, @hostname="host.com">'
455
533
  ```
534
+ </details>
456
535
 
457
536
  ### Sinclair::Options
458
537
  Options allows projects to have an easy to configure option object
459
538
 
539
+ <details>
540
+ <summary>Example of using Options</summary>
541
+
460
542
  ```ruby
461
- class ConnectionOptions < Sinclair::Options
462
- with_options :timeout, :retries, port: 443, protocol: 'https'
543
+ class ConnectionOptions < Sinclair::Options
544
+ with_options :timeout, :retries, port: 443, protocol: 'https'
463
545
 
464
- # skip_validation if you dont want to validate intialization arguments
465
- end
546
+ # skip_validation if you dont want to validate intialization arguments
547
+ end
466
548
 
467
- options = ConnectionOptions.new(
468
- timeout: 10,
469
- protocol: 'http'
470
- )
549
+ options = ConnectionOptions.new(
550
+ timeout: 10,
551
+ protocol: 'http'
552
+ )
471
553
 
472
- options.timeout # returns 10
473
- options.retries # returns nil
474
- options.protocol # returns 'http'
475
- options.port # returns 443
554
+ options.timeout # returns 10
555
+ options.retries # returns nil
556
+ options.protocol # returns 'http'
557
+ options.port # returns 443
476
558
 
477
- ConnectionOptions.new(invalid: 10) # raises Sinclair::Exception::InvalidOptions
559
+ ConnectionOptions.new(invalid: 10) # raises Sinclair::Exception::InvalidOptions
478
560
  ```
561
+ </details>
479
562
 
480
563
  ### Sinclair::Comparable
481
564
  Comparable allows a class to implement quickly a `==` method comparing given attributes
482
565
 
566
+ <details>
567
+ <summary>Example of Comparable usage</summary>
568
+
483
569
  ```ruby
484
- class SampleModel
485
- include Sinclair::Comparable
570
+ class SampleModel
571
+ include Sinclair::Comparable
486
572
 
487
- comparable_by :name
488
- attr_reader :name, :age
573
+ comparable_by :name
574
+ attr_reader :name, :age
489
575
 
490
- def initialize(name: nil, age: nil)
491
- @name = name
492
- @age = age
493
- end
576
+ def initialize(name: nil, age: nil)
577
+ @name = name
578
+ @age = age
494
579
  end
580
+ end
495
581
 
496
- model1 = model_class.new(name: 'jack', age: 21)
497
- model2 = model_class.new(name: 'jack', age: 23)
582
+ model1 = model_class.new(name: 'jack', age: 21)
583
+ model2 = model_class.new(name: 'jack', age: 23)
498
584
 
499
- model1 == model2 # returns true
585
+ model1 == model2 # returns true
500
586
  ```
587
+ </details>
501
588
 
502
589
  RSspec matcher
503
590
  ---------------
504
591
 
505
592
  You can use the provided matcher to check that your builder is adding a method correctly
506
593
 
594
+ <details>
595
+ <summary>Sample of specs over adding methods</summary>
596
+
507
597
  ```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
598
+ class DefaultValue
599
+ delegate :build, to: :builder
600
+ attr_reader :klass, :method, :value, :class_method
601
+
602
+ def initialize(klass, method, value, class_method: false)
603
+ @klass = klass
604
+ @method = method
605
+ @value = value
606
+ @class_method = class_method
607
+ end
518
608
 
519
- private
609
+ private
520
610
 
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
611
+ def builder
612
+ @builder ||= Sinclair.new(klass).tap do |b|
613
+ if class_method
614
+ b.add_class_method(method) { value }
615
+ else
616
+ b.add_method(method) { value }
528
617
  end
529
618
  end
530
619
  end
620
+ end
531
621
 
532
- RSpec.describe Sinclair::Matchers do
533
- subject(:builder_class) { DefaultValue }
622
+ RSpec.describe Sinclair::Matchers do
623
+ subject(:builder_class) { DefaultValue }
534
624
 
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 }
625
+ let(:klass) { Class.new }
626
+ let(:method) { :the_method }
627
+ let(:value) { Random.rand(100) }
628
+ let(:builder) { builder_class.new(klass, method, value) }
629
+ let(:instance) { klass.new }
540
630
 
541
- context 'when the builder runs' do
542
- it do
543
- expect { builder.build }.to add_method(method).to(instance)
544
- end
631
+ context 'when the builder runs' do
632
+ it do
633
+ expect { builder.build }.to add_method(method).to(instance)
545
634
  end
635
+ end
546
636
 
547
- context 'when the builder runs' do
548
- it do
549
- expect { builder.build }.to add_method(method).to(klass)
550
- end
637
+ context 'when the builder runs' do
638
+ it do
639
+ expect { builder.build }.to add_method(method).to(klass)
551
640
  end
641
+ end
552
642
 
553
- context 'when adding class methods' do
554
- subject(:builder) { builder_class.new(klass, method, value, class_method: true) }
643
+ context 'when adding class methods' do
644
+ subject(:builder) { builder_class.new(klass, method, value, class_method: true) }
555
645
 
556
- context 'when the builder runs' do
557
- it do
558
- expect { builder.build }.to add_class_method(method).to(klass)
559
- end
646
+ context 'when the builder runs' do
647
+ it do
648
+ expect { builder.build }.to add_class_method(method).to(klass)
560
649
  end
561
650
  end
562
651
  end
652
+ end
563
653
  ```
564
654
 
565
655
  ```bash
566
-
567
656
  > bundle exec rspec
568
657
  ```
569
658
 
@@ -577,10 +666,11 @@ Sinclair::Matchers
577
666
  when the builder runs
578
667
  should add method class_method 'the_method' to #<Class:0x000055e5d9b95d88>
579
668
  ```
669
+ </details>
580
670
 
581
671
  Projects Using
582
672
  ---------------
583
673
 
584
- - [Arstotzka](https://github.com/darthjee/arstotzka)
585
- - [Azeroth](https://github.com/darthjee/azeroth)
586
- - [Magicka](https://github.com/darthjee/magicka)
674
+ - [Arstotzka](https://github.com/darthjee/arstotzka)
675
+ - [Azeroth](https://github.com/darthjee/azeroth)
676
+ - [Magicka](https://github.com/darthjee/magicka)