fast_attributes 0.1.0 → 0.2.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/CHANGELOG.md +4 -0
- data/README.md +70 -1
- data/fast_attributes.gemspec +1 -0
- data/lib/fast_attributes/builder.rb +94 -0
- data/lib/fast_attributes/version.rb +1 -1
- data/lib/fast_attributes.rb +25 -49
- data/spec/fast_attributes_spec.rb +44 -52
- data/spec/fixtures/classes.rb +37 -0
- data/spec/spec_helper.rb +12 -3
- metadata +18 -3
- data/spec/fixtures/book.rb +0 -10
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA1:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: c733dbbfc7afd222369f25c9db5870a364549e8d
|
4
|
+
data.tar.gz: 22bd1f76a4b062a408d7d2c9ebf307e393f64c2b
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: ebfd89f2154c563f8bb5ab26c7b6ffcf62668b174f76717edd190660b6ff8af46e48d026e7ae08a61477c5df26c15bd7f8962d8f44bc9947f46fd0f1c2b96fa6
|
7
|
+
data.tar.gz: 9992ea942ef045861f5920fa8ff4b23fa6f9f0e2bac5ea8ad95dc521a03fd8a9dd769fd7cd6c0b4a1c63d81c81d051d64e8266afb3f51fd58b28dab648cf8382
|
data/CHANGELOG.md
CHANGED
@@ -1,3 +1,7 @@
|
|
1
|
+
**0.2.0 (June 27, 2014)**
|
2
|
+
* Add `define_attributes` method which allows to generate `initialize` and `attributes`
|
3
|
+
* Raise `FastAttributes::UnsupportedTypeError` error when unknown attribute type is specified
|
4
|
+
|
1
5
|
**0.1.0 (June 26, 2014)**
|
2
6
|
* Support `Integer`, `String`, `Array`, `Date`, `Time` and `DateTime` attribute types
|
3
7
|
|
data/README.md
CHANGED
@@ -2,6 +2,8 @@
|
|
2
2
|
[](http://rubygems.org/gems/fast_attributes)
|
3
3
|
[](https://travis-ci.org/applift/fast_attributes)
|
4
4
|
[](https://coveralls.io/r/applift/fast_attributes?branch=master)
|
5
|
+
[](https://codeclimate.com/github/applift/fast_attributes)
|
6
|
+
[](https://gemnasium.com/applift/fast_attributes)
|
5
7
|
|
6
8
|
## Motivation
|
7
9
|
There are already a lot of good and flexible gems which solve the similar problem, allow to define attributes with their types, for example: [virtus](https://github.com/solnic/virtus) or [attrio](https://github.com/jetrockets/attrio). But the disadvantage of existing gems is performance. So, the goal of `fast_attributes` is to make a simple solution which is fast, understandable and easy extendable.
|
@@ -76,7 +78,74 @@ Or install it yourself as:
|
|
76
78
|
|
77
79
|
## Usage
|
78
80
|
|
79
|
-
|
81
|
+
Define getter/setter methods:
|
82
|
+
```ruby
|
83
|
+
class Book
|
84
|
+
extend FastAttributes
|
85
|
+
|
86
|
+
attribute :title, :name, String
|
87
|
+
attribute :pages, Integer
|
88
|
+
attribute :authors, Array
|
89
|
+
attribute :published, Date
|
90
|
+
attribute :sold, Time
|
91
|
+
attribute :finished, DateTime
|
92
|
+
end
|
93
|
+
|
94
|
+
book = Book.new
|
95
|
+
book.title = 'There and Back Again'
|
96
|
+
book.name = 'The Hobbit'
|
97
|
+
book.pages = '200'
|
98
|
+
book.authors = 'Tolkien'
|
99
|
+
book.published = '1937-09-21'
|
100
|
+
book.sold = '2014-06-25 13:45'
|
101
|
+
book.finished = '1937-08-20 12:35'
|
102
|
+
|
103
|
+
#<Book:0x007f9a0110be20
|
104
|
+
@authors=["Tolkien"],
|
105
|
+
@finished=
|
106
|
+
#<DateTime: 1937-08-20T12:35:00+00:00 ((2428766j,45300s,0n),+0s,2299161j)>,
|
107
|
+
@name="The Hobbit",
|
108
|
+
@pages=200,
|
109
|
+
@published=#<Date: 1937-09-21 ((2428798j,0s,0n),+0s,2299161j)>,
|
110
|
+
@sold=2014-06-25 13:45:00 +0200,
|
111
|
+
@title="There and Back Again">
|
112
|
+
```
|
113
|
+
|
114
|
+
To generate `initialize` and `attributes` methods, attribute definition should be wrapped with `define_attributes`:
|
115
|
+
```ruby
|
116
|
+
class Book
|
117
|
+
extend FastAttributes
|
118
|
+
|
119
|
+
define_attributes initialize: true, attributes: true do
|
120
|
+
attribute :title, :name, String
|
121
|
+
attribute :pages, Integer
|
122
|
+
attribute :authors, Array
|
123
|
+
attribute :published, Date
|
124
|
+
attribute :sold, Time
|
125
|
+
attribute :finished, DateTime
|
126
|
+
end
|
127
|
+
end
|
128
|
+
|
129
|
+
book = Book.new(
|
130
|
+
title: 'There and Back Again',
|
131
|
+
name: 'The Hobbit',
|
132
|
+
pages: '200',
|
133
|
+
authors: 'Tolkien',
|
134
|
+
published: '1937-09-21',
|
135
|
+
sold: '2014-06-25 13:45',
|
136
|
+
finished: '1937-08-20 12:35'
|
137
|
+
)
|
138
|
+
|
139
|
+
#<Book:0x007f9a0110be20
|
140
|
+
@authors=["Tolkien"],
|
141
|
+
@finished=
|
142
|
+
#<DateTime: 1937-08-20T12:35:00+00:00 ((2428766j,45300s,0n),+0s,2299161j)>,
|
143
|
+
@name="The Hobbit",
|
144
|
+
@pages=200,
|
145
|
+
@published=#<Date: 1937-09-21 ((2428798j,0s,0n),+0s,2299161j)>,
|
146
|
+
@sold=2014-06-25 13:45:00 +0200,
|
147
|
+
@title="There and Back Again">
|
148
|
+
```
|
80
149
|
|
81
150
|
## Contributing
|
82
151
|
|
data/fast_attributes.gemspec
CHANGED
@@ -0,0 +1,94 @@
|
|
1
|
+
module FastAttributes
|
2
|
+
class UnsupportedTypeError < TypeError
|
3
|
+
end
|
4
|
+
|
5
|
+
class Builder
|
6
|
+
def initialize(klass, options = {})
|
7
|
+
@klass = klass
|
8
|
+
@options = options
|
9
|
+
@attributes = []
|
10
|
+
end
|
11
|
+
|
12
|
+
def attribute(*attributes, type)
|
13
|
+
unless FastAttributes.type_exists?(type)
|
14
|
+
raise UnsupportedTypeError, %(Unsupported attribute type "#{type.name}")
|
15
|
+
end
|
16
|
+
|
17
|
+
@attributes << [attributes, type]
|
18
|
+
end
|
19
|
+
|
20
|
+
def compile!
|
21
|
+
compile_getter!
|
22
|
+
compile_setter!
|
23
|
+
|
24
|
+
if @options[:initialize]
|
25
|
+
compile_initialize!
|
26
|
+
end
|
27
|
+
|
28
|
+
if @options[:attributes]
|
29
|
+
compile_attributes!
|
30
|
+
end
|
31
|
+
end
|
32
|
+
|
33
|
+
private
|
34
|
+
|
35
|
+
def compile_getter!
|
36
|
+
each_attribute do |attribute, _|
|
37
|
+
@klass.class_eval <<-EOS, __FILE__, __LINE__ + 1
|
38
|
+
def #{attribute} # def name
|
39
|
+
@#{attribute} # @name
|
40
|
+
end # end
|
41
|
+
EOS
|
42
|
+
end
|
43
|
+
end
|
44
|
+
|
45
|
+
def compile_setter!
|
46
|
+
each_attribute do |attribute, type|
|
47
|
+
type_matching = "when #{type.name} then value"
|
48
|
+
type_casting = FastAttributes.get_type_casting(type) % 'value'
|
49
|
+
|
50
|
+
@klass.class_eval <<-EOS, __FILE__, __LINE__ + 1
|
51
|
+
def #{attribute}=(value) # def name=(value)
|
52
|
+
@#{attribute} = case value # @name = case value
|
53
|
+
when nil then nil # when nil then nil
|
54
|
+
#{type_matching} # when String then value
|
55
|
+
else # else
|
56
|
+
#{type_casting} # String(value)
|
57
|
+
end # end
|
58
|
+
end
|
59
|
+
EOS
|
60
|
+
end
|
61
|
+
end
|
62
|
+
|
63
|
+
def compile_initialize!
|
64
|
+
@klass.class_eval <<-EOS, __FILE__, __LINE__ + 1
|
65
|
+
def initialize(attributes = {})
|
66
|
+
attributes.each do |name, value|
|
67
|
+
public_send("\#{name}=", value)
|
68
|
+
end
|
69
|
+
end
|
70
|
+
EOS
|
71
|
+
end
|
72
|
+
|
73
|
+
def compile_attributes!
|
74
|
+
attributes = @attributes.flat_map(&:first)
|
75
|
+
attributes = attributes.map do |attribute|
|
76
|
+
"'#{attribute}' => @#{attribute}"
|
77
|
+
end
|
78
|
+
|
79
|
+
@klass.class_eval <<-EOS, __FILE__, __LINE__ + 1
|
80
|
+
def attributes # def attributes
|
81
|
+
{#{attributes.join(', ')}} # {'name' => @name, ...}
|
82
|
+
end # end
|
83
|
+
EOS
|
84
|
+
end
|
85
|
+
|
86
|
+
def each_attribute
|
87
|
+
@attributes.each do |attributes, type|
|
88
|
+
attributes.each do |attribute|
|
89
|
+
yield attribute, type
|
90
|
+
end
|
91
|
+
end
|
92
|
+
end
|
93
|
+
end
|
94
|
+
end
|
data/lib/fast_attributes.rb
CHANGED
@@ -1,72 +1,48 @@
|
|
1
|
-
require '
|
1
|
+
require 'date'
|
2
|
+
require 'time'
|
2
3
|
|
3
4
|
module FastAttributes
|
4
5
|
class << self
|
5
6
|
def type_casting
|
6
7
|
@type_casting ||= {
|
7
|
-
|
8
|
-
|
9
|
-
|
10
|
-
|
11
|
-
|
12
|
-
|
8
|
+
String => 'String(%s)',
|
9
|
+
Integer => 'Integer(%s)',
|
10
|
+
Array => 'Array(%s)',
|
11
|
+
Date => 'Date.parse(%s)',
|
12
|
+
Time => 'Time.parse(%s)',
|
13
|
+
DateTime => 'DateTime.parse(%s)'
|
13
14
|
}
|
14
15
|
end
|
15
16
|
|
16
17
|
def get_type_casting(klass)
|
17
|
-
@type_casting[
|
18
|
+
@type_casting[klass]
|
18
19
|
end
|
19
20
|
|
20
|
-
def
|
21
|
-
type_casting[
|
21
|
+
def set_type_casting(klass, casting)
|
22
|
+
type_casting[klass] = casting
|
22
23
|
end
|
23
24
|
|
24
25
|
def remove_type_casting(klass)
|
25
|
-
type_casting.delete(
|
26
|
+
type_casting.delete(klass)
|
26
27
|
end
|
27
28
|
|
28
29
|
def type_exists?(klass)
|
29
|
-
type_casting.has_key?(
|
30
|
-
end
|
31
|
-
|
32
|
-
def type_from_class(klass)
|
33
|
-
klass.name
|
30
|
+
type_casting.has_key?(klass)
|
34
31
|
end
|
35
32
|
end
|
36
33
|
|
37
|
-
def
|
38
|
-
|
39
|
-
|
40
|
-
|
41
|
-
|
42
|
-
@fast_attributes ||= []
|
43
|
-
attributes.each do |attribute|
|
44
|
-
@fast_attributes << attribute
|
45
|
-
|
46
|
-
type_matching = "when #{FastAttributes.type_from_class(klass)} then value"
|
47
|
-
type_casting = FastAttributes.get_type_casting(klass) % 'value'
|
48
|
-
all_attributes = @fast_attributes.map do |attr|
|
49
|
-
"'#{attr}'=>@#{attr}"
|
50
|
-
end
|
51
|
-
|
52
|
-
class_eval <<-EOS, __FILE__, __LINE__ + 1
|
53
|
-
def #{attribute} # def name
|
54
|
-
@#{attribute} # @name
|
55
|
-
end # end
|
56
|
-
|
57
|
-
def #{attribute}=(value) # def name=(value)
|
58
|
-
@#{attribute} = case value # @name = case value
|
59
|
-
when nil then nil # when nil then nil
|
60
|
-
#{type_matching} # when String then value
|
61
|
-
else # else
|
62
|
-
#{type_casting} # String(value)
|
63
|
-
end # end
|
64
|
-
end # end
|
34
|
+
def define_attributes(options = {}, &block)
|
35
|
+
builder = Builder.new(self, options)
|
36
|
+
builder.instance_eval(&block)
|
37
|
+
builder.compile!
|
38
|
+
end
|
65
39
|
|
66
|
-
|
67
|
-
|
68
|
-
|
69
|
-
|
70
|
-
end
|
40
|
+
def attribute(*attributes, type)
|
41
|
+
builder = Builder.new(self)
|
42
|
+
builder.attribute *attributes, type
|
43
|
+
builder.compile!
|
71
44
|
end
|
72
45
|
end
|
46
|
+
|
47
|
+
require 'fast_attributes/version'
|
48
|
+
require 'fast_attributes/builder'
|
@@ -4,12 +4,12 @@ describe FastAttributes do
|
|
4
4
|
describe '.type_casting' do
|
5
5
|
it 'returns predefined type casting rules' do
|
6
6
|
expect(FastAttributes.type_casting).to eq({
|
7
|
-
|
8
|
-
|
9
|
-
|
10
|
-
|
11
|
-
|
12
|
-
|
7
|
+
String => 'String(%s)',
|
8
|
+
Integer => 'Integer(%s)',
|
9
|
+
Array => 'Array(%s)',
|
10
|
+
Date => 'Date.parse(%s)',
|
11
|
+
Time => 'Time.parse(%s)',
|
12
|
+
DateTime => 'DateTime.parse(%s)'
|
13
13
|
})
|
14
14
|
end
|
15
15
|
end
|
@@ -21,20 +21,20 @@ describe FastAttributes do
|
|
21
21
|
end
|
22
22
|
end
|
23
23
|
|
24
|
-
describe '.
|
24
|
+
describe '.set_type_casting' do
|
25
25
|
after do
|
26
26
|
FastAttributes.remove_type_casting(OpenStruct)
|
27
27
|
end
|
28
28
|
|
29
29
|
it 'adds type to supported type casting list' do
|
30
|
-
FastAttributes.
|
30
|
+
FastAttributes.set_type_casting(OpenStruct, 'OpenStruct.new(a: %s)')
|
31
31
|
expect(FastAttributes.get_type_casting(OpenStruct)).to eq('OpenStruct.new(a: %s)')
|
32
32
|
end
|
33
33
|
end
|
34
34
|
|
35
35
|
describe '.remove_type_casting' do
|
36
36
|
before do
|
37
|
-
FastAttributes.
|
37
|
+
FastAttributes.set_type_casting(OpenStruct, 'OpenStruct.new(a: %s)')
|
38
38
|
end
|
39
39
|
|
40
40
|
it 'removes type casting function from supported list' do
|
@@ -50,14 +50,13 @@ describe FastAttributes do
|
|
50
50
|
end
|
51
51
|
end
|
52
52
|
|
53
|
-
describe '
|
54
|
-
it '
|
55
|
-
|
56
|
-
|
53
|
+
describe '#attribute' do
|
54
|
+
it 'raises an exception when type is not supported' do
|
55
|
+
type = Class.new(Object) { def self.name; 'CustomType' end }
|
56
|
+
klass = Class.new(Object) { extend FastAttributes }
|
57
|
+
expect{klass.attribute(:name, type)}.to raise_error(FastAttributes::UnsupportedTypeError, 'Unsupported attribute type "CustomType"')
|
57
58
|
end
|
58
|
-
end
|
59
59
|
|
60
|
-
describe '#attribute' do
|
61
60
|
it 'generates getter methods' do
|
62
61
|
book = Book.new
|
63
62
|
expect(book.respond_to?(:title)).to be(true)
|
@@ -80,11 +79,6 @@ describe FastAttributes do
|
|
80
79
|
expect(book.respond_to?(:finished=)).to be(true)
|
81
80
|
end
|
82
81
|
|
83
|
-
it 'generates attributes method' do
|
84
|
-
book = Book.new
|
85
|
-
expect(book.respond_to?(:attributes)).to be(true)
|
86
|
-
end
|
87
|
-
|
88
82
|
it 'setter methods convert values to correct datatype' do
|
89
83
|
book = Book.new
|
90
84
|
book.title = 123
|
@@ -160,39 +154,37 @@ describe FastAttributes do
|
|
160
154
|
expect{ book.sold = 'time' }.to raise_error(ArgumentError)
|
161
155
|
expect{ book.finished = 'datetime' }.to raise_error(ArgumentError)
|
162
156
|
end
|
157
|
+
end
|
163
158
|
|
164
|
-
|
165
|
-
|
166
|
-
|
167
|
-
'
|
168
|
-
|
169
|
-
|
170
|
-
|
171
|
-
|
172
|
-
|
173
|
-
|
174
|
-
|
175
|
-
|
176
|
-
|
177
|
-
|
178
|
-
|
179
|
-
|
180
|
-
|
181
|
-
|
182
|
-
|
183
|
-
|
184
|
-
|
185
|
-
|
186
|
-
|
187
|
-
|
188
|
-
|
189
|
-
|
190
|
-
'
|
191
|
-
'
|
192
|
-
|
193
|
-
'sold' => Time.new(2014, 6, 21, 20, 45, 15),
|
194
|
-
'finished' => DateTime.new(2014, 05, 20, 21, 35, 20),
|
195
|
-
})
|
159
|
+
describe '#define_attributes' do
|
160
|
+
describe 'option initialize: true' do
|
161
|
+
it 'generates initialize method' do
|
162
|
+
reader = Reader.new(name: 104, age: '23')
|
163
|
+
expect(reader.name).to eq('104')
|
164
|
+
expect(reader.age).to be(23)
|
165
|
+
end
|
166
|
+
end
|
167
|
+
|
168
|
+
describe 'option attributes: true' do
|
169
|
+
it 'generates attributes method' do
|
170
|
+
publisher = Publisher.new
|
171
|
+
expect(publisher.attributes).to eq({'name' => nil, 'books' => nil})
|
172
|
+
|
173
|
+
reader = Reader.new
|
174
|
+
expect(reader.attributes).to eq({'name' => nil, 'age' => nil})
|
175
|
+
end
|
176
|
+
|
177
|
+
it 'attributes method return all attributes with their values' do
|
178
|
+
publisher = Publisher.new
|
179
|
+
publisher.name = 101
|
180
|
+
publisher.books = '20'
|
181
|
+
expect(publisher.attributes).to eq({'name' => '101', 'books' => 20})
|
182
|
+
|
183
|
+
reader = Reader.new
|
184
|
+
reader.name = 102
|
185
|
+
reader.age = '25'
|
186
|
+
expect(reader.attributes).to eq({'name' => '102', 'age' => 25})
|
187
|
+
end
|
196
188
|
end
|
197
189
|
end
|
198
190
|
end
|
@@ -0,0 +1,37 @@
|
|
1
|
+
class Book
|
2
|
+
extend FastAttributes
|
3
|
+
|
4
|
+
attribute :title, :name, String
|
5
|
+
attribute :pages, Integer
|
6
|
+
attribute :authors, Array
|
7
|
+
attribute :published, Date
|
8
|
+
attribute :sold, Time
|
9
|
+
attribute :finished, DateTime
|
10
|
+
end
|
11
|
+
|
12
|
+
class Author
|
13
|
+
extend FastAttributes
|
14
|
+
|
15
|
+
define_attributes initialize: true do
|
16
|
+
attribute :name, String
|
17
|
+
attribute :age, Integer
|
18
|
+
end
|
19
|
+
end
|
20
|
+
|
21
|
+
class Publisher
|
22
|
+
extend FastAttributes
|
23
|
+
|
24
|
+
define_attributes attributes: true do
|
25
|
+
attribute :name, String
|
26
|
+
attribute :books, Integer
|
27
|
+
end
|
28
|
+
end
|
29
|
+
|
30
|
+
class Reader
|
31
|
+
extend FastAttributes
|
32
|
+
|
33
|
+
define_attributes initialize: true, attributes: true do
|
34
|
+
attribute :name, String
|
35
|
+
attribute :age, Integer
|
36
|
+
end
|
37
|
+
end
|
data/spec/spec_helper.rb
CHANGED
@@ -1,8 +1,17 @@
|
|
1
|
-
|
2
|
-
|
1
|
+
if ENV['CI']
|
2
|
+
require 'coveralls'
|
3
|
+
Coveralls.wear! do
|
4
|
+
add_filter 'spec'
|
5
|
+
end
|
6
|
+
else
|
7
|
+
require 'simplecov'
|
8
|
+
SimpleCov.start do
|
9
|
+
add_filter 'spec'
|
10
|
+
end
|
11
|
+
end
|
3
12
|
|
4
13
|
require 'fast_attributes'
|
5
14
|
require 'ostruct'
|
6
15
|
require 'date'
|
7
16
|
require 'time'
|
8
|
-
require 'fixtures/
|
17
|
+
require 'fixtures/classes'
|
metadata
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: fast_attributes
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 0.
|
4
|
+
version: 0.2.0
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- Kostiantyn Stepaniuk
|
@@ -66,6 +66,20 @@ dependencies:
|
|
66
66
|
- - "~>"
|
67
67
|
- !ruby/object:Gem::Version
|
68
68
|
version: 0.7.0
|
69
|
+
- !ruby/object:Gem::Dependency
|
70
|
+
name: simplecov
|
71
|
+
requirement: !ruby/object:Gem::Requirement
|
72
|
+
requirements:
|
73
|
+
- - "~>"
|
74
|
+
- !ruby/object:Gem::Version
|
75
|
+
version: 0.8.2
|
76
|
+
type: :development
|
77
|
+
prerelease: false
|
78
|
+
version_requirements: !ruby/object:Gem::Requirement
|
79
|
+
requirements:
|
80
|
+
- - "~>"
|
81
|
+
- !ruby/object:Gem::Version
|
82
|
+
version: 0.8.2
|
69
83
|
description: Fast attributes with data types
|
70
84
|
email:
|
71
85
|
- ks@applift.com
|
@@ -86,9 +100,10 @@ files:
|
|
86
100
|
- benchmarks/comparison.rb
|
87
101
|
- fast_attributes.gemspec
|
88
102
|
- lib/fast_attributes.rb
|
103
|
+
- lib/fast_attributes/builder.rb
|
89
104
|
- lib/fast_attributes/version.rb
|
90
105
|
- spec/fast_attributes_spec.rb
|
91
|
-
- spec/fixtures/
|
106
|
+
- spec/fixtures/classes.rb
|
92
107
|
- spec/spec_helper.rb
|
93
108
|
homepage: https://github.com/applift/fast_attributes
|
94
109
|
licenses:
|
@@ -116,5 +131,5 @@ specification_version: 4
|
|
116
131
|
summary: Fast attributes with data types
|
117
132
|
test_files:
|
118
133
|
- spec/fast_attributes_spec.rb
|
119
|
-
- spec/fixtures/
|
134
|
+
- spec/fixtures/classes.rb
|
120
135
|
- spec/spec_helper.rb
|