attr_extras 2.0.0 → 2.0.1

Sign up to get free protection for your applications and to get access to all the features.
checksums.yaml CHANGED
@@ -1,7 +1,15 @@
1
1
  ---
2
- SHA1:
3
- metadata.gz: d86d1ce2f78e900f9dd4c9792df912f94ed6973f
4
- data.tar.gz: 41d756b1d2b14f6f66ce1b8eb7e0127b4839d9b3
2
+ !binary "U0hBMQ==":
3
+ metadata.gz: !binary |-
4
+ ZjhmZWFmMjMzZDY4YmFmYWYyZDFjYjIyNzU1ZGJlYmFlMmYxNGEwMQ==
5
+ data.tar.gz: !binary |-
6
+ ZTI2NzNiMmEwZDRlNjM3ZjM5ODZiMDcyYTU0YWNmZmE0YTEyODNkOQ==
5
7
  SHA512:
6
- metadata.gz: 5f4c36e7c979590d62a5a8fccc7f450eda9ec9cb1cd21466b8634c995b9bd2921bf1edbab33a09e85a7136f618d4e99ace7a3d8c70701420d216eb3b67695710
7
- data.tar.gz: 4eb369edb50ea00c9bfdb36f78053cfdbabecd9ce826050fa8d588ede9942e72ebefa064ed2b05f10edaf4b4f58be2e9cf347919892c5d74dbee34312a6f45df
8
+ metadata.gz: !binary |-
9
+ MzE5YmQ3ZjBmYjY5MzY4OTczY2VkNzNiMDVkNDMxNzUyYmFkMzFlMmJmNTA5
10
+ MjYxMjJkZDE1YmJkYzdlNjM2NzIwOWZhMjc2OTUxYjBjNmNjN2RiZDEwODIy
11
+ YWVmZGE0MWZjOGY3NWFmNGNkODg5Mzk0NDllMGMzOWJkN2Q3ZGQ=
12
+ data.tar.gz: !binary |-
13
+ M2NkMWU1NjAxYTNkMjczY2VkYWEwMDMyODM5OGY0YjcxNDU4YjRlZWRkZmYy
14
+ MjZlYzM3NjgyOWVhMjg2NjMyNmJiZTk2OTdlNjk4ODFkZTU4Y2E2ZjFkNWY5
15
+ OTgxY2EwYjk0Nzc4MWNmYTc4MTBiZTM0ODQwNzllZTE5ZGY2OGI=
data/README.md CHANGED
@@ -28,6 +28,10 @@ end
28
28
 
29
29
  This nicely complements Ruby's built-in `attr_accessor`, `attr_reader` and `attr_writer`.
30
30
 
31
+ Supports positional arguments as well as optional and required hash arguments.
32
+
33
+ Also provides conveniences for creating value objects, method objects and query methods.
34
+
31
35
 
32
36
  ## Usage
33
37
 
@@ -79,83 +83,59 @@ The `attr_initialize` notation notation for hash arguments is also supported: `v
79
83
 
80
84
  ### `method_object :fooable?, :foo`<br>
81
85
 
82
- Defines a `.fooable?` class method that takes one argument (`:foo`) and delegates to an instance method that can access `foo` as a private reader, useful for [method objects](http://refactoring.com/catalog/replaceMethodWithMethodObject.html).
83
-
84
- The `attr_initialize` notation notation for hash arguments is also supported: `method_object :fooable?, :foo, [:bar, :baz!]`
85
-
86
- You don't have to specify readers if you don't want them: `method_object :fooable?` is also valid.
87
-
88
-
89
- ### `attr_id_query :foo?, :bar?`<br>
90
- Defines query methods like `foo?`, which is true iff `foo_id` is truthy. Goes well with Active Record.
91
-
86
+ Defines a `.fooable?` class method that takes arguments (`foo`) and delegates to an instance method that can access those arguments as private readers.
92
87
 
93
- ### `attr_query :foo?, :bar?`<br>
94
- Defines query methods like `foo?`, which is true iff `foo` is truthy.
88
+ This is useful for [method objects](http://refactoring.com/catalog/replaceMethodWithMethodObject.html):
95
89
 
90
+ ``` ruby
91
+ class PriceCalculator
92
+ method_object :calculate,
93
+ :order
96
94
 
