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 CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: 8078be623117ced163bf463e484e1e2553781dc3ebf379e48a98dc5c3e710079
4
- data.tar.gz: 5edfd7356786a04ee94f5bfff56fd954e26b52b5fa4ef7eb3372ddd33ddb6c80
3
+ metadata.gz: c574eec3a6df9fca413808866c33a03da39ac3aea5a55ad588ea0532fdcb8058
4
+ data.tar.gz: ccdf5e26711abfeb0c9a2eee9f59cabc8a4ae912b624b976720b228b7be7adbd
5
5
  SHA512:
6
- metadata.gz: 03b32dcf796be1331442a673040264bc2cb215f1ad2eb657aaf416f76bea0e6aaca71ccb8bd356e5c9ab094dc31f4d2a0c3cc4dc607ccb39db429b864b5e785c
7
- data.tar.gz: 28bd3ed7dea51f96d82bdbbdb64588762cc017ab92d8847045dbf33beb8aa83abc71869ff5e55f4f8dc72f4f29f4f9907596702acb7ab5f456c43bc0b92e902b
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
- - 'spec/integration/yard/sinclair/method_definition_spec.rb'
24
- - 'spec/lib/sinclair/method_definition_spec.rb'
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
@@ -1,4 +1,4 @@
1
- FROM darthjee/ruby_gems:0.0.1 as base
1
+ FROM darthjee/ruby_gems:0.1.0 as base
2
2
  FROM darthjee/scripts:0.0.2 as scripts
3
3
 
4
4
  ######################################
data/README.md CHANGED
@@ -8,11 +8,13 @@ Sinclair
8
8
 
9
9
  ![sinclair](https://raw.githubusercontent.com/darthjee/sinclair/master/sinclair.jpg)
10
10
 
11
- This gem helps the creation of complex concern with class methods
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.0
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 Sinclairn to your `Gemfile` and `bundle install`:
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
- The concern builder can actully be used in two ways, as an stand alone object capable of
38
- adding methods to your class or by extending it for more complex logics
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
- - Stand Alone usage:
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
- - Extending the builder
63
+ ## Builder in class method:
60
64
 
61
65
  ```ruby
66
+ class HttpJsonModel
67
+ attr_reader :json
62
68
 
63
- class ValidationBuilder < Sinclair
64
- delegate :expected, to: :options_object
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(klass, options={})
67
- super
83
+ def initialize(json)
84
+ @json = json
68
85
  end
69
86
 
70
- def add_validation(field)
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
- - Caching the result
153
- If wanted, the result of the method can be stored in an
154
- instance variable with the same name
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.new(name, code, **options, &block)
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
- # Returns a new instance of MethodDefinition
16
- #
17
- # @overload initialize(name, code)
18
- # @example
19
- # Sinclair::MethodDefinition.new(:name, '@name')
20
- #
21
- # @overload initialize(name, &block)
22
- # @example
23
- # Sinclair::MethodDefinition.new(:name) { @name }
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, code = nil, **options, &block)
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
- # @param klass [Class] class which will receive the new method
43
+ # This should be implemented on child classes
41
44
  #
42
- # @example Using string method with no options
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.new(
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.new(:sequence) do
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(klass)
87
- if code.is_a?(String)
88
- build_code_method(klass)
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