attire 1.1.0 → 2.0.0
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +4 -4
- data/README.md +24 -60
- data/lib/attire.rb +2 -24
- data/lib/attire/initializer.rb +49 -0
- data/lib/attire/method_object.rb +12 -0
- metadata +5 -9
- data/lib/attire/attr_init.rb +0 -24
- data/lib/attire/attr_init/arguments.rb +0 -55
- data/lib/attire/attr_init/arguments_checker.rb +0 -66
- data/lib/attire/attr_init/instance_methods.rb +0 -37
- data/lib/attire/attr_init/values_matcher.rb +0 -42
- data/lib/attire/core_ext/duplicable.rb +0 -48
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA1:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: e47510ce77fba871c171411ee89810f79bbe8231
|
4
|
+
data.tar.gz: 1ed7fbacff39b565ce85235d25e376941006021f
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: 08e1829aecf20e6405dbcc3f2189a097f13cf8368de2770095857fd6968d02491106f0c52fba094b9d6c5a8cd50f8f4f885c11d1391440f8acd92fa5c2590d5c
|
7
|
+
data.tar.gz: 1c7084866f5e17483c32ed4fa9a20810d57b1aa5904909dd2e2712e369d85a9821e6addbc3b6dea03717c58c4e8d6215ca6d39e0a589ed6b17b1cc471a0a1203
|
data/README.md
CHANGED
@@ -3,87 +3,53 @@
|
|
3
3
|
[![Build Status](https://travis-ci.org/mushishi78/attire.svg?branch=master)](https://travis-ci.org/mushishi78/attire)
|
4
4
|
[![Gem Version](https://badge.fury.io/rb/attire.svg)](http://badge.fury.io/rb/attire)
|
5
5
|
|
6
|
-
|
6
|
+
Mixins to remove some boiler plate in defining classes.
|
7
7
|
|
8
|
-
|
8
|
+
**N.B. This is the README for version 2.0.0. Major changes have been made. For previous versions please consult the tagged commit.**
|
9
9
|
|
10
|
-
|
11
|
-
attr_init :foo, :bar, fizz: 15, pop: nil
|
12
|
-
```
|
10
|
+
## Initializer
|
13
11
|
|
14
|
-
|
12
|
+
`Attire::Initializer` extends a subsequent initialize method so that all it's parameters are assigned to instance variables and they can all be reached from private getters. For example:
|
15
13
|
|
16
14
|
``` ruby
|
17
|
-
|
18
|
-
@foo = foo
|
19
|
-
@bar = bar
|
20
|
-
@fizz = opts[:fizz]
|
21
|
-
@pop = opts[:pop]
|
22
|
-
end
|
15
|
+
require 'attire'
|
23
16
|
|
24
|
-
|
17
|
+
class MyClass
|
18
|
+
extend Attire::Initializer
|
25
19
|
|
26
|
-
|
20
|
+
def initialize(foo:, bar: 24)
|
21
|
+
@bar = bar * 2
|
22
|
+
end
|
27
23
|
|
28
|
-
def
|
29
|
-
|
24
|
+
def result
|
25
|
+
foo + bar
|
26
|
+
end
|
30
27
|
end
|
31
|
-
```
|
32
28
|
|
33
|
-
|
34
|
-
|
35
|
-
``` ruby
|
36
|
-
attr_init :'opts = {}', :'*args', :'&block'
|
29
|
+
my_instance = MyClass.new(foo: 50)
|
30
|
+
my_instance.result # 98
|
37
31
|
```
|
38
32
|
|
39
|
-
|
40
|
-
|
41
|
-
``` ruby
|
42
|
-
attr_init :foo do
|
43
|
-
@foo = foo ** 2
|
44
|
-
end
|
45
|
-
```
|
46
|
-
|
47
|
-
## attr_method
|
48
|
-
|
49
|
-
``` ruby
|
50
|
-
attr_method :select, :bar
|
51
|
-
```
|
33
|
+
## MethodObject
|
52
34
|
|
53
|
-
|
54
|
-
|
55
|
-
``` ruby
|
56
|
-
def self.select(*args, &block)
|
57
|
-
new(*args, &block).select
|
58
|
-
end
|
59
|
-
|
60
|
-
attr_init :bar
|
61
|
-
```
|
62
|
-
|
63
|
-
This is useful for method objects/use cases:
|
35
|
+
`Attire::MethodObject` does the same as `Attire::Initializer` but it also adds a singleton method that creates an instance and then calls an instance method. This is useful for objects that are designed to do one task. For example:
|
64
36
|
|
65
37
|
``` ruby
|
66
38
|
class CheeseSpreader
|
67
|
-
|
39
|
+
extend Attire::MethodObject.new(:spread)
|
40
|
+
|
41
|
+
def initialize(cheese, cracker: Jacobs.new)
|
42
|
+
end
|
68
43
|
|
69
44
|
def spread
|
70
|
-
cracker = crackers_class.new
|
71
45
|
cracker.spreads << cheese
|
72
46
|
cracker
|
73
47
|
end
|
74
48
|
end
|
75
49
|
|
76
|
-
CheeseSpreader.spread(roquefort)
|
50
|
+
CheeseSpreader.spread(:roquefort)
|
77
51
|
```
|
78
52
|
|
79
|
-
## attr_query
|
80
|
-
|
81
|
-
``` ruby
|
82
|
-
attr_query :foo?
|
83
|
-
```
|
84
|
-
|
85
|
-
Defines query `#foo?`, which is true if `foo` is truthy.
|
86
|
-
|
87
53
|
## Installation
|
88
54
|
|
89
55
|
Add to Gemfile:
|
@@ -92,11 +58,9 @@ Add to Gemfile:
|
|
92
58
|
gem 'attire'
|
93
59
|
```
|
94
60
|
|
95
|
-
|
61
|
+
## Inspirations
|
96
62
|
|
97
|
-
|
98
|
-
require 'attire'
|
99
|
-
```
|
63
|
+
* [attr_extras](https://github.com/barsoom/attr_extras).
|
100
64
|
|
101
65
|
## Contributing
|
102
66
|
|
data/lib/attire.rb
CHANGED
@@ -1,24 +1,2 @@
|
|
1
|
-
require 'attire/
|
2
|
-
|
3
|
-
module Attire
|
4
|
-
def attr_query(*names)
|
5
|
-
names.each do |name|
|
6
|
-
name = name.to_s
|
7
|
-
fail ArgumentError, "`#{name}?`, not `#{name}`." unless name.end_with?('?')
|
8
|
-
define_method(name) { !!send(name.chop) }
|
9
|
-
end
|
10
|
-
end
|
11
|
-
|
12
|
-
def attr_init(*args, &block)
|
13
|
-
include AttrInit.new(args, block)
|
14
|
-
end
|
15
|
-
|
16
|
-
def attr_method(verb, *args, &block)
|
17
|
-
define_singleton_method(verb) { |*a, &b| new(*a, &b).send(verb) }
|
18
|
-
attr_init(*args, &block)
|
19
|
-
end
|
20
|
-
end
|
21
|
-
|
22
|
-
class Module
|
23
|
-
include Attire
|
24
|
-
end
|
1
|
+
require 'attire/initializer'
|
2
|
+
require 'attire/method_object'
|
@@ -0,0 +1,49 @@
|
|
1
|
+
module Attire
|
2
|
+
module Initializer
|
3
|
+
def self.extended(base)
|
4
|
+
base.class.instance_variable_set(:@_added, false)
|
5
|
+
end
|
6
|
+
|
7
|
+
def method_added(method_name)
|
8
|
+
return super unless method_name == :initialize && !added?
|
9
|
+
method = instance_method(:initialize)
|
10
|
+
names = method.parameters.map(&:last)
|
11
|
+
add_initialize(method, names)
|
12
|
+
add_getters(names)
|
13
|
+
self.added = true
|
14
|
+
super
|
15
|
+
end
|
16
|
+
|
17
|
+
private
|
18
|
+
|
19
|
+
def added?
|
20
|
+
self.class.instance_variable_get(:@_added)
|
21
|
+
end
|
22
|
+
|
23
|
+
def added=(added)
|
24
|
+
self.class.instance_variable_set(:@_added, added)
|
25
|
+
end
|
26
|
+
|
27
|
+
def add_initialize(method, names)
|
28
|
+
initialize_line = initialize_line(method)
|
29
|
+
set_ivars = names.map { |name| "@#{name} = #{name}\n" }.join
|
30
|
+
|
31
|
+
initializer = Module.new do
|
32
|
+
class_eval "#{initialize_line}#{set_ivars}super\nend\n"
|
33
|
+
end
|
34
|
+
prepend initializer
|
35
|
+
end
|
36
|
+
|
37
|
+
def initialize_line(method)
|
38
|
+
file, endline = *method.source_location
|
39
|
+
File.readlines(file)[0..endline].reverse.each do |line|
|
40
|
+
return line if line.include?('initialize')
|
41
|
+
end
|
42
|
+
end
|
43
|
+
|
44
|
+
def add_getters(names)
|
45
|
+
attr_reader(*names)
|
46
|
+
private(*names)
|
47
|
+
end
|
48
|
+
end
|
49
|
+
end
|
@@ -0,0 +1,12 @@
|
|
1
|
+
require_relative 'initializer'
|
2
|
+
|
3
|
+
module Attire
|
4
|
+
module MethodObject
|
5
|
+
def self.new(verb)
|
6
|
+
Module.new do
|
7
|
+
define_singleton_method(:extended) { |base| base.extend Initializer }
|
8
|
+
define_method(verb) { |*a, **k, &b| new(*a, **k, &b).send(verb) }
|
9
|
+
end
|
10
|
+
end
|
11
|
+
end
|
12
|
+
end
|
metadata
CHANGED
@@ -1,14 +1,14 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: attire
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version:
|
4
|
+
version: 2.0.0
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- Max White
|
8
8
|
autorequire:
|
9
9
|
bindir: bin
|
10
10
|
cert_chain: []
|
11
|
-
date: 2015-
|
11
|
+
date: 2015-05-06 00:00:00.000000000 Z
|
12
12
|
dependencies:
|
13
13
|
- !ruby/object:Gem::Dependency
|
14
14
|
name: rspec
|
@@ -39,12 +39,8 @@ files:
|
|
39
39
|
- LICENSE.txt
|
40
40
|
- README.md
|
41
41
|
- lib/attire.rb
|
42
|
-
- lib/attire/
|
43
|
-
- lib/attire/
|
44
|
-
- lib/attire/attr_init/arguments_checker.rb
|
45
|
-
- lib/attire/attr_init/instance_methods.rb
|
46
|
-
- lib/attire/attr_init/values_matcher.rb
|
47
|
-
- lib/attire/core_ext/duplicable.rb
|
42
|
+
- lib/attire/initializer.rb
|
43
|
+
- lib/attire/method_object.rb
|
48
44
|
homepage: https://github.com/mushishi78/attire
|
49
45
|
licenses:
|
50
46
|
- MIT
|
@@ -68,5 +64,5 @@ rubyforge_project:
|
|
68
64
|
rubygems_version: 2.2.2
|
69
65
|
signing_key:
|
70
66
|
specification_version: 4
|
71
|
-
summary:
|
67
|
+
summary: Mixins to remove some boiler plate in defining classes.
|
72
68
|
test_files: []
|
data/lib/attire/attr_init.rb
DELETED
@@ -1,24 +0,0 @@
|
|
1
|
-
require 'attire/attr_init/arguments_checker'
|
2
|
-
require 'attire/attr_init/arguments'
|
3
|
-
require 'attire/attr_init/values_matcher'
|
4
|
-
require 'attire/attr_init/instance_methods'
|
5
|
-
|
6
|
-
module Attire
|
7
|
-
module AttrInit
|
8
|
-
def self.new(args, after_block)
|
9
|
-
ArgumentsChecker.check(args)
|
10
|
-
arguments = Arguments.new(args)
|
11
|
-
values_matcher = ValuesMatcher.new(arguments)
|
12
|
-
getters, defaults = arguments.getters, arguments.defaults
|
13
|
-
|
14
|
-
Module.new do
|
15
|
-
include InstanceMethods
|
16
|
-
define_method(:_arguments) { arguments }
|
17
|
-
define_method(:_after_block) { after_block }
|
18
|
-
define_method(:_values_matcher) { values_matcher }
|
19
|
-
getters.each { |n| define_method(n) { _get_attribute(n, defaults[n]) } }
|
20
|
-
private(*getters)
|
21
|
-
end
|
22
|
-
end
|
23
|
-
end
|
24
|
-
end
|
@@ -1,55 +0,0 @@
|
|
1
|
-
require 'attire/core_ext/duplicable'
|
2
|
-
|
3
|
-
module Attire
|
4
|
-
module AttrInit
|
5
|
-
class Arguments
|
6
|
-
def initialize(arguments)
|
7
|
-
@block = last_argument_with_prefix(arguments, '&')
|
8
|
-
@splat = last_argument_with_prefix(arguments, '*')
|
9
|
-
@names, @defaults = [], {}
|
10
|
-
arguments.each { |arg| extract_argument(arg) }
|
11
|
-
@arity_range = (min_arity..max_arity)
|
12
|
-
@getters = (names.flatten + [splat, block]).compact
|
13
|
-
end
|
14
|
-
|
15
|
-
attr_reader :names, :splat, :block, :defaults, :arity_range, :getters
|
16
|
-
|
17
|
-
private
|
18
|
-
|
19
|
-
def last_argument_with_prefix(arguments, prefix)
|
20
|
-
return unless arguments.last.to_s.start_with?(prefix)
|
21
|
-
arguments.pop.to_s[1..-1]
|
22
|
-
end
|
23
|
-
|
24
|
-
def extract_argument(arg)
|
25
|
-
return extract_hash(arg) if arg.is_a?(Hash)
|
26
|
-
return extract_optional(arg) if arg.to_s.include?('=')
|
27
|
-
extract_required(arg)
|
28
|
-
end
|
29
|
-
|
30
|
-
def extract_hash(hash)
|
31
|
-
names << hash.keys
|
32
|
-
defaults.merge!(hash)
|
33
|
-
end
|
34
|
-
|
35
|
-
def extract_optional(arg)
|
36
|
-
name, default = arg.to_s.split('=').map(&:strip)
|
37
|
-
names << name
|
38
|
-
defaults[name] = eval(default)
|
39
|
-
end
|
40
|
-
|
41
|
-
def extract_required(arg)
|
42
|
-
names << arg
|
43
|
-
@min_arity = names.length
|
44
|
-
end
|
45
|
-
|
46
|
-
def min_arity
|
47
|
-
@min_arity ||= 0
|
48
|
-
end
|
49
|
-
|
50
|
-
def max_arity
|
51
|
-
splat ? Float::INFINITY : names.length
|
52
|
-
end
|
53
|
-
end
|
54
|
-
end
|
55
|
-
end
|
@@ -1,66 +0,0 @@
|
|
1
|
-
module Attire
|
2
|
-
module AttrInit
|
3
|
-
class ArgumentsChecker
|
4
|
-
def self.check(arguments)
|
5
|
-
new.check(arguments)
|
6
|
-
end
|
7
|
-
|
8
|
-
def check(arguments)
|
9
|
-
arguments.reverse.each_with_index do |arg, i|
|
10
|
-
@arg, @i = arg, i
|
11
|
-
type_check
|
12
|
-
block_check
|
13
|
-
splat_check
|
14
|
-
required_after_optional_check
|
15
|
-
end
|
16
|
-
end
|
17
|
-
|
18
|
-
private
|
19
|
-
|
20
|
-
attr_reader :arg, :i
|
21
|
-
|
22
|
-
TYPES = [Symbol, String, Hash]
|
23
|
-
|
24
|
-
def type_check
|
25
|
-
return if TYPES.include?(arg.class)
|
26
|
-
fail ArgumentError, 'Must be Symbol, String or Hash.'
|
27
|
-
end
|
28
|
-
|
29
|
-
def block_check
|
30
|
-
return unless block?
|
31
|
-
fail ArgumentError, 'Block arguments must be last' unless i == 0
|
32
|
-
@has_block = true
|
33
|
-
end
|
34
|
-
|
35
|
-
def splat_check
|
36
|
-
return unless splat?
|
37
|
-
return if i == (@has_block ? 1 : 0)
|
38
|
-
msg = 'Splat arguments must come after required and optional arguments'
|
39
|
-
fail ArgumentError, msg
|
40
|
-
end
|
41
|
-
|
42
|
-
def required_after_optional_check
|
43
|
-
return if block? || splat? || hash?
|
44
|
-
return @has_requireds = true unless optional?
|
45
|
-
return unless @has_requireds
|
46
|
-
fail ArgumentError, 'Required arguments must come before optional'
|
47
|
-
end
|
48
|
-
|
49
|
-
def block?
|
50
|
-
arg.to_s.start_with?('&')
|
51
|
-
end
|
52
|
-
|
53
|
-
def splat?
|
54
|
-
arg.to_s.start_with?('*')
|
55
|
-
end
|
56
|
-
|
57
|
-
def hash?
|
58
|
-
arg.is_a?(Hash)
|
59
|
-
end
|
60
|
-
|
61
|
-
def optional?
|
62
|
-
arg.to_s.include?('=')
|
63
|
-
end
|
64
|
-
end
|
65
|
-
end
|
66
|
-
end
|
@@ -1,37 +0,0 @@
|
|
1
|
-
module Attire
|
2
|
-
module AttrInit
|
3
|
-
module InstanceMethods
|
4
|
-
def initialize(*values, &value_block)
|
5
|
-
_arity_check(values)
|
6
|
-
_match(values, value_block).each { |k, v| instance_variable_set("@#{k}", v) }
|
7
|
-
instance_exec(&_after_block) if _after_block
|
8
|
-
end
|
9
|
-
|
10
|
-
private
|
11
|
-
|
12
|
-
def _arity_check(values)
|
13
|
-
return if _arity_range.include?(values.length)
|
14
|
-
msg = "wrong number of arguments (#{values.length} for #{_arity_range})"
|
15
|
-
fail ArgumentError, msg
|
16
|
-
end
|
17
|
-
|
18
|
-
def _get_attribute(name, default)
|
19
|
-
value = instance_variable_get("@#{name}")
|
20
|
-
return value unless value.nil? && !default.nil?
|
21
|
-
instance_variable_set("@#{name}", _duped_default(default))
|
22
|
-
end
|
23
|
-
|
24
|
-
def _duped_default(default)
|
25
|
-
default.duplicable? ? default.dup : default
|
26
|
-
end
|
27
|
-
|
28
|
-
def _match(*args)
|
29
|
-
_values_matcher.match(*args)
|
30
|
-
end
|
31
|
-
|
32
|
-
def _arity_range
|
33
|
-
_arguments.arity_range
|
34
|
-
end
|
35
|
-
end
|
36
|
-
end
|
37
|
-
end
|
@@ -1,42 +0,0 @@
|
|
1
|
-
require 'forwardable'
|
2
|
-
|
3
|
-
module Attire
|
4
|
-
module AttrInit
|
5
|
-
class ValuesMatcher
|
6
|
-
extend Forwardable
|
7
|
-
|
8
|
-
def initialize(arguments)
|
9
|
-
@arguments = arguments
|
10
|
-
end
|
11
|
-
|
12
|
-
def match(values, value_block)
|
13
|
-
@matched = {}
|
14
|
-
names.zip(values) { |n, v| n.is_a?(Array) ? set_from_hash(v) : set(n, v) }
|
15
|
-
set_splat(values)
|
16
|
-
set_block(value_block)
|
17
|
-
matched
|
18
|
-
end
|
19
|
-
|
20
|
-
private
|
21
|
-
|
22
|
-
attr_reader :arguments, :matched
|
23
|
-
def_delegators :arguments, :names, :splat, :block
|
24
|
-
|
25
|
-
def set(name, value)
|
26
|
-
matched[name] = value
|
27
|
-
end
|
28
|
-
|
29
|
-
def set_from_hash(values)
|
30
|
-
matched.merge!(values || {})
|
31
|
-
end
|
32
|
-
|
33
|
-
def set_splat(values)
|
34
|
-
matched[splat] = values[names.length..-1] || [] if splat
|
35
|
-
end
|
36
|
-
|
37
|
-
def set_block(value_block)
|
38
|
-
matched[block] = value_block if block
|
39
|
-
end
|
40
|
-
end
|
41
|
-
end
|
42
|
-
end
|
@@ -1,48 +0,0 @@
|
|
1
|
-
class Object
|
2
|
-
def duplicable?
|
3
|
-
true
|
4
|
-
end
|
5
|
-
end
|
6
|
-
|
7
|
-
class NilClass
|
8
|
-
def duplicable?
|
9
|
-
false
|
10
|
-
end
|
11
|
-
end
|
12
|
-
|
13
|
-
class FalseClass
|
14
|
-
def duplicable?
|
15
|
-
false
|
16
|
-
end
|
17
|
-
end
|
18
|
-
|
19
|
-
class TrueClass
|
20
|
-
def duplicable?
|
21
|
-
false
|
22
|
-
end
|
23
|
-
end
|
24
|
-
|
25
|
-
class Symbol
|
26
|
-
def duplicable?
|
27
|
-
false
|
28
|
-
end
|
29
|
-
end
|
30
|
-
|
31
|
-
class Numeric
|
32
|
-
def duplicable?
|
33
|
-
false
|
34
|
-
end
|
35
|
-
end
|
36
|
-
|
37
|
-
require 'bigdecimal'
|
38
|
-
class BigDecimal
|
39
|
-
def duplicable?
|
40
|
-
true
|
41
|
-
end
|
42
|
-
end
|
43
|
-
|
44
|
-
class Method
|
45
|
-
def duplicable?
|
46
|
-
false
|
47
|
-
end
|
48
|
-
end
|