doodle 0.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.
- data/COPYING +18 -0
- data/README +57 -0
- data/examples/event.rb +30 -0
- data/examples/event1.rb +33 -0
- data/examples/event2.rb +21 -0
- data/examples/example-01.rb +16 -0
- data/examples/example-01.rdoc +16 -0
- data/examples/example-02.rb +61 -0
- data/examples/example-02.rdoc +62 -0
- data/lib/doodle.rb +800 -0
- data/lib/molic_orderedhash.rb +243 -0
- data/lib/spec_helper.rb +19 -0
- data/spec/arg_order_spec.rb +125 -0
- data/spec/attributes_spec.rb +106 -0
- data/spec/class_spec.rb +90 -0
- data/spec/conversion_spec.rb +59 -0
- data/spec/defaults_spec.rb +158 -0
- data/spec/doodle_spec.rb +297 -0
- data/spec/flatten_first_level_spec.rb +36 -0
- data/spec/required_spec.rb +25 -0
- data/spec/superclass_spec.rb +27 -0
- data/spec/validation_spec.rb +108 -0
- metadata +74 -0
@@ -0,0 +1,243 @@
|
|
1
|
+
# AUTHOR
|
2
|
+
# jan molic /mig/at/1984/dot/cz/
|
3
|
+
#
|
4
|
+
# DESCRIPTION
|
5
|
+
# Hash with preserved order and some array-like extensions
|
6
|
+
# Public domain.
|
7
|
+
#
|
8
|
+
# THANKS
|
9
|
+
# Andrew Johnson for his suggestions and fixes of Hash[],
|
10
|
+
# merge, to_a, inspect and shift
|
11
|
+
class OrderedHash < ::Hash
|
12
|
+
#--{{{
|
13
|
+
attr_accessor :order
|
14
|
+
|
15
|
+
class << self
|
16
|
+
#--{{{
|
17
|
+
def [] *args
|
18
|
+
#--{{{
|
19
|
+
hsh = OrderedHash.new
|
20
|
+
if Hash === args[0]
|
21
|
+
hsh.replace args[0]
|
22
|
+
elsif (args.size % 2) != 0
|
23
|
+
raise ArgumentError, "odd number of elements for Hash"
|
24
|
+
else
|
25
|
+
hsh[args.shift] = args.shift while args.size > 0
|
26
|
+
end
|
27
|
+
hsh
|
28
|
+
#--}}}
|
29
|
+
end
|
30
|
+
#--}}}
|
31
|
+
end
|
32
|
+
# def initialize
|
33
|
+
##--{{{
|
34
|
+
# @order = []
|
35
|
+
##--}}}
|
36
|
+
# end
|
37
|
+
def initialize(*a, &b)
|
38
|
+
#--{{{
|
39
|
+
super
|
40
|
+
@order = []
|
41
|
+
#--}}}
|
42
|
+
end
|
43
|
+
def store_only a,b
|
44
|
+
#--{{{
|
45
|
+
store a,b
|
46
|
+
#--}}}
|
47
|
+
end
|
48
|
+
alias orig_store store
|
49
|
+
def store a,b
|
50
|
+
#--{{{
|
51
|
+
@order.push a unless has_key? a
|
52
|
+
super a,b
|
53
|
+
#--}}}
|
54
|
+
end
|
55
|
+
alias []= store
|
56
|
+
def == hsh2
|
57
|
+
#--{{{
|
58
|
+
return false if @order != hsh2.order
|
59
|
+
super hsh2
|
60
|
+
#--}}}
|
61
|
+
end
|
62
|
+
def clear
|
63
|
+
#--{{{
|
64
|
+
@order = []
|
65
|
+
super
|
66
|
+
#--}}}
|
67
|
+
end
|
68
|
+
def delete key
|
69
|
+
#--{{{
|
70
|
+
@order.delete key
|
71
|
+
super
|
72
|
+
#--}}}
|
73
|
+
end
|
74
|
+
def each_key
|
75
|
+
#--{{{
|
76
|
+
@order.each { |k| yield k }
|
77
|
+
self
|
78
|
+
#--}}}
|
79
|
+
end
|
80
|
+
def each_value
|
81
|
+
#--{{{
|
82
|
+
@order.each { |k| yield self[k] }
|
83
|
+
self
|
84
|
+
#--}}}
|
85
|
+
end
|
86
|
+
def each
|
87
|
+
#--{{{
|
88
|
+
@order.each { |k| yield k,self[k] }
|
89
|
+
self
|
90
|
+
#--}}}
|
91
|
+
end
|
92
|
+
alias each_pair each
|
93
|
+
def delete_if
|
94
|
+
#--{{{
|
95
|
+
@order.clone.each { |k|
|
96
|
+
delete k if yield
|
97
|
+
}
|
98
|
+
self
|
99
|
+
#--}}}
|
100
|
+
end
|
101
|
+
def values
|
102
|
+
#--{{{
|
103
|
+
ary = []
|
104
|
+
@order.each { |k| ary.push self[k] }
|
105
|
+
ary
|
106
|
+
#--}}}
|
107
|
+
end
|
108
|
+
def keys
|
109
|
+
#--{{{
|
110
|
+
@order
|
111
|
+
#--}}}
|
112
|
+
end
|
113
|
+
def invert
|
114
|
+
#--{{{
|
115
|
+
hsh2 = Hash.new
|
116
|
+
@order.each { |k| hsh2[self[k]] = k }
|
117
|
+
hsh2
|
118
|
+
#--}}}
|
119
|
+
end
|
120
|
+
def reject &block
|
121
|
+
#--{{{
|
122
|
+
self.dup.delete_if &block
|
123
|
+
#--}}}
|
124
|
+
end
|
125
|
+
def reject! &block
|
126
|
+
#--{{{
|
127
|
+
hsh2 = reject &block
|
128
|
+
self == hsh2 ? nil : hsh2
|
129
|
+
#--}}}
|
130
|
+
end
|
131
|
+
def replace hsh2
|
132
|
+
#--{{{
|
133
|
+
@order = hsh2.keys
|
134
|
+
super hsh2
|
135
|
+
#--}}}
|
136
|
+
end
|
137
|
+
def shift
|
138
|
+
#--{{{
|
139
|
+
key = @order.first
|
140
|
+
key ? [key,delete(key)] : super
|
141
|
+
#--}}}
|
142
|
+
end
|
143
|
+
def unshift k,v
|
144
|
+
#--{{{
|
145
|
+
unless self.include? k
|
146
|
+
@order.unshift k
|
147
|
+
orig_store(k,v)
|
148
|
+
true
|
149
|
+
else
|
150
|
+
false
|
151
|
+
end
|
152
|
+
#--}}}
|
153
|
+
end
|
154
|
+
def push k,v
|
155
|
+
#--{{{
|
156
|
+
unless self.include? k
|
157
|
+
@order.push k
|
158
|
+
orig_store(k,v)
|
159
|
+
true
|
160
|
+
else
|
161
|
+
false
|
162
|
+
end
|
163
|
+
#--}}}
|
164
|
+
end
|
165
|
+
def pop
|
166
|
+
#--{{{
|
167
|
+
key = @order.last
|
168
|
+
key ? [key,delete(key)] : nil
|
169
|
+
#--}}}
|
170
|
+
end
|
171
|
+
def to_a
|
172
|
+
#--{{{
|
173
|
+
ary = []
|
174
|
+
each { |k,v| ary << [k,v] }
|
175
|
+
ary
|
176
|
+
#--}}}
|
177
|
+
end
|
178
|
+
def to_s
|
179
|
+
#--{{{
|
180
|
+
self.to_a.to_s
|
181
|
+
#--}}}
|
182
|
+
end
|
183
|
+
def inspect
|
184
|
+
#--{{{
|
185
|
+
ary = []
|
186
|
+
each {|k,v| ary << k.inspect + "=>" + v.inspect}
|
187
|
+
'{' + ary.join(", ") + '}'
|
188
|
+
#--}}}
|
189
|
+
end
|
190
|
+
def update hsh2
|
191
|
+
#--{{{
|
192
|
+
hsh2.each { |k,v| self[k] = v }
|
193
|
+
self
|
194
|
+
#--}}}
|
195
|
+
end
|
196
|
+
alias :merge! update
|
197
|
+
def merge(hsh2)
|
198
|
+
#--{{{
|
199
|
+
self.dup.update(hsh2)
|
200
|
+
#--}}}
|
201
|
+
end
|
202
|
+
def select
|
203
|
+
#--{{{
|
204
|
+
ary = []
|
205
|
+
each { |k,v| ary << [k,v] if yield k,v }
|
206
|
+
ary
|
207
|
+
#--}}}
|
208
|
+
end
|
209
|
+
def class
|
210
|
+
#--{{{
|
211
|
+
Hash
|
212
|
+
#--}}}
|
213
|
+
end
|
214
|
+
|
215
|
+
attr_accessor "to_yaml_style"
|
216
|
+
def yaml_inline= bool
|
217
|
+
if respond_to?("to_yaml_style")
|
218
|
+
self.to_yaml_style = :inline
|
219
|
+
else
|
220
|
+
unless defined? @__yaml_inline_meth
|
221
|
+
@__yaml_inline_meth =
|
222
|
+
lambda {|opts|
|
223
|
+
YAML::quick_emit(object_id, opts) {|emitter|
|
224
|
+
emitter << '{ ' << map{|kv| kv.join ': '}.join(', ') << ' }'
|
225
|
+
}
|
226
|
+
}
|
227
|
+
class << self
|
228
|
+
def to_yaml opts = {}
|
229
|
+
begin
|
230
|
+
@__yaml_inline ? @__yaml_inline_meth[ opts ] : super
|
231
|
+
rescue
|
232
|
+
@to_yaml_style = :inline
|
233
|
+
super
|
234
|
+
end
|
235
|
+
end
|
236
|
+
end
|
237
|
+
end
|
238
|
+
end
|
239
|
+
@__yaml_inline = bool
|
240
|
+
end
|
241
|
+
def yaml_inline!() self.yaml_inline = true end
|
242
|
+
#--}}}
|
243
|
+
end # class OrderedHash
|
data/lib/spec_helper.rb
ADDED
@@ -0,0 +1,19 @@
|
|
1
|
+
$:.unshift(File.join(File.dirname(__FILE__), '../.'))
|
2
|
+
|
3
|
+
require 'lib/doodle'
|
4
|
+
require 'date'
|
5
|
+
|
6
|
+
def undefine_const(*consts)
|
7
|
+
consts.each do |const|
|
8
|
+
if Object.const_defined?(const)
|
9
|
+
Object.send(:remove_const, const)
|
10
|
+
end
|
11
|
+
end
|
12
|
+
end
|
13
|
+
|
14
|
+
def raise_if_defined(*args)
|
15
|
+
defined, undefined = args.partition{ |x| Object.const_defined?(x)}
|
16
|
+
raise "Namespace pollution: #{defined.join(', ')}" if defined.size > 0
|
17
|
+
end
|
18
|
+
|
19
|
+
|
@@ -0,0 +1,125 @@
|
|
1
|
+
require 'lib/spec_helper'
|
2
|
+
|
3
|
+
describe 'arg_order' do
|
4
|
+
before :each do
|
5
|
+
raise_if_defined :Foo
|
6
|
+
class Foo < Doodle::Base
|
7
|
+
has :name
|
8
|
+
has :extra
|
9
|
+
has :value
|
10
|
+
arg_order :value, :name
|
11
|
+
end
|
12
|
+
end
|
13
|
+
after :each do
|
14
|
+
undefine_const :Foo
|
15
|
+
end
|
16
|
+
it 'should specify order of positional arguments' do
|
17
|
+
foo = Foo.new 1, 2, 3
|
18
|
+
foo.value.should == 1
|
19
|
+
foo.name.should == 2
|
20
|
+
foo.extra.should == 3
|
21
|
+
end
|
22
|
+
|
23
|
+
it 'should allow only symbols as arguments to arg_order' do
|
24
|
+
proc { Foo.arg_order Foo}.should raise_error(Doodle::InvalidOrderError)
|
25
|
+
proc { Foo.arg_order 1}.should raise_error(Doodle::InvalidOrderError)
|
26
|
+
proc { Foo.arg_order Date.new}.should raise_error(Doodle::InvalidOrderError)
|
27
|
+
end
|
28
|
+
|
29
|
+
it 'should not allow invalid positional arguments' do
|
30
|
+
proc { Foo.arg_order :smoo}.should raise_error(Doodle::InvalidOrderError)
|
31
|
+
proc { Foo.arg_order :name, :value}.should_not raise_error
|
32
|
+
end
|
33
|
+
end
|
34
|
+
|
35
|
+
describe 'arg_order' do
|
36
|
+
before :each do
|
37
|
+
raise_if_defined :Foo, :Bar
|
38
|
+
class Foo < Doodle::Base
|
39
|
+
has :name
|
40
|
+
end
|
41
|
+
class Bar < Foo
|
42
|
+
has :value
|
43
|
+
end
|
44
|
+
end
|
45
|
+
after :each do
|
46
|
+
undefine_const(:Bar)
|
47
|
+
undefine_const(:Foo)
|
48
|
+
end
|
49
|
+
it 'should specify order of positional arguments' do
|
50
|
+
f = Bar.new 1, 2
|
51
|
+
f.name.should == 1
|
52
|
+
f.value.should == 2
|
53
|
+
end
|
54
|
+
end
|
55
|
+
|
56
|
+
describe 'arg_order' do
|
57
|
+
before :each do
|
58
|
+
raise_if_defined(:Foo, :Bar)
|
59
|
+
class Foo < Doodle::Base
|
60
|
+
has :name
|
61
|
+
end
|
62
|
+
class Bar < Foo
|
63
|
+
has :value
|
64
|
+
arg_order :value, :name
|
65
|
+
end
|
66
|
+
end
|
67
|
+
after :each do
|
68
|
+
undefine_const(:Bar)
|
69
|
+
undefine_const(:Foo)
|
70
|
+
end
|
71
|
+
it 'should specify order of positional arguments' do
|
72
|
+
f = Bar.new 1, 2
|
73
|
+
f.value.should == 1
|
74
|
+
f.name.should == 2
|
75
|
+
end
|
76
|
+
|
77
|
+
end
|
78
|
+
|
79
|
+
describe 'arg_order' do
|
80
|
+
before :each do
|
81
|
+
raise_if_defined(:Foo, :Bar)
|
82
|
+
class Foo < Doodle::Base
|
83
|
+
has :name, :default => proc { self.class.to_s.downcase }
|
84
|
+
end
|
85
|
+
class Bar < Foo
|
86
|
+
has :value
|
87
|
+
arg_order :value, :name
|
88
|
+
end
|
89
|
+
end
|
90
|
+
after :each do
|
91
|
+
undefine_const(:Bar, :Foo)
|
92
|
+
end
|
93
|
+
|
94
|
+
it 'should specify order of positional arguments' do
|
95
|
+
f = Bar.new 1
|
96
|
+
f.value.should == 1
|
97
|
+
f.name.should == "bar"
|
98
|
+
end
|
99
|
+
|
100
|
+
end
|
101
|
+
|
102
|
+
describe 'arg_order' do
|
103
|
+
after :each do
|
104
|
+
undefine_const(:Bar, :Foo)
|
105
|
+
end
|
106
|
+
before :each do
|
107
|
+
raise_if_defined(:Foo, :Bar)
|
108
|
+
class Foo < Doodle::Base
|
109
|
+
has :name
|
110
|
+
end
|
111
|
+
class Bar < Foo
|
112
|
+
has :value
|
113
|
+
arg_order :value, :name
|
114
|
+
end
|
115
|
+
end
|
116
|
+
|
117
|
+
it 'should specify order of positional arguments' do
|
118
|
+
f = Bar.new 1, "bar"
|
119
|
+
f.value.should == 1
|
120
|
+
f.name.should == "bar"
|
121
|
+
end
|
122
|
+
|
123
|
+
end
|
124
|
+
|
125
|
+
raise_if_defined(:Foo, :Bar)
|
@@ -0,0 +1,106 @@
|
|
1
|
+
require 'lib/spec_helper'
|
2
|
+
|
3
|
+
describe Doodle::Attribute, 'basics' do
|
4
|
+
after :each do
|
5
|
+
undefine_const(:Bar)
|
6
|
+
undefine_const(:Foo)
|
7
|
+
end
|
8
|
+
before(:each) do
|
9
|
+
raise_if_defined(:Foo, :Bar)
|
10
|
+
|
11
|
+
class Foo
|
12
|
+
include Doodle::Helper
|
13
|
+
has :name, :default => 'Hello'
|
14
|
+
class << self
|
15
|
+
has :metadata
|
16
|
+
end
|
17
|
+
end
|
18
|
+
class Bar < Foo
|
19
|
+
has :info
|
20
|
+
class << self
|
21
|
+
has :doc
|
22
|
+
end
|
23
|
+
end
|
24
|
+
|
25
|
+
@goo = Foo.new
|
26
|
+
@baz = Bar.new :info => 'Hi'
|
27
|
+
end
|
28
|
+
|
29
|
+
it 'should have default name' do
|
30
|
+
#pending 'making it work'
|
31
|
+
#p [:name, :default, @goo.attributes[:name].default]
|
32
|
+
@goo.attributes[:name].default.should == 'Hello'
|
33
|
+
end
|
34
|
+
|
35
|
+
it 'should have default name' do
|
36
|
+
#pending 'making it work'
|
37
|
+
@goo.name.should == 'Hello'
|
38
|
+
end
|
39
|
+
|
40
|
+
it 'should have name required == false (because has default)' do
|
41
|
+
#pending 'to do required/optional'
|
42
|
+
@goo.attributes[:name].required?.should == false
|
43
|
+
end
|
44
|
+
|
45
|
+
it 'should have info required == true' do
|
46
|
+
#pending 'to do required/optional'
|
47
|
+
@baz.attributes[:info].required?.should == true
|
48
|
+
end
|
49
|
+
|
50
|
+
it 'should have name.optional? == true (because has default)' do
|
51
|
+
#pending 'to do required/optional'
|
52
|
+
@goo.attributes[:name].optional?.should == true
|
53
|
+
end
|
54
|
+
|
55
|
+
it 'should have info.optional? == false' do
|
56
|
+
#pending 'to do required/optional'
|
57
|
+
@baz.attributes[:info].optional?.should == false
|
58
|
+
end
|
59
|
+
|
60
|
+
it "should have parents in order" do
|
61
|
+
Bar.parents.should == [Foo, Object]
|
62
|
+
end
|
63
|
+
|
64
|
+
it "should have Bar's meta parents in reverse order of definition" do
|
65
|
+
@baz.meta.parents.should == [Bar.singleton_class.singleton_class, Bar.singleton_class, Foo.singleton_class]
|
66
|
+
end
|
67
|
+
|
68
|
+
it 'should have inherited meta local_attributes in order of definition' do
|
69
|
+
@baz.meta.class_eval { collect_inherited(:local_attributes).map { |x| x[0]} }.should == [:metadata, :doc]
|
70
|
+
end
|
71
|
+
|
72
|
+
it 'should have inherited meta attributes in order of definition' do
|
73
|
+
@baz.meta.attributes.keys.should == [:metadata, :doc]
|
74
|
+
end
|
75
|
+
end
|
76
|
+
|
77
|
+
describe Doodle::Attribute, 'attribute order' do
|
78
|
+
before :each do
|
79
|
+
undefine_const(:A)
|
80
|
+
undefine_const(:B)
|
81
|
+
undefine_const(:C)
|
82
|
+
|
83
|
+
class A < Doodle::Base
|
84
|
+
has :a
|
85
|
+
end
|
86
|
+
|
87
|
+
class B < A
|
88
|
+
has :b
|
89
|
+
end
|
90
|
+
|
91
|
+
class C < B
|
92
|
+
has :c
|
93
|
+
end
|
94
|
+
|
95
|
+
end
|
96
|
+
|
97
|
+
it 'should keep order of inherited attributes' do
|
98
|
+
C.parents.should == [B, A, Doodle::Base, Object]
|
99
|
+
end
|
100
|
+
|
101
|
+
it 'should keep order of inherited attributes' do
|
102
|
+
C.attributes.keys.should == [:a, :b, :c]
|
103
|
+
end
|
104
|
+
end
|
105
|
+
|
106
|
+
raise_if_defined(:Foo, :Bar, :A, :B, :C)
|