attributes_dsl 0.0.1

Sign up to get free protection for your applications and to get access to all the features.
checksums.yaml ADDED
@@ -0,0 +1,7 @@
1
+ ---
2
+ SHA1:
3
+ metadata.gz: da436a8b36ce35a3c4b4b87b33ed84a03a7dd910
4
+ data.tar.gz: 8d7edd3acf452eff9b4255a15a16cfee6f1d00d1
5
+ SHA512:
6
+ metadata.gz: 63ea9addb5f796e1009f0140cc3523b4be461c280811d19b878edb3247ae5e1abe9f1cda60a05c703dfe7c28558bbf2868e2432c0b5957bce0d6ab15b0a8b408
7
+ data.tar.gz: e2555ba15ad33fbadbded4f29c0936687e23273ca534de57a9af3767a82f0a3372561d39c1145ec0031ca44f6afe4b0168d871173573c1b41b2f27ecc722fe74
data/.coveralls.yml ADDED
@@ -0,0 +1,2 @@
1
+ ---
2
+ service_name: travis-ci
data/.gitignore ADDED
@@ -0,0 +1,9 @@
1
+ *.gem
2
+ *.lock
3
+ .bundle/
4
+ .yardoc/
5
+ coverage/
6
+ doc/
7
+ log/
8
+ pkg/
9
+ tmp/
data/.metrics ADDED
@@ -0,0 +1,9 @@
1
+ # Settings for metric_fu and its packages are collected in the `config/metrics`
2
+ # and loaded by the Hexx::Suit::Metrics::MetricFu.
3
+
4
+ begin
5
+ require "hexx-suit"
6
+ Hexx::Suit::Metrics::MetricFu.load
7
+ rescue LoadError
8
+ puts "The 'hexx-suit' gem is not installed"
9
+ end
data/.rspec ADDED
@@ -0,0 +1,2 @@
1
+ --require spec_helper
2
+ --color
data/.rubocop.yml ADDED
@@ -0,0 +1,2 @@
1
+ ---
2
+ inherit_from: "./config/metrics/rubocop.yml"
data/.travis.yml ADDED
@@ -0,0 +1,20 @@
1
+ ---
2
+ language: ruby
3
+ bundler_args: --without metrics --without benchmarks
4
+ cache: bundler
5
+ script: bundle exec rake test:coverage:run
6
+ rvm:
7
+ - '1.9.3'
8
+ - '2.0'
9
+ - '2.1'
10
+ - '2.2'
11
+ - ruby-head
12
+ - rbx-2 --1.9
13
+ - rbx-2 --2.1
14
+ - jruby-1.7-19mode
15
+ - jruby-9.0.0.0
16
+ - jruby-head
17
+ matrix:
18
+ allow_failures:
19
+ - rvm: ruby-head
20
+ - rvm: jruby-head
data/.yardopts ADDED
@@ -0,0 +1,3 @@
1
+ --asset LICENSE
2
+ --exclude lib/attributes_dsl/version.rb
3
+ --output doc/api
data/CHANGELOG.md ADDED
@@ -0,0 +1,3 @@
1
+ ## version 0.0.1 2015-09-10
2
+
3
+ This is the first published version
data/Gemfile ADDED
@@ -0,0 +1,16 @@
1
+ source "https://rubygems.org"
2
+
3
+ gemspec
4
+
5
+ group :metrics do
6
+ gem "hexx-suit", "~> 2.3" if RUBY_ENGINE == "ruby" && RUBY_VERSION >= "2.1"
7
+ end
8
+
9
+ group :benchmarks do
10
+ gem "virtus"
11
+ gem "benchmark-ips"
12
+ gem "active_attr"
13
+ gem "fast_attributes"
14
+ gem "anima"
15
+ gem "kwattr"
16
+ end
data/Guardfile ADDED
@@ -0,0 +1,14 @@
1
+ # encoding: utf-8
2
+
3
+ guard :rspec, cmd: "bundle exec rspec" do
4
+
5
+ watch(%r{^spec/.+_spec\.rb$})
6
+
7
+ watch(%r{^lib/attributes_dsl/(.+)\.rb}) do |m|
8
+ "spec/unit/#{m[1]}_spec.rb"
9
+ end
10
+
11
+ watch("lib/attributes_dsl.rb") { "spec" }
12
+ watch("spec/spec_helper.rb") { "spec" }
13
+
14
+ end # guard :rspec
data/LICENSE ADDED
@@ -0,0 +1,21 @@
1
+ The MIT License
2
+
3
+ Copyright (c) 2015 Andrew Kozin (nepalez), andrew.kozin@gmail.com
4
+
5
+ Permission is hereby granted, free of charge, to any person obtaining a copy
6
+ of this software and associated documentation files (the "Software"), to deal
7
+ in the Software without restriction, including without limitation the rights
8
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9
+ copies of the Software, and to permit persons to whom the Software is
10
+ furnished to do so, subject to the following conditions:
11
+
12
+ The above copyright notice and this permission notice shall be included in
13
+ all copies or substantial portions of the Software.
14
+
15
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
21
+ THE SOFTWARE.
data/README.md ADDED
@@ -0,0 +1,209 @@
1
+ Attributes DSL
2
+ ==============
3
+
4
+ [![Gem Version](https://img.shields.io/gem/v/attributes_dsl.svg?style=flat)][gem]
5
+ [![Build Status](https://img.shields.io/travis/nepalez/attributes_dsl/master.svg?style=flat)][travis]
6
+ [![Dependency Status](https://img.shields.io/gemnasium/nepalez/attributes_dsl.svg?style=flat)][gemnasium]
7
+ [![Code Climate](https://img.shields.io/codeclimate/github/nepalez/attributes_dsl.svg?style=flat)][codeclimate]
8
+ [![Coverage](https://img.shields.io/coveralls/nepalez/attributes_dsl.svg?style=flat)][coveralls]
9
+ [![Inline docs](http://inch-ci.org/github/nepalez/attributes_dsl.svg)][inch]
10
+
11
+ Lightweight DSL to define PORO attributes.
12
+
13
+ Uses immutable (deeply frozen) instances via [ice_nine][ice_nine] gem.
14
+
15
+ Synopsis
16
+ --------
17
+
18
+ ```ruby
19
+ require "attributes_dsl"
20
+
21
+ class User
22
+ extend AttributesDSL
23
+
24
+ # `name` is required should be symbolized
25
+ attribute :name, required: true do |value|
26
+ value.to_s.to_sym
27
+ end
28
+
29
+ # `sex` is optional and set to `:male` by default.
30
+ # It can be set to either :male or :female
31
+ attribute :sex, default: :male do |value|
32
+ (value == :male )? :male : :female
33
+ end
34
+
35
+ # `age` is optional and set to `nil` by default.
36
+ # Then it is converted to integer
37
+ attribute :age, &:to_i
38
+
39
+ # `position` is optional and set to `nil` by default
40
+ attribute :position
41
+
42
+ # All other attributes are ignored
43
+ end
44
+
45
+ user = User.new(name: "Jane", sex: :women, age: "26", place: "Moscow")
46
+ user.attributes
47
+ # => { name: :Jane, sex: :female, age: 26, position: nil }
48
+
49
+ # Aliases for attributes[:some_attribute]
50
+ user.name # => :Jane
51
+ user.sex # => :female
52
+ user.age # => 26
53
+ user.position # => nil
54
+
55
+ # Required attributes should be assigned:
56
+ user = User.new(sex: :women, age: "26", place: "Moscow")
57
+ # => #<ArgumentError "Undefined attributes: name">
58
+ ```
59
+
60
+ Additional Details
61
+ ------------------
62
+
63
+ The `attribute` class method takes the `name` and 2 options:
64
+ - `:default` for the default value (otherwise `nil`);
65
+ - `:required` to declare the attribute as required. It will be ignored if a default value is provided!
66
+
67
+ It is also takes the block, used to coerce a value. The coercer is applied to the default value too.
68
+
69
+ Also notice, that instance methods (like `#name`) are just aliases for the corresponding value of the `#attributes` hash. Instance variables aren't defined for them (to ensure syncronization between `#name` and `#attributes[:name]`):
70
+
71
+ ```ruby
72
+ user = User.new(name: "John")
73
+ user.attributes # => { name: :John, sex: :male, age: 0, position: nil }
74
+ user.name # => :John
75
+
76
+ # but
77
+ user.instance_variable_get :@name # => nil
78
+ ```
79
+
80
+ You're free to redefine attributes (class settings are used by the initializer only):
81
+
82
+ ```ruby
83
+ user.attributes[:name] = "Jim"
84
+ user.attributes # => { name: "Jim", sex: :male, age: 0, position: nil }
85
+ user.name # => "Jim"
86
+ ```
87
+
88
+ But if you (like me) prefer instance immutability, you can deeply freeze instances safely:
89
+
90
+ ```ruby
91
+ require "ice_nine"
92
+
93
+ class User
94
+ # ... staff like before
95
+
96
+ def initializer(attributes)
97
+ super
98
+ IceNine.deep_freeze(self)
99
+ end
100
+ end
101
+
102
+ args = { user: "Joe" }
103
+
104
+ user = User.new(args)
105
+ user.frozen? # => true
106
+ user.attributes.frozen? # => true
107
+
108
+ # "Safely" means:
109
+ args.frozen? # => false
110
+ ```
111
+
112
+ Freezing instances to exclude side effects is a part of my coding style. That's why the gem doesn't (and won't do) care about changing attributes after initialization.
113
+
114
+ Benchmarks
115
+ ----------
116
+
117
+ The list of gems to compare has been taken from [Idiosyncratic Ruby #18][idiosyncratic_ruby] by Jan Lelis. I've selected only those gems that support initialization from hash.
118
+
119
+ Look at the [benchmark source][benchmark] for details.
120
+
121
+ The results are following:
122
+
123
+ ```
124
+ -------------------------------------------------
125
+ anima 202.863k (± 5.1%) i/s - 1.023M
126
+ kwattr 171.461k (± 5.3%) i/s - 855.680k
127
+ fast_attributes 156.596k (± 2.5%) i/s - 785.792k
128
+ attributes_dsl 58.966k (± 4.6%) i/s - 296.514k
129
+ active_attr 58.322k (± 2.8%) i/s - 293.205k
130
+ virtus 45.734k (± 2.7%) i/s - 228.960k
131
+
132
+ Comparison:
133
+ anima: 202862.9 i/s
134
+ kwattr: 171460.9 i/s - 1.18x slower
135
+ fast_attributes: 156596.3 i/s - 1.30x slower
136
+ attributes_dsl: 58966.5 i/s - 3.44x slower
137
+ active_attr: 58321.6 i/s - 3.48x slower
138
+ virtus: 45734.3 i/s - 4.44x slower
139
+ ```
140
+
141
+ Results above are pretty reasonable.
142
+
143
+ The gem is faster than `virtus` that has many additional features.
144
+
145
+ It is as fast as `active_attrs` (but has more customizable coercers).
146
+
147
+ It is 2 times slower than `fast_attributes` that has no coercer and default values. And it is 3-3.5 times slower than `anima` and `kwattr` that provides only the base settings.
148
+
149
+ Installation
150
+ ------------
151
+
152
+ Add this line to your application's Gemfile:
153
+
154
+ ```ruby
155
+ # Gemfile
156
+ gem "attributes_dsl"
157
+ ```
158
+
159
+ Then execute:
160
+
161
+ ```
162
+ bundle
163
+ ```
164
+
165
+ Or add it manually:
166
+
167
+ ```
168
+ gem install attributes_dsl
169
+ ```
170
+
171
+ Compatibility
172
+ -------------
173
+
174
+ Tested under rubies [compatible to MRI 1.9+][versions].
175
+
176
+ Uses [RSpec][rspec] 3.0+ for testing and [hexx-suit][hexx-suit] for dev/test tools collection.
177
+
178
+ 100% [mutant]-proof covered.
179
+
180
+ Contributing
181
+ ------------
182
+
183
+ * Read the [STYLEGUIDE](config/metrics/STYLEGUIDE)
184
+ * [Fork the project](https://github.com/nepalez/attributes_dsl)
185
+ * Create your feature branch (`git checkout -b my-new-feature`)
186
+ * Add tests for it
187
+ * Run `rake mutant` or `rake exhort` to ensure 100% [mutant][mutant] coverage
188
+ * Commit your changes (`git commit -am '[UPDATE] Add some feature'`)
189
+ * Push to the branch (`git push origin my-new-feature`)
190
+ * Create a new Pull Request
191
+
192
+ License
193
+ -------
194
+
195
+ See the [MIT LICENSE](LICENSE).
196
+
197
+ [codeclimate]: https://codeclimate.com/github/nepalez/attributes_dsl
198
+ [coveralls]: https://coveralls.io/r/nepalez/attributes_dsl
199
+ [gem]: https://rubygems.org/gems/attributes_dsl
200
+ [gemnasium]: https://gemnasium.com/nepalez/attributes_dsl
201
+ [travis]: https://travis-ci.org/nepalez/attributes_dsl
202
+ [inch]: https://inch-ci.org/github/nepalez/attributes_dsl
203
+ [versions]: .travis.yml
204
+ [rspec]: http://rspec.org
205
+ [hexx-suit]: https://github.com/hexx-rb/hexx-suit
206
+ [mutant]: https://github.com/mbj/mutant
207
+ [ice_nine]: https://github.com/dkubb/ice_nine
208
+ [idiosyncratic_ruby]: http://idiosyncratic-ruby.com/18-con-struct-attributes.html
209
+ [benchmark]: benchmark/run.rb
data/Rakefile ADDED
@@ -0,0 +1,39 @@
1
+ # encoding: utf-8
2
+
3
+ require "bundler/setup"
4
+ require "rubygems"
5
+
6
+ # Loads bundler tasks
7
+ Bundler::GemHelper.install_tasks
8
+
9
+ # Loads the Hexx::RSpec and its tasks
10
+ begin
11
+ require "hexx-suit"
12
+ Hexx::Suit.install_tasks
13
+ rescue LoadError
14
+ require "hexx-rspec"
15
+ Hexx::RSpec.install_tasks
16
+ end
17
+
18
+ desc "Sets the Hexx::RSpec :test task to default"
19
+ task :default do
20
+ system "bundle exec rake test:coverage:run"
21
+ end
22
+
23
+ desc "Runs mutation metric for testing"
24
+ task :mutant do
25
+ system "mutant -r ./spec/spec_helper --use rspec AttributesDSL* --fail-fast"
26
+ end
27
+
28
+ desc "Exhort all evils"
29
+ task :exhort do
30
+ system "mutant -r ./spec/spec_helper --use rspec AttributesDSL*"
31
+ end
32
+
33
+ desc "Runs all the necessary metrics before making a commit"
34
+ task prepare: %w(exhort check:inch check:rubocop check:fu)
35
+
36
+ desc "Runs benchmarks"
37
+ task :benchmark do
38
+ system "ruby benchmark/run.rb"
39
+ end
@@ -0,0 +1,26 @@
1
+ $LOAD_PATH.push File.expand_path("../lib", __FILE__)
2
+ require "attributes_dsl/version"
3
+
4
+ Gem::Specification.new do |gem|
5
+
6
+ gem.name = "attributes_dsl"
7
+ gem.version = AttributesDSL::VERSION.dup
8
+ gem.author = "Andrew Kozin"
9
+ gem.email = "andrew.kozin@gmail.com"
10
+ gem.homepage = "https://github.com/nepalez/attributes_dsl"
11
+ gem.summary = "Lightweight DSL to define PORO attributes"
12
+ gem.license = "MIT"
13
+
14
+ gem.files = `git ls-files`.split($INPUT_RECORD_SEPARATOR)
15
+ gem.test_files = Dir["spec/**/*.rb"]
16
+ gem.extra_rdoc_files = Dir["README.md", "LICENSE"]
17
+ gem.require_paths = ["lib"]
18
+
19
+ gem.required_ruby_version = ">= 1.9.3"
20
+
21
+ gem.add_runtime_dependency "ice_nine", "~> 0.11"
22
+ gem.add_runtime_dependency "equalizer", "~> 0.0", ">= 0.0.11"
23
+
24
+ gem.add_development_dependency "hexx-rspec", "~> 0.5", ">= 0.5.2"
25
+
26
+ end # Gem::Specification
data/benchmark/run.rb ADDED
@@ -0,0 +1,122 @@
1
+ require "benchmark/ips"
2
+
3
+ module AttributesDSLExample
4
+ require "attributes_dsl"
5
+
6
+ class User
7
+ extend AttributesDSL
8
+
9
+ attribute :foo, required: true, &:to_s
10
+ attribute :bar, default: :BAR, &:to_s
11
+ attribute :baz, default: :BAZ, &:to_s
12
+ attribute :qux, &:to_s
13
+ end
14
+
15
+ def self.call
16
+ User.new(foo: :FOO, bar: :BAR, baz: :BAZ, qux: :QUX)
17
+ end
18
+ end
19
+
20
+ module VirtusExample
21
+ require "virtus"
22
+
23
+ class User
24
+ include Virtus.model
25
+
26
+ attribute :foo, String, required: true
27
+ attribute :bar, String, default: :BAR
28
+ attribute :baz, String, default: :BAZ
29
+ attribute :qux, String
30
+ end
31
+
32
+ def self.call
33
+ User.new(foo: :FOO, bar: :BAR, baz: :BAZ, qux: :QUX)
34
+ end
35
+ end
36
+
37
+ module ActiveAttrExample
38
+ require "active_attr"
39
+
40
+ class User
41
+ include ActiveAttr::MassAssignment
42
+ attr_accessor :foo, :bar, :baz, :qux
43
+ end
44
+
45
+ def self.call
46
+ User.new(foo: :FOO, bar: :BAR, baz: :BAZ, qux: :QUX)
47
+ end
48
+ end
49
+
50
+ module FastAttributesExample
51
+ require "fast_attributes"
52
+
53
+ class User
54
+ extend FastAttributes
55
+
56
+ define_attributes initialize: true do
57
+ attribute :foo, String
58
+ attribute :bar, String
59
+ attribute :baz, String
60
+ attribute :qux, String
61
+ end
62
+ end
63
+
64
+ def self.call
65
+ User.new(foo: :FOO, bar: :BAR, baz: :BAZ, qux: :QUX)
66
+ end
67
+ end
68
+
69
+ module AnimaExample
70
+ require "anima"
71
+
72
+ class User
73
+ include Anima.new(:foo, :bar, :baz, :qux)
74
+ end
75
+
76
+ def self.call
77
+ User.new(foo: :FOO, bar: :BAR, baz: :BAZ, qux: :QUX)
78
+ end
79
+ end
80
+
81
+ module KwattrExample
82
+ require "kwattr"
83
+
84
+ class User
85
+ kwattr :foo, :bar, :baz, :qux
86
+ end
87
+
88
+ def self.call
89
+ User.new(foo: :FOO, bar: :BAR, baz: :BAZ, qux: :QUX)
90
+ end
91
+ end
92
+
93
+ # Provide a benchmark
94
+ Benchmark.ips do |x|
95
+ x.config time: 5, warmup: 5
96
+
97
+ x.report("attributes_dsl") do
98
+ AttributesDSLExample.call
99
+ end
100
+
101
+ x.report("virtus") do
102
+ VirtusExample.call
103
+ end
104
+
105
+ x.report("active_attr") do
106
+ AttributesDSLExample.call
107
+ end
108
+
109
+ x.report("fast_attributes") do
110
+ FastAttributesExample.call
111
+ end
112
+
113
+ x.report("anima") do
114
+ AnimaExample.call
115
+ end
116
+
117
+ x.report("kwattr") do
118
+ KwattrExample.call
119
+ end
120
+
121
+ x.compare!
122
+ end