rol 0.0.1

Sign up to get free protection for your applications and to get access to all the features.
@@ -0,0 +1,7 @@
1
+ ---
2
+ SHA1:
3
+ metadata.gz: c9a583889c5bb1db7b14ba7eb318f87c2e3809ce
4
+ data.tar.gz: b918dfbaf8df2905800f05a2fdc4de9ef051a925
5
+ SHA512:
6
+ metadata.gz: e38eaca339fb517cd58a5691a59a287846b6c2d4676a3da50e8a6d3c087b35ce91bd12088759f8f159a2f277960d27ea1936ab19243b6f38609521bf005c81a7
7
+ data.tar.gz: 10122bd637c9cc64c52621b1cf92fd7e7d673af44bb768e9681673d8ea53af181bac05f996ed15b52aaeeae5920bf22d97ed6c9f916075f2556572383dddbd0e
data/LICENSE ADDED
@@ -0,0 +1,21 @@
1
+ The MIT License (MIT)
2
+
3
+ Copyright (c) 2014 Michael Lewandowski
4
+
5
+ Permission is hereby granted, free of charge, to any person obtaining a copy
6
+ of this software and associated documentation files (the "Software"), to deal
7
+ in the Software without restriction, including without limitation the rights
8
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9
+ copies of the Software, and to permit persons to whom the Software is
10
+ furnished to do so, subject to the following conditions:
11
+
12
+ The above copyright notice and this permission notice shall be included in all
13
+ copies or substantial portions of the Software.
14
+
15
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21
+ SOFTWARE.
@@ -0,0 +1,121 @@
1
+ #
2
+ # 'require' all files except ourselves.
3
+ #
4
+ me = File.absolute_path(__FILE__)
5
+ Dir.glob(File.dirname(me) + '/**/*.rb') {|fn| require fn unless fn == me }
6
+
7
+
8
+ #
9
+ # Module Rol (Ruby object literal) provides the utility method rol() that
10
+ # creates an object instance from a hash, similar to the way objects can
11
+ # created in JavaScript.
12
+ #
13
+ module Rol
14
+ def self.rol(hash = {})
15
+ check_arg(hash)
16
+ build_object(hash)
17
+ end
18
+
19
+ private
20
+
21
+ def self.check_arg(hash)
22
+ raise_not_a_hash_argument_error unless hash.respond_to?(:each_pair)
23
+ end
24
+
25
+ def self.raise_not_a_hash_argument_error
26
+ raise ArgumentError.new("rol(hash): 'hash' argument must respond to #each_pair")
27
+ end
28
+
29
+ def self.build_object(hash)
30
+ object = Object.new
31
+ add_members(object, hash)
32
+ object
33
+ end
34
+
35
+ def self.add_members(object, hash)
36
+ hash.each_pair { |name, value| add_member(object, name, value) }
37
+ end
38
+
39
+ def self.add_member(object, name, value)
40
+ if value.is_a? Proc
41
+ add_method_member(object, name, value)
42
+ else
43
+ add_attribute_member(object, name, value)
44
+ end
45
+ end
46
+
47
+ #
48
+ # def object.my_method(arg1, arg2, ...)
49
+ # ...
50
+ # end
51
+ #
52
+ def self.add_method_member(object, method_name, proc)
53
+ object.define_singleton_method(method_name) do |*args|
54
+ object.instance_exec(*args, &proc)
55
+ end
56
+ end
57
+
58
+ #
59
+ # def object.my_attribute
60
+ # @my_attribute
61
+ # end
62
+ #
63
+ # def object.my_attribute=(value)
64
+ # @my_attribute = value
65
+ # end
66
+ #
67
+ # object.my_attribute = initial_value
68
+ #
69
+ def self.add_attribute_member(object, attribute_name, initial_value)
70
+ check_attribute_name(attribute_name)
71
+ instance_variable_name = build_instance_variable_name(attribute_name)
72
+ add_attribute_getter_method(object, attribute_name, instance_variable_name)
73
+ add_attribute_setter_method(object, attribute_name, instance_variable_name)
74
+ initialize_attribute(object, instance_variable_name, initial_value)
75
+ end
76
+
77
+ def self.check_attribute_name(attribute_name)
78
+ raise_ends_with_equals_argument_error(attribute_name) if attribute_name.to_s.end_with?('=')
79
+ end
80
+
81
+ def self.raise_ends_with_equals_argument_error(attribute_name)
82
+ raise ArgumentError.new("rol(hash): attribute name '#{attribute_name}' must not end with =")
83
+ end
84
+
85
+ #
86
+ # 'my_attribute' => :@my_attribute
87
+ #
88
+ def self.build_instance_variable_name(attribute_name)
89
+ "@#{attribute_name}".to_sym
90
+ end
91
+
92
+ #
93
+ # def object.my_attribute
94
+ # @my_attribute
95
+ # end
96
+ #
97
+ def self.add_attribute_getter_method(object, attribute_name, instance_variable_name)
98
+ object.define_singleton_method(attribute_name) do
99
+ instance_variable_get(instance_variable_name)
100
+ end
101
+ end
102
+
103
+ #
104
+ # def object.my_attribute=(value)
105
+ # @my_attribute = value
106
+ # end
107
+ #
108
+ def self.add_attribute_setter_method(object, attribute_name, instance_variable_name)
109
+ method_name = "#{attribute_name}="
110
+ object.define_singleton_method(method_name) do |value|
111
+ instance_variable_set(instance_variable_name, value)
112
+ end
113
+ end
114
+
115
+ #
116
+ # object.my_attribute = initial_value
117
+ #
118
+ def self.initialize_attribute(object, instance_variable_name, initial_value)
119
+ object.instance_variable_set(instance_variable_name, initial_value)
120
+ end
121
+ end
@@ -0,0 +1,8 @@
1
+ #
2
+ # Save some typing by making rol() syntactic sugar for Rol.rol()
3
+ #
4
+ module Kernel
5
+ def rol(*args)
6
+ Rol.rol(*args)
7
+ end
8
+ end
@@ -0,0 +1,3 @@
1
+ module Rol
2
+ Version = "0.0.1"
3
+ end
@@ -0,0 +1,224 @@
1
+ require_relative '../test_helper'
2
+
3
+
4
+ #
5
+ # In the various tests below, we add various methods to an instance
6
+ # of Object. Here we ensure Object does not already have those
7
+ # methods.
8
+ #
9
+ describe 'assumptions about Object' do
10
+ [ :name, :greet, :x, :Capitalized, :first, :second, :third, :my_method, :height, :width, :area ].each do |name|
11
+ it "does not have an attribute called :#{name}" do
12
+ Object.new.instance_variables.wont_include name
13
+ end
14
+ it "does not have a method called :#{name}" do
15
+ Object.new.methods.wont_include name
16
+ end
17
+ it "does not have a method called :#{name}=" do
18
+ Object.new.methods.wont_include "#{name}=".to_sym
19
+ end
20
+ end
21
+ end
22
+
23
+
24
+ #
25
+ # What does rol do?
26
+ #
27
+ describe 'rol()' do
28
+ it 'provides a shorthand way to create an object on the fly' do
29
+ object = rol({
30
+ name: 'Fluffy',
31
+ greet: -> (your_name) { "Hi #{your_name}! My name is #{@name}!" }
32
+ })
33
+ object.greet('Spot').must_equal 'Hi Spot! My name is Fluffy!'
34
+ end
35
+ end
36
+
37
+
38
+ #
39
+ # How do I find the rol version?
40
+ #
41
+ describe 'Rol.Version' do
42
+ it 'returns the rol() version' do
43
+ Rol::Version.must_equal '0.0.1'
44
+ end
45
+ end
46
+
47
+
48
+ #
49
+ # What arguments does rol take and what restrictions do they have?
50
+ #
51
+ describe 'arguments' do
52
+ it 'expects one argument that behaves like a hash' do
53
+ rol({ x: 42 })
54
+ end
55
+ it 'raises an exception when the argument does not respond to #each_pair' do
56
+ exception = proc { rol([1,2,3]) }.must_raise ArgumentError
57
+ exception.message.must_equal "rol(hash): 'hash' argument must respond to #each_pair"
58
+ end
59
+ it 'raises an exception when called with more than one argument' do
60
+ exception = proc { rol({}, 42) }.must_raise ArgumentError
61
+ exception.message.must_equal 'wrong number of arguments (2 for 0..1)'
62
+ end
63
+ it 'does not raise an exception when called with no arguments' do
64
+ rol()
65
+ end
66
+ end
67
+
68
+
69
+ #
70
+ # What does rol return?
71
+ #
72
+ describe 'return value' do
73
+ it 'returns an instance of Object' do
74
+ rol().class.must_equal Object
75
+ end
76
+ it 'returns an instance of Object with no additional methods when no arguments are supplied' do
77
+ rol().methods.must_equal Object.new.methods
78
+ end
79
+ it 'returns an instance of Object with no additional methods when an empty hash is supplied' do
80
+ rol({}).methods.must_equal Object.new.methods
81
+ end
82
+ end
83
+
84
+
85
+ #
86
+ # What happens when I define an attribute?
87
+ #
88
+ describe 'defining an attribute' do
89
+ before do
90
+ @object = rol({ x: 42 })
91
+ end
92
+ it 'creates the attribute' do
93
+ @object.instance_variables.must_include :@x
94
+ end
95
+ it 'creates a get method for the attribute' do
96
+ @object.methods.must_include :x
97
+ end
98
+ it 'creates a set method for the attribute' do
99
+ @object.methods.must_include :x=
100
+ end
101
+ it 'sets the initial value of the attribute' do
102
+ @object.instance_variable_get(:@x).must_equal 42
103
+ end
104
+ end
105
+
106
+
107
+ #
108
+ # What does the get method for an attribute do?
109
+ #
110
+ describe 'attribute get method' do
111
+ it 'returns the value of the attibute' do
112
+ @object = rol({ x: 42 })
113
+ @object.x.must_equal 42
114
+ end
115
+ end
116
+
117
+
118
+ #
119
+ # What does the set method for an attribute do?
120
+ #
121
+ describe 'attribute set method' do
122
+ it 'sets the value of the attribute' do
123
+ @object = rol({ x: 42 })
124
+ @object.x = 24
125
+ @object.instance_variable_get(:@x).must_equal 24
126
+ end
127
+ end
128
+
129
+
130
+ #
131
+ # What are the restrictions on attribute names?
132
+ #
133
+ describe 'attribute names' do
134
+ it 'allows capitalized attribute names' do
135
+ rol({ Capitalized: 42 }).methods.must_include :Capitalized
136
+ end
137
+ it 'raises an exception if the attribute name ends with =' do
138
+ exception = proc { rol({ 'name=' => 42 }) }.must_raise ArgumentError
139
+ exception.message.must_equal "rol(hash): attribute name 'name=' must not end with ="
140
+ end
141
+ end
142
+
143
+
144
+ #
145
+ # Can I define more than one attribute?
146
+ #
147
+ describe 'multiple attribute definitions' do
148
+ it 'allows many attributes to be defined' do
149
+ object = rol({ first: 1, second: 2, third: 3 })
150
+ object.methods.must_include :first
151
+ object.methods.must_include :second
152
+ object.methods.must_include :third
153
+ end
154
+ end
155
+
156
+
157
+ #
158
+ # What happens when I define a method?
159
+ #
160
+ describe 'method definition' do
161
+ before do
162
+ @object = rol({ my_method: -> (x) { x * 2 } })
163
+ end
164
+ it 'allows a method to be defined' do
165
+ @object.methods.must_include :my_method
166
+ end
167
+ it 'defines the method as read-only' do
168
+ @object.methods.wont_include :my_method=
169
+ end
170
+ it 'defines a method that can actually be called' do
171
+ @object.my_method(3).must_equal 6
172
+ end
173
+ end
174
+
175
+
176
+ #
177
+ # What are the restrictions on method names?
178
+ #
179
+ describe 'method names' do
180
+ it 'does not raise an exception if the method name ends with =' do
181
+ rol({ 'name=' => -> {} })
182
+ end
183
+ end
184
+
185
+
186
+ #
187
+ # Can methods access attributes?
188
+ #
189
+ describe 'method invocation' do
190
+ it 'invokes the method in the context of the object' do
191
+ object = rol({
192
+ height: 2,
193
+ width: 3,
194
+ area: -> { @height * @width } # @height and @width must be accessible
195
+ })
196
+ object.area.must_equal 6
197
+ end
198
+ end
199
+
200
+
201
+ #
202
+ # Provide Kernel.rol() as an alias for Rol.rol():
203
+ #
204
+ # o = Rol.rol({ x: 42 })
205
+ # o = rol({ x: 42 }) # easier to read, faster to write
206
+ # o = %o{ x: 42 } # would be nicer still
207
+ #
208
+ describe 'Kernel.rol()' do
209
+ it 'is defined' do
210
+ Kernel.methods.must_include :rol
211
+ end
212
+ it 'invokes Rol.rol()' do
213
+ Rol.expects(:rol)
214
+ rol()
215
+ end
216
+ it 'blindly forwards any and all arguments it was called with along to Rol.rol()' do
217
+ Rol.expects(:rol).with('hello', 'doctor', 'name', 'continue', 'yesterday', 'tomorrow')
218
+ rol('hello', 'doctor', 'name', 'continue', 'yesterday', 'tomorrow')
219
+ end
220
+ it 'passes nothing to Rol.rol() when called with no arguments' do
221
+ Rol.expects(:rol).with()
222
+ rol()
223
+ end
224
+ end
metadata ADDED
@@ -0,0 +1,93 @@
1
+ --- !ruby/object:Gem::Specification
2
+ name: rol
3
+ version: !ruby/object:Gem::Version
4
+ version: 0.0.1
5
+ platform: ruby
6
+ authors:
7
+ - Michael Lewandowski
8
+ autorequire:
9
+ bindir: bin
10
+ cert_chain: []
11
+ date: 2014-11-25 00:00:00.000000000 Z
12
+ dependencies:
13
+ - !ruby/object:Gem::Dependency
14
+ name: mocha
15
+ requirement: !ruby/object:Gem::Requirement
16
+ requirements:
17
+ - - "~>"
18
+ - !ruby/object:Gem::Version
19
+ version: '0'
20
+ type: :development
21
+ prerelease: false
22
+ version_requirements: !ruby/object:Gem::Requirement
23
+ requirements:
24
+ - - "~>"
25
+ - !ruby/object:Gem::Version
26
+ version: '0'
27
+ - !ruby/object:Gem::Dependency
28
+ name: bundler
29
+ requirement: !ruby/object:Gem::Requirement
30
+ requirements:
31
+ - - "~>"
32
+ - !ruby/object:Gem::Version
33
+ version: '1'
34
+ type: :development
35
+ prerelease: false
36
+ version_requirements: !ruby/object:Gem::Requirement
37
+ requirements:
38
+ - - "~>"
39
+ - !ruby/object:Gem::Version
40
+ version: '1'
41
+ - !ruby/object:Gem::Dependency
42
+ name: rake
43
+ requirement: !ruby/object:Gem::Requirement
44
+ requirements:
45
+ - - "~>"
46
+ - !ruby/object:Gem::Version
47
+ version: '10'
48
+ type: :development
49
+ prerelease: false
50
+ version_requirements: !ruby/object:Gem::Requirement
51
+ requirements:
52
+ - - "~>"
53
+ - !ruby/object:Gem::Version
54
+ version: '10'
55
+ description: |2
56
+ rol defines Ruby objects from a hash of attributes and methods
57
+ email: milewdev@gmail.com
58
+ executables: []
59
+ extensions: []
60
+ extra_rdoc_files: []
61
+ files:
62
+ - LICENSE
63
+ - lib/rol.rb
64
+ - lib/rol/kernel.rb
65
+ - lib/rol/version.rb
66
+ - test/rol/rol_test.rb
67
+ homepage: https://github.com/milewdev/rol
68
+ licenses:
69
+ - MIT
70
+ metadata: {}
71
+ post_install_message:
72
+ rdoc_options: []
73
+ require_paths:
74
+ - lib
75
+ required_ruby_version: !ruby/object:Gem::Requirement
76
+ requirements:
77
+ - - "~>"
78
+ - !ruby/object:Gem::Version
79
+ version: '2'
80
+ required_rubygems_version: !ruby/object:Gem::Requirement
81
+ requirements:
82
+ - - ">="
83
+ - !ruby/object:Gem::Version
84
+ version: '0'
85
+ requirements:
86
+ - Ruby 2
87
+ rubyforge_project:
88
+ rubygems_version: 2.2.2
89
+ signing_key:
90
+ specification_version: 4
91
+ summary: rol defines Ruby objects from a hash of attributes and methods
92
+ test_files:
93
+ - test/rol/rol_test.rb