fast_attributes 0.5.2 → 0.6.0
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 +4 -4
- data/.travis.yml +4 -0
- data/CHANGELOG.md +47 -1
- data/README.md +40 -10
- data/lib/fast_attributes.rb +1 -1
- data/lib/fast_attributes/builder.rb +3 -17
- data/lib/fast_attributes/type_cast.rb +48 -5
- data/lib/fast_attributes/version.rb +1 -1
- data/spec/fast_attributes/type_cast_spec.rb +89 -7
- data/spec/fast_attributes_spec.rb +22 -9
- data/spec/fixtures/classes.rb +3 -0
- metadata +2 -2
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA1:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: 156410fa3391841eaf2b70060f777f96e70b17cb
|
4
|
+
data.tar.gz: d272b8684e3b06afe7de0562a06f116f9de9f168
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: 5770eaeb6359e8fe89fe78301fc6e8dc4242c02edcf99b132def5c27fed34b3e30d2c270c33a4b8ad2f01695d48789ae93d40ef35f8abdef86acaf1984db035d
|
7
|
+
data.tar.gz: 087d805216430a6cb94d7dd8bce84b72f8fdc644f1853f957aefb39941109f054f0f0ff1c985902a2d7f018a8f863698ec097a81cb8c87494367b7303888182c
|
data/.travis.yml
CHANGED
data/CHANGELOG.md
CHANGED
@@ -1,8 +1,54 @@
|
|
1
|
+
**0.6.0 (July 20, 2014)**
|
2
|
+
* Throw custom `FastAttributes::TypeCast::InvalidValueError` exception when value has invalid type.
|
3
|
+
How auto-generated method looks like:
|
4
|
+
```ruby
|
5
|
+
FastAttributes.set_type_casting(String, 'String(%s)')
|
6
|
+
# def name=(value)
|
7
|
+
# @name = begin
|
8
|
+
# case value
|
9
|
+
# when nil then nil
|
10
|
+
# when String then value
|
11
|
+
# else String(value)
|
12
|
+
# end
|
13
|
+
# rescue => e
|
14
|
+
# raise FastAttributes::TypeCast::InvalidValueError, %(Invalid value "#{value}" for attribute "name" of type "String")
|
15
|
+
# end
|
16
|
+
# end
|
17
|
+
```
|
18
|
+
|
19
|
+
* Add `on_error` method to override default rescue block:
|
20
|
+
```ruby
|
21
|
+
FastAttributes.type_cast String do
|
22
|
+
from 'nil', to: 'nil'
|
23
|
+
from 'String', to: '%s'
|
24
|
+
otherwise 'String(%s)'
|
25
|
+
on_error 'ArgumentError', act: 'nil'
|
26
|
+
on_error 'TypeError', act: '""'
|
27
|
+
on_error 'StandardError', act: 'e.message'
|
28
|
+
end
|
29
|
+
|
30
|
+
# def name=(value)
|
31
|
+
# @name = begin
|
32
|
+
# case value
|
33
|
+
# when nil then nil
|
34
|
+
# when String then value
|
35
|
+
# else String(value)
|
36
|
+
# end
|
37
|
+
# rescue ArgumentError => e
|
38
|
+
# nil
|
39
|
+
# rescue TypeError => e
|
40
|
+
# ""
|
41
|
+
# rescue StandardError => e
|
42
|
+
# e.message
|
43
|
+
# end
|
44
|
+
# end
|
45
|
+
```
|
46
|
+
|
1
47
|
**0.5.2 (July 18, 2014)**
|
2
48
|
* Throw proper exception when type casting function is not defined
|
3
49
|
|
4
50
|
**0.5.1 (July 16, 2014)**
|
5
|
-
* Fix `BigDecimal` type casting. It threw an exception when input value was `
|
51
|
+
* Fix `BigDecimal` type casting. It threw an exception when input value was `Float`
|
6
52
|
|
7
53
|
**0.5.0 (July 16, 2014)**
|
8
54
|
* Allow to control any switch statements during typecasting using new DSL.
|
data/README.md
CHANGED
@@ -143,23 +143,53 @@ square.size
|
|
143
143
|
# => [5, 5]
|
144
144
|
```
|
145
145
|
|
146
|
-
Method `FastAttributes.set_type_casting` generates the following
|
146
|
+
Method `FastAttributes.set_type_casting` generates the following template:
|
147
147
|
```ruby
|
148
148
|
FastAttributes.set_type_casting String, 'String(%s)'
|
149
|
-
#
|
150
|
-
#
|
151
|
-
#
|
152
|
-
#
|
149
|
+
# begin
|
150
|
+
# case %s
|
151
|
+
# when nil then nil
|
152
|
+
# when String then %s
|
153
|
+
# else String(%s)
|
154
|
+
# end
|
155
|
+
# rescue => e
|
156
|
+
# raise FastAttributes::TypeCast::InvalidValueError, %(Invalid value "\#{%s}" for attribute "%a" of type "String")
|
153
157
|
# end
|
154
158
|
```
|
159
|
+
and when the attribute is defined, `fast_attributes` generates the following setter method:
|
160
|
+
```ruby
|
161
|
+
class A
|
162
|
+
extend FastAttributes
|
163
|
+
attribute :name, String
|
164
|
+
end
|
165
|
+
|
166
|
+
# def name=(value)
|
167
|
+
# @name = begin
|
168
|
+
# case value
|
169
|
+
# when nil then nil
|
170
|
+
# when String then value
|
171
|
+
# else String(value)
|
172
|
+
# end
|
173
|
+
# rescue => e
|
174
|
+
# raise FastAttributes::TypeCast::InvalidValueError, %(Invalid value "#{value}" for attribute "name" of type "String")
|
175
|
+
# end
|
176
|
+
# end
|
177
|
+
```
|
178
|
+
Notice, placeholder `%a` represents method name.
|
155
179
|
|
156
180
|
If you need to conrol the whole type casting process, you can use the following DSL:
|
157
181
|
```ruby
|
158
|
-
FastAttributes.type_cast String do
|
159
|
-
|
160
|
-
from '
|
161
|
-
|
162
|
-
|
182
|
+
FastAttributes.type_cast String do # begin
|
183
|
+
# case String
|
184
|
+
from 'nil', to: 'nil' # when nil then nil
|
185
|
+
from 'String', to: '%s' # when String then %s
|
186
|
+
otherwise 'String(%s)' # else String(%s)
|
187
|
+
# end
|
188
|
+
on_error 'TypeError', act: 'nil' # rescue TypeError => e
|
189
|
+
# nil
|
190
|
+
on_error 'StandardError', act: '""' # rescue StandardError => e
|
191
|
+
# ""
|
192
|
+
end # end
|
163
193
|
```
|
164
194
|
|
165
195
|
## Extensions
|
data/lib/fast_attributes.rb
CHANGED
@@ -47,12 +47,12 @@ module FastAttributes
|
|
47
47
|
|
48
48
|
def compile_setter
|
49
49
|
each_attribute do |attribute, type|
|
50
|
-
type_cast
|
51
|
-
|
50
|
+
type_cast = FastAttributes.get_type_casting(type)
|
51
|
+
method_body = type_cast.compile_method_body(attribute, 'value')
|
52
52
|
|
53
53
|
@methods.module_eval <<-EOS, __FILE__, __LINE__ + 1
|
54
54
|
def #{attribute}=(value)
|
55
|
-
@#{attribute} = #{
|
55
|
+
@#{attribute} = #{method_body}
|
56
56
|
end
|
57
57
|
EOS
|
58
58
|
end
|
@@ -97,19 +97,5 @@ module FastAttributes
|
|
97
97
|
end
|
98
98
|
end
|
99
99
|
end
|
100
|
-
|
101
|
-
private
|
102
|
-
|
103
|
-
def escape_template(template, argument_name)
|
104
|
-
template.gsub(/%+s/) do |match|
|
105
|
-
match.each_char.each_slice(2).map do |placeholder|
|
106
|
-
case placeholder
|
107
|
-
when %w[% s] then argument_name
|
108
|
-
when %w[% %] then '%'
|
109
|
-
else placeholder.join
|
110
|
-
end
|
111
|
-
end.join
|
112
|
-
end
|
113
|
-
end
|
114
100
|
end
|
115
101
|
end
|
@@ -3,9 +3,30 @@ module FastAttributes
|
|
3
3
|
class UnknownTypeCastingError < StandardError
|
4
4
|
end
|
5
5
|
|
6
|
-
|
7
|
-
|
8
|
-
|
6
|
+
class InvalidValueError < TypeError
|
7
|
+
end
|
8
|
+
|
9
|
+
def initialize(type)
|
10
|
+
@type = type
|
11
|
+
@if_conditions = []
|
12
|
+
@else_condition = %q(raise FastAttributes::TypeCast::UnknownTypeCastingError, 'Type casting is not defined')
|
13
|
+
@rescue_conditions = nil
|
14
|
+
@default_rescue = %(raise FastAttributes::TypeCast::InvalidValueError, %(Invalid value "\#{%s}" for attribute "%a" of type "#{@type}"))
|
15
|
+
end
|
16
|
+
|
17
|
+
class << self
|
18
|
+
def escape_template(template, attribute_name, argument_name)
|
19
|
+
template.gsub(/%+a|%+s/) do |match|
|
20
|
+
match.each_char.each_slice(2).map do |placeholder|
|
21
|
+
case placeholder
|
22
|
+
when %w[% a] then attribute_name
|
23
|
+
when %w[% s] then argument_name
|
24
|
+
when %w[% %] then '%'
|
25
|
+
else placeholder.join
|
26
|
+
end
|
27
|
+
end.join
|
28
|
+
end
|
29
|
+
end
|
9
30
|
end
|
10
31
|
|
11
32
|
def from(condition, options = {})
|
@@ -16,8 +37,13 @@ module FastAttributes
|
|
16
37
|
@else_condition = else_condition
|
17
38
|
end
|
18
39
|
|
19
|
-
def
|
20
|
-
@
|
40
|
+
def on_error(error, options = {})
|
41
|
+
@rescue_conditions ||=[]
|
42
|
+
@rescue_conditions << [error, options[:act]]
|
43
|
+
end
|
44
|
+
|
45
|
+
def type_casting_template
|
46
|
+
@type_casting_template ||= begin
|
21
47
|
if @if_conditions.any?
|
22
48
|
conditions = @if_conditions.map do |from, to|
|
23
49
|
"when #{from}\n" +
|
@@ -34,5 +60,22 @@ module FastAttributes
|
|
34
60
|
end
|
35
61
|
end
|
36
62
|
end
|
63
|
+
|
64
|
+
def rescue_template
|
65
|
+
rescues = @rescue_conditions || [['', @default_rescue]]
|
66
|
+
rescues.map do |error, action|
|
67
|
+
"rescue #{error} => e\n" +
|
68
|
+
" #{action}"
|
69
|
+
end.join("\n")
|
70
|
+
end
|
71
|
+
|
72
|
+
def compile_method_body(attribute_name, argument_name)
|
73
|
+
method_body = "begin\n" +
|
74
|
+
" #{type_casting_template}\n" +
|
75
|
+
"#{rescue_template}\n" +
|
76
|
+
"end"
|
77
|
+
|
78
|
+
self.class.escape_template(method_body, attribute_name, argument_name)
|
79
|
+
end
|
37
80
|
end
|
38
81
|
end
|
@@ -1,12 +1,20 @@
|
|
1
1
|
require 'spec_helper'
|
2
2
|
|
3
3
|
describe FastAttributes::TypeCast do
|
4
|
-
describe '
|
5
|
-
|
4
|
+
describe '.escape_template' do
|
5
|
+
it 'replaces placeholder with proper values' do
|
6
|
+
template = '% %s %%s %%%s %%%%s % %a %%a %%%a %%%%a'
|
7
|
+
escaped = FastAttributes::TypeCast.escape_template(template, 'price', 'arg')
|
8
|
+
expect(escaped).to eq('% arg %s %arg %%s % price %a %price %%a')
|
9
|
+
end
|
10
|
+
end
|
11
|
+
|
12
|
+
describe '#type_casting_template' do
|
13
|
+
let(:type_cast) { FastAttributes::TypeCast.new(String) }
|
6
14
|
|
7
15
|
describe 'without any conditions' do
|
8
16
|
it 'return exception' do
|
9
|
-
expect(type_cast.
|
17
|
+
expect(type_cast.type_casting_template.gsub(' ', '')).to eq <<-EOS.gsub(' ', '').chomp
|
10
18
|
raise FastAttributes::TypeCast::UnknownTypeCastingError, 'Type casting is not defined'
|
11
19
|
EOS
|
12
20
|
end
|
@@ -18,7 +26,7 @@ describe FastAttributes::TypeCast do
|
|
18
26
|
end
|
19
27
|
|
20
28
|
it 'returns one when statement' do
|
21
|
-
expect(type_cast.
|
29
|
+
expect(type_cast.type_casting_template.gsub(' ', '')).to eq <<-EOS.gsub(' ', '').chomp
|
22
30
|
case %s
|
23
31
|
when nil
|
24
32
|
nil
|
@@ -37,7 +45,7 @@ describe FastAttributes::TypeCast do
|
|
37
45
|
end
|
38
46
|
|
39
47
|
it 'returns three when statements' do
|
40
|
-
expect(type_cast.
|
48
|
+
expect(type_cast.type_casting_template.gsub(' ', '')).to eq <<-EOS.gsub(' ', '').chomp
|
41
49
|
case %s
|
42
50
|
when String
|
43
51
|
String(%s)
|
@@ -60,7 +68,7 @@ describe FastAttributes::TypeCast do
|
|
60
68
|
end
|
61
69
|
|
62
70
|
it 'returns 2 when statements and overrides default else condition' do
|
63
|
-
expect(type_cast.
|
71
|
+
expect(type_cast.type_casting_template.gsub(' ', '')).to eq <<-EOS.gsub(' ', '').chomp
|
64
72
|
case %s
|
65
73
|
when Date
|
66
74
|
Date.parse(%s)
|
@@ -79,10 +87,84 @@ describe FastAttributes::TypeCast do
|
|
79
87
|
end
|
80
88
|
|
81
89
|
it 'returns otherwise statement only' do
|
82
|
-
expect(type_cast.
|
90
|
+
expect(type_cast.type_casting_template.gsub(' ', '')).to eq <<-EOS.gsub(' ', '').chomp
|
83
91
|
42 * %s
|
84
92
|
EOS
|
85
93
|
end
|
86
94
|
end
|
87
95
|
end
|
96
|
+
|
97
|
+
describe '#rescue_template' do
|
98
|
+
let(:type_cast) { FastAttributes::TypeCast.new(Float) }
|
99
|
+
|
100
|
+
describe 'when on_error is not defined' do
|
101
|
+
it 'raises default error message' do
|
102
|
+
expect(type_cast.rescue_template.gsub(' ', '')).to eq <<-EOS.gsub(' ', '').chomp
|
103
|
+
rescue => e
|
104
|
+
raise FastAttributes::TypeCast::InvalidValueError, %(Invalid value "\#{%s}" for attribute "%a" of type "Float")
|
105
|
+
EOS
|
106
|
+
end
|
107
|
+
end
|
108
|
+
|
109
|
+
describe 'when on_error is defined' do
|
110
|
+
before do
|
111
|
+
type_cast.on_error 'ArgumentError', act: '"%s" + "%a"'
|
112
|
+
end
|
113
|
+
|
114
|
+
it 'overrides default action' do
|
115
|
+
expect(type_cast.rescue_template.gsub(' ', '')).to eq <<-EOS.gsub(' ', '').chomp
|
116
|
+
rescue ArgumentError => e
|
117
|
+
"%s" + "%a"
|
118
|
+
EOS
|
119
|
+
end
|
120
|
+
end
|
121
|
+
|
122
|
+
describe 'when several on_error methods is called' do
|
123
|
+
before do
|
124
|
+
type_cast.on_error 'ArgumentError', act: '0'
|
125
|
+
type_cast.on_error 'StandardError', act: '1'
|
126
|
+
end
|
127
|
+
|
128
|
+
it 'generates several rescue conditions' do
|
129
|
+
expect(type_cast.rescue_template.gsub(' ', '')).to eq <<-EOS.gsub(' ', '').chomp
|
130
|
+
rescue ArgumentError => e
|
131
|
+
0
|
132
|
+
rescue StandardError => e
|
133
|
+
1
|
134
|
+
EOS
|
135
|
+
end
|
136
|
+
end
|
137
|
+
end
|
138
|
+
|
139
|
+
describe '#compile_method_body' do
|
140
|
+
let(:type_cast) { FastAttributes::TypeCast.new(Float) }
|
141
|
+
|
142
|
+
before do
|
143
|
+
type_cast.from 'nil', to: 'nil'
|
144
|
+
type_cast.from 'Float', to: '%s'
|
145
|
+
type_cast.otherwise 'Float(%s)'
|
146
|
+
type_cast.on_error 'ArgumentError', act: 'raise "a %s %a"'
|
147
|
+
type_cast.on_error 'StandardError', act: 'raise "b %s %a"'
|
148
|
+
end
|
149
|
+
|
150
|
+
|
151
|
+
it 'generates type casting method' do
|
152
|
+
expect(type_cast.compile_method_body('price', 'argument').gsub(' ', '')).to eq <<-EOS.gsub(' ', '').chomp
|
153
|
+
begin
|
154
|
+
case argument
|
155
|
+
when nil
|
156
|
+
nil
|
157
|
+
when Float
|
158
|
+
argument
|
159
|
+
else
|
160
|
+
Float(argument)
|
161
|
+
end
|
162
|
+
rescue ArgumentError => e
|
163
|
+
raise "a argument price"
|
164
|
+
rescue StandardError => e
|
165
|
+
raise "b argument price"
|
166
|
+
end
|
167
|
+
EOS
|
168
|
+
end
|
169
|
+
end
|
88
170
|
end
|
@@ -178,16 +178,19 @@ describe FastAttributes do
|
|
178
178
|
end
|
179
179
|
|
180
180
|
it 'setter methods raise an exception when cannot parse values' do
|
181
|
-
|
181
|
+
object = BasicObject.new
|
182
|
+
def object.to_s; 'BasicObject'; end
|
183
|
+
def object.to_str; 1/0 end
|
182
184
|
|
183
|
-
|
184
|
-
expect{ book.
|
185
|
-
expect{ book.
|
186
|
-
expect{ book.
|
187
|
-
expect{ book.
|
188
|
-
expect{ book.
|
189
|
-
expect{ book.
|
190
|
-
expect{ book.
|
185
|
+
book = Book.new
|
186
|
+
expect{ book.title = object }.to raise_error(FastAttributes::TypeCast::InvalidValueError, 'Invalid value "BasicObject" for attribute "title" of type "String"')
|
187
|
+
expect{ book.name = object }.to raise_error(FastAttributes::TypeCast::InvalidValueError, 'Invalid value "BasicObject" for attribute "name" of type "String"')
|
188
|
+
expect{ book.pages = 'number' }.to raise_error(FastAttributes::TypeCast::InvalidValueError, 'Invalid value "number" for attribute "pages" of type "Integer"')
|
189
|
+
expect{ book.price = 'bigdecimal' }.to raise_error(FastAttributes::TypeCast::InvalidValueError, 'Invalid value "bigdecimal" for attribute "price" of type "BigDecimal"')
|
190
|
+
expect{ book.published = 'date' }.to raise_error(FastAttributes::TypeCast::InvalidValueError, 'Invalid value "date" for attribute "published" of type "Date"')
|
191
|
+
expect{ book.sold = 'time' }.to raise_error(FastAttributes::TypeCast::InvalidValueError, 'Invalid value "time" for attribute "sold" of type "Time"')
|
192
|
+
expect{ book.finished = 'datetime' }.to raise_error(FastAttributes::TypeCast::InvalidValueError, 'Invalid value "datetime" for attribute "finished" of type "DateTime"')
|
193
|
+
expect{ book.rate = 'float' }.to raise_error(FastAttributes::TypeCast::InvalidValueError, 'Invalid value "float" for attribute "rate" of type "Float"')
|
191
194
|
end
|
192
195
|
|
193
196
|
it 'setter method can escape placeholder using double %' do
|
@@ -195,6 +198,16 @@ describe FastAttributes do
|
|
195
198
|
placeholder.value = 3
|
196
199
|
expect(placeholder.value).to eq('value %s %value %%s 2')
|
197
200
|
end
|
201
|
+
|
202
|
+
it 'setter method can accept %a placeholder which return attribute name' do
|
203
|
+
placeholder = PlaceholderClass.new
|
204
|
+
|
205
|
+
placeholder.title = 'attribute name 1'
|
206
|
+
expect(placeholder.title).to eq('title')
|
207
|
+
|
208
|
+
placeholder.title = 'attribute name 2'
|
209
|
+
expect(placeholder.title).to eq('title%a%title%title!')
|
210
|
+
end
|
198
211
|
end
|
199
212
|
|
200
213
|
describe '#define_attributes' do
|
data/spec/fixtures/classes.rb
CHANGED
@@ -77,10 +77,13 @@ class Placeholder < String
|
|
77
77
|
end
|
78
78
|
|
79
79
|
FastAttributes.type_cast Placeholder do
|
80
|
+
from '"attribute name 1"', to: '"%a"'
|
81
|
+
from '"attribute name 2"', to: %q("%a%%a%%%a%#{%a<<'!'}")
|
80
82
|
otherwise '"%s %%s %%%s %%%%s #{5%%%s}"'
|
81
83
|
end
|
82
84
|
|
83
85
|
class PlaceholderClass
|
84
86
|
extend FastAttributes
|
85
87
|
attribute :value, Placeholder
|
88
|
+
attribute :title, Placeholder
|
86
89
|
end
|
metadata
CHANGED
@@ -1,14 +1,14 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: fast_attributes
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 0.
|
4
|
+
version: 0.6.0
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- Kostiantyn Stepaniuk
|
8
8
|
autorequire:
|
9
9
|
bindir: bin
|
10
10
|
cert_chain: []
|
11
|
-
date: 2014-07-
|
11
|
+
date: 2014-07-20 00:00:00.000000000 Z
|
12
12
|
dependencies:
|
13
13
|
- !ruby/object:Gem::Dependency
|
14
14
|
name: bundler
|