97
- ## Philosophy
95
+ def calculate
96
+ order.price * factor
97
+ end
98
98
 
99
- Findability is a core value.
100
- Hence the long name `attr_initialize`, so you see it when scanning for the initializer;
101
- and the enforced questionmarks with `attr_id_query :foo?`, so you can search for that method.
99
+ private
102
100
 
101
+ def factor
102
+ 1 + rand
103
+ end
104
+ end
105
+ ```
103
106
 
104
- ## Example
107
+ Shortcut for
105
108
 
106
109
  ``` ruby
107
- class MyClass
108
- pattr_initialize :foo, :bar
109
- attr_id_query :item?
110
- attr_query :oof?
111
-
112
- def oof
113
- foo.reverse
114
- end
110
+ attr_initialize :foo
111
+ attr_private :foo
115
112
 
116
- def item_id
117
- 123
118
- end
113
+ def self.fooable?(foo)
114
+ new(foo).fooable?
119
115
  end
116
+ ```
120
117
 
121
- x = MyClass.new("Foo!", "Bar!")
122
- x.oof # => "!ooF"
123
- x.foo # NoMethodError: private method `foo' called.
124
- x.item? # => true
125
- x.oof? # => true
118
+ The `attr_initialize` notation notation for hash arguments is also supported: `method_object :fooable?, :foo, [:bar, :baz!]`
126
119
 
120
+ You don't have to specify readers if you don't want them: `method_object :fooable?` is also valid.
127
121
 
128
- class MyMethodObject
129
- method_object :fooable?,
130
- :foo
131
122
 
132
- def fooable?
133
- foo == :some_value
134
- end
135
- end
123
+ ### `attr_id_query :foo?, :bar?`<br>
136
124
 
137
- MyMethodObject.fooable?(:some_value) # => true
138
- MyMethodObject.fooable?(:another_value) # => false
125
+ Defines query methods like `foo?`, which is true if (and only if) `foo_id` is truthy. Goes well with Active Record.
139
126
 
140
127
 
141
- class MyHashyObject
142
- attr_initialize :foo, [:bar, :baz]
143
- attr_reader :bar
144
- end
128
+ ### `attr_query :foo?, :bar?`<br>
145
129
 
146
- x = MyHashyObject.new("Foo!", bar: "Bar!", baz: "Baz!")
147
- x.bar # => "Bar!"
130
+ Defines query methods like `foo?`, which is true if (and only if) `foo` is truthy.
148
131
 
149
132
 
150
- class MyValueObject
151
- attr_value :foo, :bar
152
- end
133
+ ## Philosophy
134
+
135
+ Findability is a core value.
136
+ Hence the long name `attr_initialize`, so you see it when scanning for the initializer;
137
+ and the enforced questionmarks with `attr_id_query :foo?`, so you can search for that method.
153
138
 
