attire 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: 53a76fd77e586cecce6fea04de34aea4225090d3
4
+ data.tar.gz: a8e7d3b291bc0adc3845a7d893d4bca319256254
5
+ SHA512:
6
+ metadata.gz: 66e94421748b69661c57680844088fee92e8474a252b7130e5c6e9918f8f6b16f015746c3b1ce51fa851ac6f561cfd53055ef74efd4e3bbfa27106f7affa8931
7
+ data.tar.gz: 0efe0cdb17fc8e4903746b983ef0b039b13db70ebb783bbcb732599c9270b43dd604cb14131b9b33292368e1cfc71a48351ce245b84f91c3be209dadb90534f0
data/.gitignore ADDED
@@ -0,0 +1,15 @@
1
+ /.bundle/
2
+ /.yardoc
3
+ /Gemfile.lock
4
+ /_yardoc/
5
+ /coverage/
6
+ /doc/
7
+ /pkg/
8
+ /spec/reports/
9
+ /tmp/
10
+ *.bundle
11
+ *.so
12
+ *.o
13
+ *.a
14
+ mkmf.log
15
+ *.gem
data/.rspec ADDED
@@ -0,0 +1 @@
1
+ --require spec_helper
data/Gemfile ADDED
@@ -0,0 +1,4 @@
1
+ source 'https://rubygems.org'
2
+
3
+ # Specify your gem's dependencies in attire.gemspec
4
+ gemspec
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
@@ -0,0 +1,2 @@
1
+ require "bundler/gem_tasks"
2
+
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
@@ -0,0 +1,3 @@
1
+ module Attire
2
+ VERSION = "0.0.1"
3
+ 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
@@ -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
+
@@ -0,0 +1,5 @@
1
+ require 'attire'
2
+
3
+ def define_class(proc)
4
+ Class.new { instance_eval(&proc) }
5
+ end
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