fast_attributes 0.1.0 → 0.2.0

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
  SHA1:
3
- metadata.gz: 77f2e59818faf79d91ebf8f90f76cebc2bab978b
4
- data.tar.gz: bb9905bc7899f5c2d4d4e69c8f9c42760e28be3e
3
+ metadata.gz: c733dbbfc7afd222369f25c9db5870a364549e8d
4
+ data.tar.gz: 22bd1f76a4b062a408d7d2c9ebf307e393f64c2b
5
5
  SHA512:
6
- metadata.gz: 0c6a59bfb0c1a73be212f8c2f9485186aaf3282d90d09633aac841fffd083645d4c12c9afc144781c38f850aa30362661710d1833886a834b544a9d568ae7410
7
- data.tar.gz: 009c11fbd431c352d1c8fe72502851a14a1eaeb55c73184c3b2585ccf2db1b6dbdebff910bc27a05cc5e78e503f82398a95d4a719f44ea28600b191aad7bf848
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
  [![Gem Version](http://img.shields.io/gem/v/fast_attributes.svg)](http://rubygems.org/gems/fast_attributes)
3
3
  [![Build Status](http://img.shields.io/travis/applift/fast_attributes.svg)](https://travis-ci.org/applift/fast_attributes)
4
4
  [![Coverage Status](http://img.shields.io/coveralls/applift/fast_attributes.svg)](https://coveralls.io/r/applift/fast_attributes?branch=master)
5
+ [![Code Climate](http://img.shields.io/codeclimate/github/applift/fast_attributes.svg)](https://codeclimate.com/github/applift/fast_attributes)
6
+ [![Dependency Status](http://img.shields.io/gemnasium/applift/fast_attributes.svg)](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
- TODO: Write usage instructions here
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
 
@@ -22,4 +22,5 @@ Gem::Specification.new do |spec|
22
22
  spec.add_development_dependency 'rake'
23
23
  spec.add_development_dependency 'rspec', '~> 3.0.0'
24
24
  spec.add_development_dependency 'coveralls', '~> 0.7.0'
25
+ spec.add_development_dependency 'simplecov', '~> 0.8.2'
25
26
  end
@@ -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
@@ -1,3 +1,3 @@
1
1
  module FastAttributes
2
- VERSION = '0.1.0'
2
+ VERSION = '0.2.0'
3
3
  end
@@ -1,72 +1,48 @@
1
- require 'fast_attributes/version'
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
- '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)'
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[type_from_class(klass)]
18
+ @type_casting[klass]
18
19
  end
19
20
 
20
- def add_type_casting(klass, casting)
21
- type_casting[type_from_class(klass)] = 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(type_from_class(klass))
26
+ type_casting.delete(klass)
26
27
  end
27
28
 
28
29
  def type_exists?(klass)
29
- type_casting.has_key?(type_from_class(klass))
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 attribute(*attributes, klass)
38
- unless FastAttributes.type_exists?(klass)
39
- raise %(Unsupported attribute type "#{FastAttributes.type_from_class(klass)}")
40
- end
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
- def attributes # def attributes
67
- {#{all_attributes.join(',')}} # {'name'=>@name}
68
- end # end
69
- EOS
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
- '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)'
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 '.add_type_casting' do
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.add_type_casting(OpenStruct, 'OpenStruct.new(a: %s)')
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.add_type_casting(OpenStruct, 'OpenStruct.new(a: %s)')
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 '.type_from_class' do
54
- it 'converts class into type name' do
55
- expect(FastAttributes.type_from_class(DateTime)).to eq('DateTime')
56
- expect(FastAttributes.type_from_class(OpenStruct)).to eq('OpenStruct')
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
- it 'attributes method return all attributes with nil values by default' do
165
- book = Book.new
166
- expect(book.attributes).to eq({
167
- 'title' => nil,
168
- 'name' => nil,
169
- 'pages' => nil,
170
- 'authors' => nil,
171
- 'published' => nil,
172
- 'sold' => nil,
173
- 'finished' => nil,
174
- })
175
- end
176
-
177
- it 'attributes method return all attributes with their values' do
178
- book = Book.new
179
- book.title = 'One'
180
- book.name = 'Two'
181
- book.pages = 250
182
- book.authors = %w[Jobs]
183
- book.published = Date.new(2014, 06, 21)
184
- book.sold = Time.new(2014, 6, 21, 20, 45, 15)
185
- book.finished = DateTime.new(2014, 05, 20, 21, 35, 20)
186
-
187
- expect(book.attributes).to eq({
188
- 'title' => 'One',
189
- 'name' => 'Two',
190
- 'pages' => 250,
191
- 'authors' => %w[Jobs],
192
- 'published' => Date.new(2014, 06, 21),
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
- require 'coveralls'
2
- Coveralls.wear!
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/book'
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.1.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/book.rb
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/book.rb
134
+ - spec/fixtures/classes.rb
120
135
  - spec/spec_helper.rb
@@ -1,10 +0,0 @@
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