154
- x = MyValueObject.new(5, 10)
155
- x.foo # => 5
156
- x.bar # => 10
157
- x.foo = 20 # NoMethodError: undefined method `foo=''`
158
- ```
159
139
 
160
140
  ## Why not use `Struct`?
161
141
 
@@ -0,0 +1,57 @@
1
+ class AttrExtras::AttrInitialize
2
+ def initialize(klass, names)
3
+ @klass, @names = klass, names
4
+ end
5
+
6
+ attr_reader :klass, :names
7
+ private :klass, :names
8
+
9
+ def apply
10
+ # The define_method block can't call our methods, so we need to make
11
+ # things available via local variables.
12
+ names = @names
13
+ validate_arity = method(:validate_arity)
14
+ set_ivar_from_hash = method(:set_ivar_from_hash)
15
+
16
+ klass.send(:define_method, :initialize) do |*values|
17
+ validate_arity.call(values.length)
18
+
19
+ names.zip(values).each do |name_or_names, value|
20
+ if name_or_names.is_a?(Array)
21
+ hash = value || {}
22
+
23
+ name_or_names.each do |name|
24
+ set_ivar_from_hash.call(self, name, hash)
25
+ end
26
+ else
27
+ name = name_or_names
28
+ instance_variable_set("@#{name}", value)
29
+ end
30
+ end
31
+ end
32
+ end
33
+
34
+ private
35
+
36
+ def validate_arity(provided_arity)
37
+ arity_without_hashes = names.count { |name| not name.is_a?(Array) }
38
+ arity_with_hashes = names.length
39
+
40
+ unless (arity_without_hashes..arity_with_hashes).include?(provided_arity)
41
+ arity_range = [arity_without_hashes, arity_with_hashes].uniq.join("..")
42
+ raise ArgumentError, "wrong number of arguments (#{provided_arity} for #{arity_range})"
43
+ end
44
+ end
45
+
46
+ def set_ivar_from_hash(instance, name, hash)
47
+ if name.to_s.end_with?("!")
48
+ actual_name = name.to_s.chop.to_sym
49
+ value = hash.fetch(actual_name)
50
+ else
51
+ actual_name = name
52
+ value = hash[name]
53
+ end
54
+
55
+ instance.instance_variable_set("@#{actual_name}", value)
56
+ end
57
+ end
@@ -0,0 +1,13 @@
1
+ module AttrExtras::AttrQuery
2
+ def self.define_with_suffix(klass, suffix, *names)
3
+ names.each do |name|
4
+ name = name.to_s
5
+
6
+ raise "#{__method__} wants `#{name}?`, not `#{name}`." unless name.end_with?("?")
7
+
8
+ klass.send(:define_method, name) do # def foo?
9
+ !!send("#{name.chop}#{suffix}") # !!send("foo_id")
10
+ end # end
11
+ end
12
+ end
13
+ end
@@ -0,0 +1,5 @@
1
+ module AttrExtras::Utils
2
+ def self.flat_names(names)
3
+ names.flatten.map { |x| x.to_s.sub(/!\z/, "") }
4
+ end
5
+ end
@@ -1,3 +1,3 @@
1
1
  module AttrExtras
2
- VERSION = "2.0.0"
2
+ VERSION = "2.0.1"
3
3
  end
data/lib/attr_extras.rb CHANGED
@@ -1,40 +1,12 @@
1
1
  require "attr_extras/version"
2
+ require "attr_extras/attr_initialize"
3
+ require "attr_extras/attr_query"
4
+ require "attr_extras/utils"
2
5
 
3
6
  module AttrExtras
4
7
  module ClassMethods
5
8
  def attr_initialize(*names)
6
- min_arity = names.count { |n| not n.is_a?(Array) }
7
- max_arity = names.length
8
-
9
- define_method(:initialize) do |*values|
10
- provided_arity = values.length
11
-
12
- unless (min_arity..max_arity).include?(provided_arity)
13
- arity_range = [min_arity, max_arity].uniq.join("..")
14
- raise ArgumentError, "wrong number of arguments (#{provided_arity} for #{arity_range})"
15
- end
16
-
17
- names.zip(values).each do |name_or_names, value|
18
- if name_or_names.is_a?(Array)
19
- value ||= {}
20
-
21
- name_or_names.each do |name|
22
- if name.to_s.end_with?("!")
23
- actual_name = name.to_s.chop.to_sym
24
- actual_value = value.fetch(actual_name)
25
- else
26
- actual_name = name
27
- actual_value = value[name]
28
- end
29
-
30
- instance_variable_set("@#{actual_name}", actual_value)
31
- end
32
- else
33
- name = name_or_names
34
- instance_variable_set("@#{name}", value)
35
- end
36
- end
37
- end
9
+ AttrInitialize.new(self, names).apply
38
10
  end
