attr_extras 2.0.0 → 2.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 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.