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 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