39
11
 
40
12
  def attr_private(*names)
@@ -44,12 +16,12 @@ module AttrExtras
44
16
 
45
17
  def pattr_initialize(*names)
46
18
  attr_initialize(*names)
47
- attr_private *attr_flat_names(names)
19
+ attr_private *Utils.flat_names(names)
48
20
  end
49
21
 
50
22
  def vattr_initialize(*names)
51
23
  attr_initialize(*names)
52
- attr_value *attr_flat_names(names)
24
+ attr_value *Utils.flat_names(names)
53
25
  end
54
26
 
55
27
  def attr_value(*names)
@@ -71,29 +43,11 @@ module AttrExtras
71
43
  end
72
44
 
73
45
  def attr_query(*names)
74
- attr_query_with_suffix(*names, "")
46
+ AttrQuery.define_with_suffix(self, "", *names)
75
47
  end
76
48
 
77
49
  def attr_id_query(*names)
78
- attr_query_with_suffix(*names, "_id")
79
- end
80
-
81
- private
82
-
83
- def attr_flat_names(names)
84
- names.flatten.map { |x| x.to_s.sub(/!\z/, "") }
85
- end
86
-
87
- def attr_query_with_suffix(*names, suffix)
88
- names.each do |name|
89
- name = name.to_s
90
-
91
- raise "#{__method__} wants `#{name}?`, not `#{name}`." unless name.end_with?("?")
92
-
93
- define_method(name) do # def foo?
94
- !!send("#{name.chop}#{suffix}") # !!send("foo_id")
95
- end # end
96
- end
50
+ AttrQuery.define_with_suffix(self, "_id", *names)
97
51
  end
98
52
  end
99
53
  end
metadata CHANGED
@@ -1,7 +1,7 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: attr_extras
3
3
  version: !ruby/object:Gem::Version
4
- version: 2.0.0
4
+ version: 2.0.1
5
5
  platform: ruby
6
6
  authors:
7
7
  - Henrik Nyh
@@ -15,14 +15,14 @@ dependencies:
15
15
  name: rake
16
16
  requirement: !ruby/object:Gem::Requirement
17
17
  requirements:
18
- - - '>='
18
+ - - ! '>='
19
19
  - !ruby/object:Gem::Version
20
20
  version: '0'
21
21
  type: :development
22
22
  prerelease: false
23
23
  version_requirements: !ruby/object:Gem::Requirement
24
24
  requirements:
25
- - - '>='
25
+ - - ! '>='
26
26
  - !ruby/object:Gem::Version
27
27
  version: '0'
28
28
  description:
@@ -40,6 +40,9 @@ files:
40
40
  - Rakefile
41
41
  - attr_extras.gemspec
42
42
  - lib/attr_extras.rb
43
+ - lib/attr_extras/attr_initialize.rb
44
+ - lib/attr_extras/attr_query.rb
45
+ - lib/attr_extras/utils.rb
43
46
  - lib/attr_extras/version.rb
44
47
  - script/test
45
48
  - spec/attr_extras_spec.rb
@@ -53,17 +56,17 @@ require_paths:
53
56
  - lib
54
57
  required_ruby_version: !ruby/object:Gem::Requirement
55
58
  requirements:
56
- - - '>='
59
+ - - ! '>='
57
60
  - !ruby/object:Gem::Version
58
61
  version: '0'
59
62
  required_rubygems_version: !ruby/object:Gem::Requirement
60
63
  requirements:
61
- - - '>='
64
+ - - ! '>='
62
65
  - !ruby/object:Gem::Version
63
66
  version: '0'
64
67
  requirements: []
65
68
  rubyforge_project:
66
- rubygems_version: 2.2.0
69
+ rubygems_version: 2.2.1
67
70
  signing_key:
68
71
  specification_version: 4
69
72
  summary: Takes some boilerplate out of Ruby with methods like attr_initialize.