sinclair 1.3.0 → 1.3.1

Sign up to get free protection for your applications and to get access to all the features.
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