sinclair 1.14.1 → 1.15.0
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +4 -4
- data/.rubocop.yml +1 -0
- data/README.md +37 -18
- data/config/yardstick.yml +10 -0
- data/lib/sinclair/caster/class_methods.rb +127 -0
- data/lib/sinclair/caster.rb +381 -0
- data/lib/sinclair/class_methods.rb +41 -0
- data/lib/sinclair/config_class.rb +4 -10
- data/lib/sinclair/method_definition/stringifier.rb +9 -7
- data/lib/sinclair/version.rb +1 -1
- data/lib/sinclair.rb +38 -21
- data/spec/integration/readme/sinclair/types_of_definition_spec.rb +8 -8
- data/spec/integration/yard/my_builder_spec.rb +8 -14
- data/spec/integration/yard/sinclair/add_method_spec.rb +11 -10
- data/spec/integration/yard/sinclair/caster/cast_spec.rb +57 -0
- data/spec/integration/yard/sinclair/caster/cast_with_spec.rb +54 -0
- data/spec/integration/yard/sinclair/caster/caster_for_spec.rb +16 -0
- data/spec/integration/yard/sinclair/caster/class_methods_spec.rb +17 -0
- data/spec/integration/yard/sinclair/class_methods/build_spec.rb +23 -0
- data/spec/integration/yard/sinclair/config_builder_spec.rb +6 -8
- data/spec/integration/yard/sinclair/config_class_spec.rb +7 -21
- data/spec/integration/yard/sinclair_spec.rb +14 -33
- data/spec/lib/sinclair/caster/class_methods_spec.rb +186 -0
- data/spec/lib/sinclair/caster_spec.rb +75 -0
- data/spec/lib/sinclair/class_methods_spec.rb +38 -0
- data/spec/support/models/enum_caster.rb +6 -0
- data/spec/support/models/enum_converter.rb +27 -0
- data/spec/support/models/hash_model.rb +11 -0
- data/spec/support/models/hash_person.rb +11 -0
- data/spec/support/models/http_json_model.rb +4 -6
- data/spec/support/models/math_caster.rb +17 -0
- data/spec/support/models/ruby_string_caster.rb +14 -0
- data/spec/support/models/string_parser.rb +9 -0
- metadata +20 -2
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA256:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: bd10c98e0ec9983e0bd34a8b438126cc391ae667c9e7d5b7ca62f473c5296aea
|
4
|
+
data.tar.gz: f1bffcf49524f9dc05ee27e7510c4e0a76be23b674642fcfa2b10cfdb9f43a82
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: f3afe247b25b8b10f692227c3d5c031e4d6fd04f8aa00e021d0aa8d5813d9cba75c05b4a7d8aae6cc5b6be96d7de404becf35e9391db92c3ceadfc2804f72ce5
|
7
|
+
data.tar.gz: c80cee176cd24ecce3410fa6a64789ca3f52789bce24d558b421751fb6c74a4bf5c86eb1246e48d09abbad962e3bc6f965f1dcf09beae9eca68a198d4e969771
|
data/.rubocop.yml
CHANGED
data/README.md
CHANGED
@@ -15,13 +15,13 @@ create custom comparators, configure your application, create powerfull options,
|
|
15
15
|
|
16
16
|
Employing Sinclair in your applications helps you streamline your development workflow and enhance your development process through more efficient, cleaner code
|
17
17
|
|
18
|
-
Current Release: [1.
|
18
|
+
Current Release: [1.15.0](https://github.com/darthjee/sinclair/tree/1.15.0)
|
19
19
|
|
20
|
-
[Next release](https://github.com/darthjee/sinclair/compare/1.
|
20
|
+
[Next release](https://github.com/darthjee/sinclair/compare/1.15.0...master)
|
21
21
|
|
22
22
|
Yard Documentation
|
23
23
|
-------------------
|
24
|
-
[https://www.rubydoc.info/gems/sinclair/1.
|
24
|
+
[https://www.rubydoc.info/gems/sinclair/1.15.0](https://www.rubydoc.info/gems/sinclair/1.15.0)
|
25
25
|
|
26
26
|
Installation
|
27
27
|
---------------
|
@@ -79,20 +79,20 @@ puts "One Hundred => #{Clazz.one_hundred_twenty}" # One Hundred Twenty => 120
|
|
79
79
|
<summary>Builder in class method</summary>
|
80
80
|
|
81
81
|
```ruby
|
82
|
+
# http_json_model.rb
|
83
|
+
|
82
84
|
class HttpJsonModel
|
83
85
|
attr_reader :json
|
84
86
|
|
85
87
|
class << self
|
86
88
|
def parse(attribute, path: [])
|
87
|
-
builder = Sinclair.new(self)
|
88
|
-
|
89
89
|
keys = (path + [attribute]).map(&:to_s)
|
90
90
|
|
91
|
-
|
92
|
-
|
91
|
+
Sinclair.build(self) do
|
92
|
+
add_method(attribute) do
|
93
|
+
keys.inject(hash) { |h, key| h[key] }
|
94
|
+
end
|
93
95
|
end
|
94
|
-
|
95
|
-
builder.build
|
96
96
|
end
|
97
97
|
end
|
98
98
|
|
@@ -104,6 +104,10 @@ class HttpJsonModel
|
|
104
104
|
@hash ||= JSON.parse(json)
|
105
105
|
end
|
106
106
|
end
|
107
|
+
```
|
108
|
+
|
109
|
+
```ruby
|
110
|
+
# http_person.rb
|
107
111
|
|
108
112
|
class HttpPerson < HttpJsonModel
|
109
113
|
parse :uid
|
@@ -112,7 +116,9 @@ class HttpPerson < HttpJsonModel
|
|
112
116
|
parse :username, path: [:digital_information]
|
113
117
|
parse :email, path: [:digital_information]
|
114
118
|
end
|
119
|
+
```
|
115
120
|
|
121
|
+
```ruby
|
116
122
|
json = <<-JSON
|
117
123
|
{
|
118
124
|
"uid": "12sof511",
|
@@ -284,9 +290,9 @@ Block methods accepts, as option
|
|
284
290
|
klass = Class.new
|
285
291
|
instance = klass.new
|
286
292
|
|
287
|
-
|
288
|
-
|
289
|
-
|
293
|
+
Sinclair.build(klass) do
|
294
|
+
add_method(:random_number) { Random.rand(10..20) }
|
295
|
+
end
|
290
296
|
|
291
297
|
instance.random_number # returns a number between 10 and 20
|
292
298
|
```
|
@@ -320,11 +326,11 @@ instance.random_number # returns a number between 10 and 20
|
|
320
326
|
class MyClass
|
321
327
|
end
|
322
328
|
|
323
|
-
|
324
|
-
|
325
|
-
|
326
|
-
)
|
327
|
-
|
329
|
+
Sinclair.build(MyClass) do
|
330
|
+
add_class_method(
|
331
|
+
:function, 'a ** b + c', parameters: [:a], named_parameters: [:b, { c: 15 }]
|
332
|
+
)
|
333
|
+
end
|
328
334
|
|
329
335
|
MyClass.function(10, b: 2) # returns 115
|
330
336
|
```
|
@@ -675,6 +681,15 @@ You can use the provided matcher to check that your builder is adding a method c
|
|
675
681
|
<summary>Sample of specs over adding methods</summary>
|
676
682
|
|
677
683
|
```ruby
|
684
|
+
# spec_helper.rb
|
685
|
+
|
686
|
+
RSpec.configure do |config|
|
687
|
+
config.include Sinclair::Matchers
|
688
|
+
end
|
689
|
+
```
|
690
|
+
|
691
|
+
```ruby
|
692
|
+
# default_value.rb
|
678
693
|
class DefaultValue
|
679
694
|
delegate :build, to: :builder
|
680
695
|
attr_reader :klass, :method, :value, :class_method
|
@@ -698,8 +713,12 @@ class DefaultValue
|
|
698
713
|
end
|
699
714
|
end
|
700
715
|
end
|
716
|
+
```
|
717
|
+
|
718
|
+
```ruby
|
719
|
+
# default_value_spec.rb
|
701
720
|
|
702
|
-
RSpec.describe
|
721
|
+
RSpec.describe DefaultValue do
|
703
722
|
subject(:builder_class) { DefaultValue }
|
704
723
|
|
705
724
|
let(:klass) { Class.new }
|
data/config/yardstick.yml
CHANGED
@@ -18,6 +18,14 @@ rules:
|
|
18
18
|
exclude:
|
19
19
|
- Sinclair#add_method
|
20
20
|
- Sinclair#add_class_method
|
21
|
+
- Sinclair.build
|
22
|
+
- Sinclair::Caster.cast
|
23
|
+
- Sinclair::Caster.cast_with
|
24
|
+
- Sinclair::Caster.caster_for
|
25
|
+
- Sinclair::Caster#initialize
|
26
|
+
- Sinclair::Caster::ClassMethods#cast
|
27
|
+
- Sinclair::Caster::ClassMethods#cast_with
|
28
|
+
- Sinclair::Caster::ClassMethods#caster_for
|
21
29
|
- Sinclair::Configurable#config
|
22
30
|
- Sinclair::Configurable#reset_config
|
23
31
|
- Sinclair::Configurable#configure
|
@@ -34,6 +42,7 @@ rules:
|
|
34
42
|
ReturnTag:
|
35
43
|
enabled: true
|
36
44
|
exclude:
|
45
|
+
- Sinclair.build
|
37
46
|
- Sinclair::Matchers::AddClassMethodTo#raise_block_syntax_error
|
38
47
|
- Sinclair::Matchers::AddInstanceMethodTo#raise_block_syntax_error
|
39
48
|
- Sinclair::MethodBuilder#build_from_definition
|
@@ -42,6 +51,7 @@ rules:
|
|
42
51
|
Summary::Presence:
|
43
52
|
enabled: true
|
44
53
|
exclude:
|
54
|
+
- Sinclair::Caster#initialize
|
45
55
|
- Sinclair::Config::MethodsBuilder#initialize
|
46
56
|
- Sinclair::ConfigFactory#initialize
|
47
57
|
- Sinclair::EnvSettable::Builder#initialize
|
@@ -0,0 +1,127 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
class Sinclair
|
4
|
+
class Caster
|
5
|
+
# @api public
|
6
|
+
# @author darhtjee
|
7
|
+
#
|
8
|
+
# Class methods for {Caster}
|
9
|
+
module ClassMethods
|
10
|
+
# (see Caster.master_caster!)
|
11
|
+
def master_caster!
|
12
|
+
@master_caster = true
|
13
|
+
end
|
14
|
+
|
15
|
+
# (see Caster.cast_with)
|
16
|
+
def cast_with(key, method_name = nil, &block)
|
17
|
+
caster = instance_for(method_name, &block)
|
18
|
+
|
19
|
+
return class_casters[key] = caster if key.is_a?(Class)
|
20
|
+
|
21
|
+
casters[key] = caster
|
22
|
+
end
|
23
|
+
|
24
|
+
# (see Caster.cast)
|
25
|
+
def cast(value, key, **opts)
|
26
|
+
caster_for(key).cast(value, **opts)
|
27
|
+
end
|
28
|
+
|
29
|
+
# (see Caster.caster_for)
|
30
|
+
def caster_for(key)
|
31
|
+
return casters[key] if casters.key?(key)
|
32
|
+
|
33
|
+
caster_for_class(key) || superclas_caster_for(key) || new { |value| value }
|
34
|
+
end
|
35
|
+
|
36
|
+
protected
|
37
|
+
|
38
|
+
# @api private
|
39
|
+
#
|
40
|
+
# Returns a caster from the superclass
|
41
|
+
#
|
42
|
+
# @param key [Symbol,Class] key to be checked
|
43
|
+
#
|
44
|
+
# @see caster_for
|
45
|
+
# @return [Caster]
|
46
|
+
def superclas_caster_for(key)
|
47
|
+
return if master_caster?
|
48
|
+
|
49
|
+
superclass.caster_for(key)
|
50
|
+
end
|
51
|
+
|
52
|
+
# @api private
|
53
|
+
#
|
54
|
+
# Returns a caster searching for using class as key
|
55
|
+
#
|
56
|
+
# This is called by {#caster_for} any time key is a class
|
57
|
+
#
|
58
|
+
# @param klass [Class] class to be used in the search
|
59
|
+
#
|
60
|
+
# When the given class is not registered, a caster for a parent
|
61
|
+
# class is returned
|
62
|
+
#
|
63
|
+
# @return [Caster]
|
64
|
+
def caster_for_class(klass)
|
65
|
+
class_casters.find do |klazz, _|
|
66
|
+
klass <= klazz
|
67
|
+
end&.second
|
68
|
+
end
|
69
|
+
|
70
|
+
# @api private
|
71
|
+
#
|
72
|
+
# Returns a new instance {Caster}
|
73
|
+
#
|
74
|
+
# @overload instance_for(method_name, &block)
|
75
|
+
# @param method_name [Symbol] method to be called in the model
|
76
|
+
# @param block [Proc] block to perform the casting
|
77
|
+
#
|
78
|
+
# When +method_name+ is not given, the block is used
|
79
|
+
#
|
80
|
+
# @overload instance_for(caster)
|
81
|
+
# @param caster [Caster] instance of caster to be returned
|
82
|
+
#
|
83
|
+
# @return [Caster]
|
84
|
+
def instance_for(method_name, &block)
|
85
|
+
return new(&block) unless method_name
|
86
|
+
return method_name if method_name.is_a?(Caster)
|
87
|
+
|
88
|
+
new(&method_name)
|
89
|
+
end
|
90
|
+
|
91
|
+
private
|
92
|
+
|
93
|
+
# @api private
|
94
|
+
# @private
|
95
|
+
#
|
96
|
+
# Caster map stored by +Symbols+
|
97
|
+
#
|
98
|
+
# @return [Hash<Symbol,Caster>]
|
99
|
+
def casters
|
100
|
+
@casters ||= {}
|
101
|
+
end
|
102
|
+
|
103
|
+
# @api private
|
104
|
+
# @private
|
105
|
+
#
|
106
|
+
# Caster map stored by +Classs+
|
107
|
+
#
|
108
|
+
# @return [Hash<Class,Caster>]
|
109
|
+
def class_casters
|
110
|
+
@class_casters ||= {}
|
111
|
+
end
|
112
|
+
|
113
|
+
# @api private
|
114
|
+
# @private
|
115
|
+
#
|
116
|
+
# Chack if the caster class is a master
|
117
|
+
#
|
118
|
+
# A master caster never checks if a superclass has a caster
|
119
|
+
#
|
120
|
+
# @see master_caster!
|
121
|
+
# @return [TrueClass,FalseClass]
|
122
|
+
def master_caster?
|
123
|
+
@master_caster
|
124
|
+
end
|
125
|
+
end
|
126
|
+
end
|
127
|
+
end
|
@@ -0,0 +1,381 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
class Sinclair
|
4
|
+
# @api public
|
5
|
+
# @author darhtjee
|
6
|
+
#
|
7
|
+
# Class responsible for defining how to and casting values
|
8
|
+
#
|
9
|
+
# First the class needs to be configured using {.cast_with} and later
|
10
|
+
# a value can be cast by using {.cast} or {.caster_for}
|
11
|
+
#
|
12
|
+
# Inheritance grants the hability to have different casting for different
|
13
|
+
# purposes / applications / gems
|
14
|
+
class Caster
|
15
|
+
autoload :ClassMethods, 'sinclair/caster/class_methods'
|
16
|
+
extend Caster::ClassMethods
|
17
|
+
master_caster!
|
18
|
+
|
19
|
+
# @method self.master_caster!
|
20
|
+
# @api public
|
21
|
+
#
|
22
|
+
# Changes the class to be the master caster
|
23
|
+
#
|
24
|
+
# The master caster never checks with its an
|
25
|
+
#
|
26
|
+
# @example
|
27
|
+
# class MyCaster < Sinclair::Caster
|
28
|
+
# end
|
29
|
+
#
|
30
|
+
# MyCaster.cast(10, :string) # returns '10'
|
31
|
+
#
|
32
|
+
# MyCaster.master_caster!
|
33
|
+
#
|
34
|
+
# MyCaster.cast(10, :string) # returns 10
|
35
|
+
#
|
36
|
+
# @see Caster::ClassMethods#master_caster!
|
37
|
+
#
|
38
|
+
# @return [TrueClass]
|
39
|
+
|
40
|
+
# @method self.cast_with
|
41
|
+
# @api public
|
42
|
+
#
|
43
|
+
# Register a caster under a key
|
44
|
+
#
|
45
|
+
# @overload cast_with(key, method_name)
|
46
|
+
# @param key [Symbol] key where the caster will be store.
|
47
|
+
# @param method_name [Symbol] method to be called on the
|
48
|
+
# value that is being converted
|
49
|
+
#
|
50
|
+
# @example Casting from pre registered symbol caster
|
51
|
+
# class MyCaster < Sinclair::Caster
|
52
|
+
# cast_with(:json, :to_json)
|
53
|
+
# end
|
54
|
+
#
|
55
|
+
# MyCaster.cast({ key: :value }, :json) # returns '{"key":"value"}'
|
56
|
+
#
|
57
|
+
# @overload cast_with(key, &block)
|
58
|
+
# @param key [Symbol] key where the caster will be store.
|
59
|
+
# @param block [Proc] block to be used when casting the value.
|
60
|
+
#
|
61
|
+
# @example Casting from pre registered block caster
|
62
|
+
# MyCaster.cast_with(:complex) do |hash|
|
63
|
+
# real = hash[:real]
|
64
|
+
# imaginary = hash[:imaginary]
|
65
|
+
#
|
66
|
+
# "#{real.to_f} + #{imaginary.to_f} i"
|
67
|
+
# end
|
68
|
+
#
|
69
|
+
# value = { real: 10, imaginary: 5 }
|
70
|
+
#
|
71
|
+
# MyCaster.cast(value, :complex) # returns '10.0 + 5.0 i'
|
72
|
+
#
|
73
|
+
# @overload cast_with(class_key, method_name)
|
74
|
+
# @param class_key [Class] class to be used as key.
|
75
|
+
# This will be used as parent class when the calling {Caster.cast}.
|
76
|
+
# @param method_name [Symbol] method to be called on the
|
77
|
+
# value that is being converted.
|
78
|
+
#
|
79
|
+
# @example Casting from pre registered class
|
80
|
+
# class MyCaster < Sinclair::Caster
|
81
|
+
# cast_with(Numeric, :to_i)
|
82
|
+
# end
|
83
|
+
#
|
84
|
+
# MyCaster.cast('10', Integer) # returns 10
|
85
|
+
#
|
86
|
+
# @overload cast_with(class_key, &block)
|
87
|
+
# @param class_key [Class] class to be used as key.
|
88
|
+
# This will be used as parent class when the calling {Caster.cast}.
|
89
|
+
# @param block [Proc] block to be used when casting the value.
|
90
|
+
#
|
91
|
+
# @example Casting from pre registered block caster from a class
|
92
|
+
# # hash_model.rb
|
93
|
+
#
|
94
|
+
# class HashModel
|
95
|
+
# def initialize(hash)
|
96
|
+
# hash.each do |attribute, value|
|
97
|
+
# method_name = "#{attribute}="
|
98
|
+
#
|
99
|
+
# send(method_name, value) if respond_to?(method_name)
|
100
|
+
# end
|
101
|
+
# end
|
102
|
+
# end
|
103
|
+
#
|
104
|
+
# # hash_person.rb
|
105
|
+
# class HashPerson < HashModel
|
106
|
+
# attr_accessor :name, :age
|
107
|
+
# end
|
108
|
+
#
|
109
|
+
# # caster_config.rb
|
110
|
+
# Caster.cast_with(HashModel) do |value, klass:|
|
111
|
+
# klass.new(value)
|
112
|
+
# end
|
113
|
+
#
|
114
|
+
# Caster.cast_with(String, &:to_json)
|
115
|
+
#
|
116
|
+
# # main.rb
|
117
|
+
# values = [
|
118
|
+
# { klass: String, value: { name: 'john', age: 20, country: 'BR' } },
|
119
|
+
# { klass: HashPerson, value: { name: 'Mary', age: 22, country: 'IT' } }
|
120
|
+
# ]
|
121
|
+
#
|
122
|
+
# values.map! do |config|
|
123
|
+
# value = config[:value]
|
124
|
+
# klass = config[:klass]
|
125
|
+
#
|
126
|
+
# Caster.cast(value, klass, klass: klass)
|
127
|
+
# end
|
128
|
+
#
|
129
|
+
# values[0] # returns '{"name":"john","age":20,"country":"BR"}'
|
130
|
+
# values[1] # returns HashPerson.new(name: 'Mary', age: 22)
|
131
|
+
#
|
132
|
+
# @see Caster::ClassMethods#cast_with
|
133
|
+
# @see Caster.caster_for
|
134
|
+
# @see Caster.cast
|
135
|
+
#
|
136
|
+
# @return [Caster] the registered caster
|
137
|
+
|
138
|
+
# @method self.cast
|
139
|
+
# @api public
|
140
|
+
#
|
141
|
+
# Cast a value using the registered caster
|
142
|
+
#
|
143
|
+
# @overload cast(value, key, **opts)
|
144
|
+
# @param value [Object] value to be cast
|
145
|
+
# @param key [Symbol] key where the caster is registered under
|
146
|
+
# @param opts [Hash] Options to be sent to the caster
|
147
|
+
#
|
148
|
+
# @example Casts with a symbol key
|
149
|
+
# # math_caster.rb
|
150
|
+
# class MathCaster < Sinclair::Caster
|
151
|
+
# cast_with(:float, :to_f)
|
152
|
+
#
|
153
|
+
# cast_with(:log) do |value, base: 10|
|
154
|
+
# value = MathCaster.cast(value, :float)
|
155
|
+
#
|
156
|
+
# Math.log(value, base)
|
157
|
+
# end
|
158
|
+
#
|
159
|
+
# cast_with(:exp) do |value, base: 10|
|
160
|
+
# value = MathCaster.cast(value, :float)
|
161
|
+
#
|
162
|
+
# base**value
|
163
|
+
# end
|
164
|
+
# end
|
165
|
+
#
|
166
|
+
# # main.rb
|
167
|
+
# initial = Random.rand(10..20)
|
168
|
+
# log = MathCaster.cast(initial, :log)
|
169
|
+
# exp = MathCaster.cast(log, :exp)
|
170
|
+
#
|
171
|
+
# # exp will be betwween initial - 0.0001 and initial + 0.0001
|
172
|
+
#
|
173
|
+
# @example Casts passing parameter
|
174
|
+
# base = Random.rand(3..6)
|
175
|
+
# initial = Random.rand(10..20)
|
176
|
+
# log = MathCaster.cast(initial, :log, base: base)
|
177
|
+
# exp = MathCaster.cast(log, :exp, base: base)
|
178
|
+
#
|
179
|
+
# # exp will be betwween initial - 0.0001 and initial + 0.0001
|
180
|
+
#
|
181
|
+
# @overload cast(value, class_key, **opts)
|
182
|
+
# @param value [Object] value to be cast
|
183
|
+
# @param class_key [Class] Class to used as key in the casters storage
|
184
|
+
# @param opts [Hash] Options to be sent to the caster
|
185
|
+
#
|
186
|
+
# When the +class_key+ does not match the stored key, but matches a superclass,
|
187
|
+
# the registerd caster is returned.
|
188
|
+
#
|
189
|
+
# @example Casts with class key
|
190
|
+
# # ruby_string_caster.rb
|
191
|
+
# class RubyStringCaster < Sinclair::Caster
|
192
|
+
# master_caster!
|
193
|
+
#
|
194
|
+
# cast_with(NilClass) { 'nil' }
|
195
|
+
# cast_with(Symbol) { |value| ":#{value}" }
|
196
|
+
# cast_with(String, :to_json)
|
197
|
+
# cast_with(Object, :to_s)
|
198
|
+
#
|
199
|
+
# def self.to_ruby_string(value)
|
200
|
+
# cast(value, value.class)
|
201
|
+
# end
|
202
|
+
# end
|
203
|
+
#
|
204
|
+
# # main.rb
|
205
|
+
# hash = { a: 1, b: 2, 'c' => nil }
|
206
|
+
# string = 'my string'
|
207
|
+
# symbol = :the_symbol
|
208
|
+
# number = 10
|
209
|
+
# null = nil
|
210
|
+
#
|
211
|
+
# <<-RUBY
|
212
|
+
# hash_value = #{RubyStringCaster.to_ruby_string(hash)}
|
213
|
+
# string_value = #{RubyStringCaster.to_ruby_string(string)}
|
214
|
+
# symbol_value = #{RubyStringCaster.to_ruby_string(symbol)}
|
215
|
+
# number = #{RubyStringCaster.to_ruby_string(number)}
|
216
|
+
# null_value = #{RubyStringCaster.to_ruby_string(null)}
|
217
|
+
# RUBY
|
218
|
+
#
|
219
|
+
# # Generates the String
|
220
|
+
# #
|
221
|
+
# # <<-RUBY
|
222
|
+
# # hash_value = {:a=>1, :b=>2, "c"=>nil}
|
223
|
+
# # string_value = "my string"
|
224
|
+
# # symbol_value = :the_symbol
|
225
|
+
# # number = 10
|
226
|
+
# # null_value = nil
|
227
|
+
# # RUBY
|
228
|
+
#
|
229
|
+
# @see Caster::ClassMethods#cast
|
230
|
+
# @see Caster.cast_with
|
231
|
+
# @see Caster.caster_for
|
232
|
+
# @see Caster#cast
|
233
|
+
#
|
234
|
+
# @return [Object] the value cast
|
235
|
+
|
236
|
+
# @method self.caster_for
|
237
|
+
# @api public
|
238
|
+
#
|
239
|
+
# Returns an instance of caster for the provided key
|
240
|
+
#
|
241
|
+
# When no registered caster is found one is requested for the parent class.
|
242
|
+
# If no caster is found, then a default caster is returned
|
243
|
+
#
|
244
|
+
# The default caster performs no casting returning the value itself
|
245
|
+
#
|
246
|
+
# @overload caster_for(key)
|
247
|
+
# @param key [Symbol] key where the caster is registered under
|
248
|
+
#
|
249
|
+
# @example Getting the caster with symbol key
|
250
|
+
# # enum_caster.rb
|
251
|
+
# class EnumCaster < Sinclair::Caster
|
252
|
+
# cast_with(:hash, :to_h)
|
253
|
+
# cast_with(:array, :to_a)
|
254
|
+
# end
|
255
|
+
#
|
256
|
+
# # enum_converter.rb
|
257
|
+
# module EnumConverter
|
258
|
+
# class << self
|
259
|
+
# def to_hash(value)
|
260
|
+
# return value if value.is_a?(Hash)
|
261
|
+
#
|
262
|
+
# hash_caster.cast(value)
|
263
|
+
# end
|
264
|
+
#
|
265
|
+
# def to_array(value)
|
266
|
+
# return value if value.is_a?(Array)
|
267
|
+
#
|
268
|
+
# array_caster.cast(value)
|
269
|
+
# end
|
270
|
+
#
|
271
|
+
# private
|
272
|
+
#
|
273
|
+
# def hash_caster
|
274
|
+
# @hash_caster ||= EnumCaster.caster_for(:hash)
|
275
|
+
# end
|
276
|
+
#
|
277
|
+
# def array_caster
|
278
|
+
# @array_caster ||= EnumCaster.caster_for(:array)
|
279
|
+
# end
|
280
|
+
# end
|
281
|
+
# end
|
282
|
+
#
|
283
|
+
# # main.rb
|
284
|
+
# EnumConverter.to_array({ key: :value }) # returns [%i[key value]]
|
285
|
+
# EnumConverter.to_hash([%i[key value]]) # returns { key: :value }
|
286
|
+
#
|
287
|
+
# @overload caster_for(class_key)
|
288
|
+
# @param class_key [Class] Class to used as key in the casters storage
|
289
|
+
#
|
290
|
+
# When the +class_key+ does not match the stored key, but matches a superclass,
|
291
|
+
# the registerd caster is returned.
|
292
|
+
#
|
293
|
+
# @example Getting the caster with class key'
|
294
|
+
# # stringer_parser.rb
|
295
|
+
# class StringParser < Sinclair::Caster
|
296
|
+
# master_caster!
|
297
|
+
#
|
298
|
+
# cast_with(JSON) { |value| JSON.parse(value) }
|
299
|
+
# cast_with(Integer, :to_i)
|
300
|
+
# cast_with(Float, :to_f)
|
301
|
+
# end
|
302
|
+
#
|
303
|
+
# # main.rb
|
304
|
+
# StringParser.cast('{"key":"value"}', JSON) # returns { "key" => "value" }
|
305
|
+
# StringParser.cast('10.2', Integer) # returns 10
|
306
|
+
# StringParser.cast('10.2', Float) # returns 10.2
|
307
|
+
#
|
308
|
+
# @see Caster::ClassMethods#caster_for
|
309
|
+
# @see Caster.cast_with
|
310
|
+
# @see Caster.cast
|
311
|
+
#
|
312
|
+
# @return [Caster]
|
313
|
+
|
314
|
+
# @param block [Proc] Proc to be used when converting the value object
|
315
|
+
def initialize(&block)
|
316
|
+
@block = block.to_proc
|
317
|
+
end
|
318
|
+
|
319
|
+
# Cast a value using the given the set +block+
|
320
|
+
#
|
321
|
+
# @param value [Object] value to be converted
|
322
|
+
# @param opts [Hash] options to be sent to the block
|
323
|
+
#
|
324
|
+
# When the block does not accept options, those
|
325
|
+
# are not passed
|
326
|
+
#
|
327
|
+
# @example Casts from a selected caster
|
328
|
+
# # math_caster.rb
|
329
|
+
# class MathCaster < Sinclair::Caster
|
330
|
+
# cast_with(:float, :to_f)
|
331
|
+
#
|
332
|
+
# cast_with(:log) do |value, base: 10|
|
333
|
+
# value = MathCaster.cast(value, :float)
|
334
|
+
#
|
335
|
+
# Math.log(value, base)
|
336
|
+
# end
|
337
|
+
# end
|
338
|
+
#
|
339
|
+
# # main.rb
|
340
|
+
# caster = MathCaster.caster_for(:log)
|
341
|
+
#
|
342
|
+
# caster.cast(100) # returns 2
|
343
|
+
# caster.cast(8, base: 2) # returns 3
|
344
|
+
#
|
345
|
+
# @return [Object] the result of the converting block
|
346
|
+
def cast(value, **opts)
|
347
|
+
options = opts.select do |key, _|
|
348
|
+
options_keys.include?(key)
|
349
|
+
end
|
350
|
+
|
351
|
+
block.call(value, **options)
|
352
|
+
end
|
353
|
+
|
354
|
+
private
|
355
|
+
|
356
|
+
# @api private
|
357
|
+
# @private
|
358
|
+
#
|
359
|
+
# Keys of options accepted by the block
|
360
|
+
#
|
361
|
+
# @return [Array<Symbol>]
|
362
|
+
def options_keys
|
363
|
+
@options_keys ||= block.parameters.select do |(type, _)|
|
364
|
+
%i[key keyreq].include? type
|
365
|
+
end.map(&:second)
|
366
|
+
end
|
367
|
+
|
368
|
+
# @method block
|
369
|
+
# @api private
|
370
|
+
# @private
|
371
|
+
#
|
372
|
+
# Proc to be used when converting the value object
|
373
|
+
#
|
374
|
+
# @return [Proc]
|
375
|
+
attr_reader :block
|
376
|
+
|
377
|
+
cast_with(:string, :to_s)
|
378
|
+
cast_with(:integer, :to_i)
|
379
|
+
cast_with(:float, :to_f)
|
380
|
+
end
|
381
|
+
end
|