rol 0.0.1
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +7 -0
- data/LICENSE +21 -0
- data/lib/rol.rb +121 -0
- data/lib/rol/kernel.rb +8 -0
- data/lib/rol/version.rb +3 -0
- data/test/rol/rol_test.rb +224 -0
- metadata +93 -0
checksums.yaml
ADDED
@@ -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.
|
data/lib/rol.rb
ADDED
@@ -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
|
data/lib/rol/kernel.rb
ADDED
data/lib/rol/version.rb
ADDED
@@ -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
|