attire 0.0.1
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 +7 -0
- data/.gitignore +15 -0
- data/.rspec +1 -0
- data/Gemfile +4 -0
- data/LICENSE.txt +22 -0
- data/README.md +88 -0
- data/Rakefile +2 -0
- data/attire.gemspec +21 -0
- data/lib/attire/attr_init.rb +76 -0
- data/lib/attire/version.rb +3 -0
- data/lib/attire.rb +37 -0
- data/spec/attr_init_spec.rb +47 -0
- data/spec/attr_method_spec.rb +16 -0
- data/spec/attr_query_spec.rb +19 -0
- data/spec/fattr_spec.rb +17 -0
- data/spec/spec_helper.rb +5 -0
- metadata +78 -0
checksums.yaml
ADDED
@@ -0,0 +1,7 @@
|
|
1
|
+
---
|
2
|
+
SHA1:
|
3
|
+
metadata.gz: 53a76fd77e586cecce6fea04de34aea4225090d3
|
4
|
+
data.tar.gz: a8e7d3b291bc0adc3845a7d893d4bca319256254
|
5
|
+
SHA512:
|
6
|
+
metadata.gz: 66e94421748b69661c57680844088fee92e8474a252b7130e5c6e9918f8f6b16f015746c3b1ce51fa851ac6f561cfd53055ef74efd4e3bbfa27106f7affa8931
|
7
|
+
data.tar.gz: 0efe0cdb17fc8e4903746b983ef0b039b13db70ebb783bbcb732599c9270b43dd604cb14131b9b33292368e1cfc71a48351ce245b84f91c3be209dadb90534f0
|
data/.gitignore
ADDED
data/.rspec
ADDED
@@ -0,0 +1 @@
|
|
1
|
+
--require spec_helper
|
data/Gemfile
ADDED
data/LICENSE.txt
ADDED
@@ -0,0 +1,22 @@
|
|
1
|
+
Copyright (c) 2015 Max White
|
2
|
+
|
3
|
+
MIT License
|
4
|
+
|
5
|
+
Permission is hereby granted, free of charge, to any person obtaining
|
6
|
+
a copy of this software and associated documentation files (the
|
7
|
+
"Software"), to deal in the Software without restriction, including
|
8
|
+
without limitation the rights to use, copy, modify, merge, publish,
|
9
|
+
distribute, sublicense, and/or sell copies of the Software, and to
|
10
|
+
permit persons to whom the Software is furnished to do so, subject to
|
11
|
+
the following conditions:
|
12
|
+
|
13
|
+
The above copyright notice and this permission notice shall be
|
14
|
+
included in all copies or substantial portions of the Software.
|
15
|
+
|
16
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
|
17
|
+
EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
|
18
|
+
MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
|
19
|
+
NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
|
20
|
+
LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
|
21
|
+
OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
|
22
|
+
WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
data/README.md
ADDED
@@ -0,0 +1,88 @@
|
|
1
|
+
# Attire
|
2
|
+
|
3
|
+
Convenience methods to remove some boiler plate in defining classes. Heavily inspired by [attr_extras](https://github.com/barsoom/attr_extras).
|
4
|
+
|
5
|
+
## Usage
|
6
|
+
|
7
|
+
### `attr_init :foo, :bar, fizz: 15, pop: nil`
|
8
|
+
|
9
|
+
Defines the following initializer:
|
10
|
+
|
11
|
+
``` ruby
|
12
|
+
def initializer(foo, bar, opts = {})
|
13
|
+
@foo = foo
|
14
|
+
@bar = bar
|
15
|
+
@fizz = opts[:fizz] || 15
|
16
|
+
@pop = opts[:pop]
|
17
|
+
end
|
18
|
+
```
|
19
|
+
|
20
|
+
`attr_init` can also accept a block which will be invoked after initialization.
|
21
|
+
|
22
|
+
### `attr_method :select, :bar`
|
23
|
+
|
24
|
+
Shortcut for:
|
25
|
+
|
26
|
+
``` ruby
|
27
|
+
attr_init :bar
|
28
|
+
|
29
|
+
def self.select(bar)
|
30
|
+
new(bar).select
|
31
|
+
end
|
32
|
+
```
|
33
|
+
|
34
|
+
This is useful for method objects/use cases:
|
35
|
+
|
36
|
+
``` ruby
|
37
|
+
class CheeseSpreader
|
38
|
+
attr_method :spread, :cheese, crackers: Jacobs.new
|
39
|
+
|
40
|
+
def spread
|
41
|
+
raise CheeseError unless cheese.is_a?(Cheddar)
|
42
|
+
cheese.spread_on(crackers)
|
43
|
+
end
|
44
|
+
end
|
45
|
+
|
46
|
+
...
|
47
|
+
|
48
|
+
my_cheese = Roquefort.new
|
49
|
+
CheeseSpreader.spread(my_cheese) # CheeseError
|
50
|
+
```
|
51
|
+
|
52
|
+
### `attr_query :foo?`
|
53
|
+
|
54
|
+
Defines query methods like `foo?`, which is true if `foo` is truthy.
|
55
|
+
|
56
|
+
### `fattr_init :foo`
|
57
|
+
|
58
|
+
Calls `self.freeze` after initialization to make object immutable.
|
59
|
+
|
60
|
+
### `fattr_method :foo`
|
61
|
+
|
62
|
+
Same as `fattr_init`, only for `attr_method`.
|
63
|
+
|
64
|
+
|
65
|
+
## Installation
|
66
|
+
|
67
|
+
Add this line to your application's Gemfile:
|
68
|
+
|
69
|
+
```ruby
|
70
|
+
gem 'attire'
|
71
|
+
```
|
72
|
+
|
73
|
+
And then execute:
|
74
|
+
|
75
|
+
$ bundle
|
76
|
+
|
77
|
+
Or install it yourself as:
|
78
|
+
|
79
|
+
$ gem install attire
|
80
|
+
|
81
|
+
|
82
|
+
## Contributing
|
83
|
+
|
84
|
+
1. Fork it ( https://github.com/[my-github-username]/attire/fork )
|
85
|
+
2. Create your feature branch (`git checkout -b my-new-feature`)
|
86
|
+
3. Commit your changes (`git commit -am 'Add some feature'`)
|
87
|
+
4. Push to the branch (`git push origin my-new-feature`)
|
88
|
+
5. Create a new Pull Request
|
data/Rakefile
ADDED
data/attire.gemspec
ADDED
@@ -0,0 +1,21 @@
|
|
1
|
+
# coding: utf-8
|
2
|
+
lib = File.expand_path('../lib', __FILE__)
|
3
|
+
$LOAD_PATH.unshift(lib) unless $LOAD_PATH.include?(lib)
|
4
|
+
require 'attire/version'
|
5
|
+
|
6
|
+
Gem::Specification.new do |spec|
|
7
|
+
spec.name = "attire"
|
8
|
+
spec.version = Attire::VERSION
|
9
|
+
spec.authors = ["Max White"]
|
10
|
+
spec.email = ["mushishi78@gmail.com"]
|
11
|
+
spec.summary = %q{Convenience methods to remove some boiler plate in defining classes.}
|
12
|
+
spec.homepage = "https://github.com/mushishi78/attire"
|
13
|
+
spec.license = "MIT"
|
14
|
+
|
15
|
+
spec.files = `git ls-files -z`.split("\x0")
|
16
|
+
spec.executables = spec.files.grep(%r{^bin/}) { |f| File.basename(f) }
|
17
|
+
spec.test_files = spec.files.grep(%r{^(test|spec|features)/})
|
18
|
+
spec.require_paths = ["lib"]
|
19
|
+
|
20
|
+
spec.add_development_dependency 'rspec', '~> 3.1'
|
21
|
+
end
|
@@ -0,0 +1,76 @@
|
|
1
|
+
module Attire
|
2
|
+
class AttrInit
|
3
|
+
def self.apply(*args)
|
4
|
+
new(*args).apply
|
5
|
+
end
|
6
|
+
|
7
|
+
def initialize(klass, args, block)
|
8
|
+
@klass, @args, @block = klass, args, block
|
9
|
+
end
|
10
|
+
|
11
|
+
def apply
|
12
|
+
type_check
|
13
|
+
init = method(:init)
|
14
|
+
klass.send(:define_method, :initialize) { |*values| init.call(self, values) }
|
15
|
+
define_getters
|
16
|
+
end
|
17
|
+
|
18
|
+
private
|
19
|
+
|
20
|
+
attr_reader :klass, :args, :block, :instance, :values
|
21
|
+
|
22
|
+
def init(instance, values)
|
23
|
+
@instance, @values = instance, values
|
24
|
+
arity_check
|
25
|
+
set_variables
|
26
|
+
instance.instance_eval(&block) if block
|
27
|
+
end
|
28
|
+
|
29
|
+
def set_variables
|
30
|
+
args.zip(values).each do |arg, value|
|
31
|
+
next set_variable(arg, value) if arg.is_a?(Symbol)
|
32
|
+
value = {} if value.nil?
|
33
|
+
hash_check(value)
|
34
|
+
arg.each { |k, v| set_variable(k, value[k] || v) }
|
35
|
+
end
|
36
|
+
end
|
37
|
+
|
38
|
+
def define_getters
|
39
|
+
getter_names.each do |arg|
|
40
|
+
klass.send(:define_method, arg) { instance_variable_get("@#{arg}") }
|
41
|
+
klass.send(:private, arg)
|
42
|
+
end
|
43
|
+
end
|
44
|
+
|
45
|
+
def type_check
|
46
|
+
return if args.all? {|a| [Symbol, Hash].include?(a.class) }
|
47
|
+
fail ArgumentError, 'Must be Symbol or Hash.'
|
48
|
+
end
|
49
|
+
|
50
|
+
def arity_check
|
51
|
+
return if arity_range.include?(values.length)
|
52
|
+
fail ArgumentError, "wrong number of arguments (#{values.length} for #{arity_range})"
|
53
|
+
end
|
54
|
+
|
55
|
+
def hash_check(value)
|
56
|
+
return if value.is_a?(Hash)
|
57
|
+
fail ArgumentError, "#{value} should be Hash."
|
58
|
+
end
|
59
|
+
|
60
|
+
def min_arity
|
61
|
+
args.last.is_a?(Hash) ? args.length - 1 : args.length
|
62
|
+
end
|
63
|
+
|
64
|
+
def arity_range
|
65
|
+
@arity_range ||= (min_arity..args.length)
|
66
|
+
end
|
67
|
+
|
68
|
+
def set_variable(name, value)
|
69
|
+
instance.instance_variable_set("@#{name}", value)
|
70
|
+
end
|
71
|
+
|
72
|
+
def getter_names
|
73
|
+
args.map { |arg| arg.respond_to?(:keys) ? arg.keys : arg }.flatten
|
74
|
+
end
|
75
|
+
end
|
76
|
+
end
|
data/lib/attire.rb
ADDED
@@ -0,0 +1,37 @@
|
|
1
|
+
require 'attire/version'
|
2
|
+
require 'attire/attr_init'
|
3
|
+
|
4
|
+
module Attire
|
5
|
+
def attr_query(*names)
|
6
|
+
names.each do |name|
|
7
|
+
name = name.to_s
|
8
|
+
fail ArgumentError, "`#{name}?`, not `#{name}`." unless name.end_with?("?")
|
9
|
+
define_method(name) { !!send(name.chop) }
|
10
|
+
end
|
11
|
+
end
|
12
|
+
|
13
|
+
def attr_init(*args, &b)
|
14
|
+
AttrInit.apply(self, args, b)
|
15
|
+
end
|
16
|
+
|
17
|
+
def attr_method(verb, *args, &b)
|
18
|
+
define_singleton_method(verb) { |*a| new(*a).send(verb) }
|
19
|
+
attr_init(*args, &b)
|
20
|
+
end
|
21
|
+
|
22
|
+
def fattr_init(*args, &b)
|
23
|
+
attr_init(*args) do
|
24
|
+
instance_eval(&b) if b
|
25
|
+
self.freeze
|
26
|
+
end
|
27
|
+
end
|
28
|
+
|
29
|
+
def fattr_method(verb, *args, &b)
|
30
|
+
define_singleton_method(verb) { |*a| new(*a).send(verb) }
|
31
|
+
fattr_init(*args, &b)
|
32
|
+
end
|
33
|
+
end
|
34
|
+
|
35
|
+
class Module
|
36
|
+
include Attire
|
37
|
+
end
|
@@ -0,0 +1,47 @@
|
|
1
|
+
describe '#attr_init' do
|
2
|
+
let(:instance) { define_class(inside).new(1, 2, option1: 54) }
|
3
|
+
let(:inside) do
|
4
|
+
Proc.new { attr_init :argument1, :argument2, option1: 23, option2: 'default', option3: nil }
|
5
|
+
end
|
6
|
+
|
7
|
+
it 'assigns arguments to private methods' do
|
8
|
+
expect(instance.send(:argument1)).to eq(1)
|
9
|
+
expect(instance.send(:argument2)).to eq(2)
|
10
|
+
expect(instance.send(:option1)).to eq(54)
|
11
|
+
expect(instance.send(:option2)).to eq('default')
|
12
|
+
expect(instance.send(:option3)).to eq(nil)
|
13
|
+
end
|
14
|
+
|
15
|
+
context 'with optional hash missing' do
|
16
|
+
let(:instance) { define_class(inside).new(1, 2) }
|
17
|
+
|
18
|
+
it 'still assigns arguments to private methods using default' do
|
19
|
+
expect(instance.send(:option1)).to eq(23)
|
20
|
+
end
|
21
|
+
end
|
22
|
+
|
23
|
+
context 'with input of wrong type' do
|
24
|
+
let(:inside) { Proc.new { attr_init 'Strings bad' } }
|
25
|
+
it { expect { define_class(inside) }.to raise_error(ArgumentError) }
|
26
|
+
end
|
27
|
+
|
28
|
+
context 'with too many arguments' do
|
29
|
+
let(:instance) { define_class(inside).new(1, 2, {}, 4, 5) }
|
30
|
+
it { expect { instance }.to raise_error(ArgumentError) }
|
31
|
+
end
|
32
|
+
|
33
|
+
context 'with too few arguments' do
|
34
|
+
let(:instance) { define_class(inside).new(1) }
|
35
|
+
it { expect { instance }.to raise_error(ArgumentError) }
|
36
|
+
end
|
37
|
+
|
38
|
+
context 'with block' do
|
39
|
+
let(:instance) { define_class(inside).new(1) }
|
40
|
+
let(:inside) { Proc.new { attr_init :foo do @foo = 5 end } }
|
41
|
+
|
42
|
+
it 'calls block' do
|
43
|
+
expect(instance.send(:foo)).to eq(5)
|
44
|
+
end
|
45
|
+
end
|
46
|
+
end
|
47
|
+
|
@@ -0,0 +1,16 @@
|
|
1
|
+
describe '#attr_method' do
|
2
|
+
let(:klass) do
|
3
|
+
klass = Class.new do
|
4
|
+
attr_method :select, :child, toy: 5
|
5
|
+
|
6
|
+
def select
|
7
|
+
toy * child
|
8
|
+
end
|
9
|
+
end
|
10
|
+
end
|
11
|
+
|
12
|
+
it 'creates a class method that initializes and calls instance method' do
|
13
|
+
expect(klass.select(3)).to eq(15)
|
14
|
+
expect(klass.select(3, toy: 2)).to eq(6)
|
15
|
+
end
|
16
|
+
end
|
@@ -0,0 +1,19 @@
|
|
1
|
+
describe '#attr_query' do
|
2
|
+
context 'with correct arguments' do
|
3
|
+
let(:instance) { define_class(inside).new }
|
4
|
+
let(:inside) do
|
5
|
+
Proc.new { attr_accessor :foo; attr_query :foo? }
|
6
|
+
end
|
7
|
+
|
8
|
+
it 'creates a query method' do
|
9
|
+
expect(instance.foo?).to be(false)
|
10
|
+
instance.foo = 'rah'
|
11
|
+
expect(instance.foo?).to be(true)
|
12
|
+
end
|
13
|
+
end
|
14
|
+
|
15
|
+
context 'without a tailing questionmark' do
|
16
|
+
let(:inside) { Proc.new { attr_query :bar } }
|
17
|
+
it { expect { define_class(inside) }.to raise_error }
|
18
|
+
end
|
19
|
+
end
|
data/spec/fattr_spec.rb
ADDED
@@ -0,0 +1,17 @@
|
|
1
|
+
describe '#fattr_init' do
|
2
|
+
let(:instance) { define_class(inside).new(5) }
|
3
|
+
let(:inside) { Proc.new { fattr_init :chick } }
|
4
|
+
|
5
|
+
it 'freezes class after initializing' do
|
6
|
+
expect(instance.send(:chick)).to eq(5)
|
7
|
+
expect { instance.instance_variable_set('@chick', 12) }.to raise_error
|
8
|
+
end
|
9
|
+
|
10
|
+
context 'with block' do
|
11
|
+
let(:inside) { Proc.new { fattr_init :chick do @chick = 15 end } }
|
12
|
+
it 'still evaluates block' do
|
13
|
+
expect(instance.send(:chick)).to eq(15)
|
14
|
+
end
|
15
|
+
end
|
16
|
+
end
|
17
|
+
|
data/spec/spec_helper.rb
ADDED
metadata
ADDED
@@ -0,0 +1,78 @@
|
|
1
|
+
--- !ruby/object:Gem::Specification
|
2
|
+
name: attire
|
3
|
+
version: !ruby/object:Gem::Version
|
4
|
+
version: 0.0.1
|
5
|
+
platform: ruby
|
6
|
+
authors:
|
7
|
+
- Max White
|
8
|
+
autorequire:
|
9
|
+
bindir: bin
|
10
|
+
cert_chain: []
|
11
|
+
date: 2015-01-08 00:00:00.000000000 Z
|
12
|
+
dependencies:
|
13
|
+
- !ruby/object:Gem::Dependency
|
14
|
+
name: rspec
|
15
|
+
requirement: !ruby/object:Gem::Requirement
|
16
|
+
requirements:
|
17
|
+
- - "~>"
|
18
|
+
- !ruby/object:Gem::Version
|
19
|
+
version: '3.1'
|
20
|
+
type: :development
|
21
|
+
prerelease: false
|
22
|
+
version_requirements: !ruby/object:Gem::Requirement
|
23
|
+
requirements:
|
24
|
+
- - "~>"
|
25
|
+
- !ruby/object:Gem::Version
|
26
|
+
version: '3.1'
|
27
|
+
description:
|
28
|
+
email:
|
29
|
+
- mushishi78@gmail.com
|
30
|
+
executables: []
|
31
|
+
extensions: []
|
32
|
+
extra_rdoc_files: []
|
33
|
+
files:
|
34
|
+
- ".gitignore"
|
35
|
+
- ".rspec"
|
36
|
+
- Gemfile
|
37
|
+
- LICENSE.txt
|
38
|
+
- README.md
|
39
|
+
- Rakefile
|
40
|
+
- attire.gemspec
|
41
|
+
- lib/attire.rb
|
42
|
+
- lib/attire/attr_init.rb
|
43
|
+
- lib/attire/version.rb
|
44
|
+
- spec/attr_init_spec.rb
|
45
|
+
- spec/attr_method_spec.rb
|
46
|
+
- spec/attr_query_spec.rb
|
47
|
+
- spec/fattr_spec.rb
|
48
|
+
- spec/spec_helper.rb
|
49
|
+
homepage: https://github.com/mushishi78/attire
|
50
|
+
licenses:
|
51
|
+
- MIT
|
52
|
+
metadata: {}
|
53
|
+
post_install_message:
|
54
|
+
rdoc_options: []
|
55
|
+
require_paths:
|
56
|
+
- lib
|
57
|
+
required_ruby_version: !ruby/object:Gem::Requirement
|
58
|
+
requirements:
|
59
|
+
- - ">="
|
60
|
+
- !ruby/object:Gem::Version
|
61
|
+
version: '0'
|
62
|
+
required_rubygems_version: !ruby/object:Gem::Requirement
|
63
|
+
requirements:
|
64
|
+
- - ">="
|
65
|
+
- !ruby/object:Gem::Version
|
66
|
+
version: '0'
|
67
|
+
requirements: []
|
68
|
+
rubyforge_project:
|
69
|
+
rubygems_version: 2.2.2
|
70
|
+
signing_key:
|
71
|
+
specification_version: 4
|
72
|
+
summary: Convenience methods to remove some boiler plate in defining classes.
|
73
|
+
test_files:
|
74
|
+
- spec/attr_init_spec.rb
|
75
|
+
- spec/attr_method_spec.rb
|
76
|
+
- spec/attr_query_spec.rb
|
77
|
+
- spec/fattr_spec.rb
|
78
|
+
- spec/spec_helper.rb
|