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 CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA1:
3
- metadata.gz: 7b5f750706150406ddcaa4787a9dece903c0c392
4
- data.tar.gz: 3c365ac0da073679f56adffa0af47ff96ac8105c
3
+ metadata.gz: 156410fa3391841eaf2b70060f777f96e70b17cb
4
+ data.tar.gz: d272b8684e3b06afe7de0562a06f116f9de9f168
5
5
  SHA512:
6
- metadata.gz: 132afa531200fe1b2ded5ae0a17b0d9d0e232f6dd7d3216d55281e52e19c0ecec322f861c05b8aa353fb6c3cc52c9efc76716b173a1ea64d040508d9fcf66f99
7
- data.tar.gz: 38ef6165ba6fe8f57b5d8c67974671b9de4b5990b5d0d5176c79226432560e5deb85fd33bb1f073ef77f237b7ba8bf2088319547ad40c21a4752fe55573e603e
6
+ metadata.gz: 5770eaeb6359e8fe89fe78301fc6e8dc4242c02edcf99b132def5c27fed34b3e30d2c270c33a4b8ad2f01695d48789ae93d40ef35f8abdef86acaf1984db035d
7
+ data.tar.gz: 087d805216430a6cb94d7dd8bce84b72f8fdc644f1853f957aefb39941109f054f0f0ff1c985902a2d7f018a8f863698ec097a81cb8c87494367b7303888182c
@@ -1,2 +1,6 @@
1
1
  language: ruby
2
2
  script: bundle exec rspec spec
3
+ rvm:
4
+ - 1.9.3
5
+ - 2.0.0
6
+ - 2.1.1
@@ -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 `Integer` or `Float`
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 code:
146
+ Method `FastAttributes.set_type_casting` generates the following template:
147
147
  ```ruby
148
148
  FastAttributes.set_type_casting String, 'String(%s)'
149
- # case %s
150
- # when nil then nil
151
- # when String then %s
152
- # else String(%s)
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 # case String
159
- from 'nil', to: 'nil' # when nil then nil
160
- from 'String', to: '%s' # when String then %s
161
- otherwise 'String(%s)' # else String(%s)
162
- end # end
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
@@ -32,7 +32,7 @@ module FastAttributes
32
32
  end
33
33
 
34
34
  def type_cast(klass, &block)
35
- type_cast = TypeCast.new
35
+ type_cast = TypeCast.new(klass)
36
36
  type_cast.instance_eval(&block)
37
37
  type_casting[klass] = type_cast
38
38
  end
@@ -47,12 +47,12 @@ module FastAttributes
47
47
 
48
48
  def compile_setter
49
49
  each_attribute do |attribute, type|
50
- type_cast = FastAttributes.get_type_casting(type)
51
- template = escape_template(type_cast.template, 'value')
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} = #{template}
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
- def initialize
7
- @if_conditions = []
8
- @else_condition = %q(raise FastAttributes::TypeCast::UnknownTypeCastingError, 'Type casting is not defined')
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 template
20
- @template ||= begin
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,3 +1,3 @@
1
1
  module FastAttributes
2
- VERSION = '0.5.2'
2
+ VERSION = '0.6.0'
3
3
  end
@@ -1,12 +1,20 @@
1
1
  require 'spec_helper'
2
2
 
3
3
  describe FastAttributes::TypeCast do
4
- describe '#template' do
5
- let(:type_cast) { FastAttributes::TypeCast.new }
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.template.gsub(' ', '')).to eq <<-EOS.gsub(' ', '').chomp
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.template.gsub(' ', '')).to eq <<-EOS.gsub(' ', '').chomp
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.template.gsub(' ', '')).to eq <<-EOS.gsub(' ', '').chomp
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.template.gsub(' ', '')).to eq <<-EOS.gsub(' ', '').chomp
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.template.gsub(' ', '')).to eq <<-EOS.gsub(' ', '').chomp
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
- book = Book.new
181
+ object = BasicObject.new
182
+ def object.to_s; 'BasicObject'; end
183
+ def object.to_str; 1/0 end
182
184
 
183
- expect{ book.title = BasicObject.new }.to raise_error(TypeError)
184
- expect{ book.name = BasicObject.new }.to raise_error(TypeError)
185
- expect{ book.pages = 'number' }.to raise_error(ArgumentError)
186
- expect{ book.price = 'bigdecimal' }.to raise_error(ArgumentError)
187
- expect{ book.published = 'date' }.to raise_error(ArgumentError)
188
- expect{ book.sold = 'time' }.to raise_error(ArgumentError)
189
- expect{ book.finished = 'datetime' }.to raise_error(ArgumentError)
190
- expect{ book.rate = 'float' }.to raise_error(ArgumentError)
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
@@ -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.5.2
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-18 00:00:00.000000000 Z
11
+ date: 2014-07-20 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: bundler