rhodes 0.1.0
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/.gitignore +2 -0
- data/History.txt +4 -0
- data/Manifest.txt +52 -0
- data/README.rdoc +2 -0
- data/Rakefile +33 -0
- data/bin/rhogen +8 -0
- data/generators/rhogen.rb +99 -0
- data/generators/templates/application/application.rb +4 -0
- data/generators/templates/application/index.html +25 -0
- data/generators/templates/model/config.rb +3 -0
- data/generators/templates/model/controller.rb +48 -0
- data/generators/templates/model/edit.erb +21 -0
- data/generators/templates/model/index.erb +10 -0
- data/generators/templates/model/new.erb +16 -0
- data/lib/ServeME.rb +7 -0
- data/lib/TestServe.rb +9 -0
- data/lib/builtinME.rb +481 -0
- data/lib/date.rb +1781 -0
- data/lib/date/format.rb +1313 -0
- data/lib/erb.rb +896 -0
- data/lib/find.rb +81 -0
- data/lib/rational.rb +19 -0
- data/lib/rho.rb +1 -0
- data/lib/rho/render.rb +9 -0
- data/lib/rho/renderME.rb +8 -0
- data/lib/rho/rho.rb +173 -0
- data/lib/rho/rhoapplication.rb +36 -0
- data/lib/rho/rhocontroller.rb +51 -0
- data/lib/rho/rhofsconnector.rb +39 -0
- data/lib/rho/rhofsconnectorME.rb +36 -0
- data/lib/rho/rhosupport.rb +139 -0
- data/lib/rhodes.rb +5 -0
- data/lib/rhofsconnector.rb +5 -0
- data/lib/rhom.rb +1 -0
- data/lib/rhom/rhom.rb +41 -0
- data/lib/rhom/rhom_db_adapter.rb +183 -0
- data/lib/rhom/rhom_db_adapterME.rb +91 -0
- data/lib/rhom/rhom_object.rb +53 -0
- data/lib/rhom/rhom_object_factory.rb +246 -0
- data/lib/singleton.rb +313 -0
- data/lib/time.rb +839 -0
- data/rhodes.gemspec +18 -0
- data/spec/app_generator_spec.rb +27 -0
- data/spec/generator_spec_helper.rb +12 -0
- data/spec/model_generator_spec.rb +28 -0
- data/spec/rho_spec.rb +28 -0
- data/spec/rhom_object_factory_spec.rb +147 -0
- data/spec/spec.opts +1 -0
- data/spec/spec_helper.rb +14 -0
- data/spec/stubs.rb +17 -0
- data/spec/syncdbtest.sqlite +0 -0
- data/tasks/rspec.rake +34 -0
- metadata +157 -0
@@ -0,0 +1,53 @@
|
|
1
|
+
#
|
2
|
+
# rhom_object.rb
|
3
|
+
# rhodes
|
4
|
+
#
|
5
|
+
# Copyright (C) 2008 Lars Burgess. All rights reserved.
|
6
|
+
#
|
7
|
+
# This program is free software: you can redistribute it and/or modify
|
8
|
+
# it under the terms of the GNU General Public License as published by
|
9
|
+
# the Free Software Foundation, either version 3 of the License, or
|
10
|
+
# (at your option) any later version.
|
11
|
+
#
|
12
|
+
# This program is distributed in the hope that it will be useful,
|
13
|
+
# but WITHOUT ANY WARRANTY; without even the implied warranty of
|
14
|
+
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
15
|
+
# GNU General Public License for more details.
|
16
|
+
#
|
17
|
+
# You should have received a copy of the GNU General Public License
|
18
|
+
# along with this program. If not, see <http://www.gnu.org/licenses/>.
|
19
|
+
#
|
20
|
+
module Rhom
|
21
|
+
module RhomObject
|
22
|
+
# defines a method at runtime for the
|
23
|
+
# dynamically created class
|
24
|
+
|
25
|
+
|
26
|
+
# we override method_missing here so that instance variables,
|
27
|
+
# when retrieved or set, are added to the object
|
28
|
+
def method_missing(name, *args)
|
29
|
+
unless name == Fixnum
|
30
|
+
varname = name.to_s.gsub(/=/,"")
|
31
|
+
if instance_variable_defined? "@#{varname}"
|
32
|
+
#TODO: Figure out why this returns an array
|
33
|
+
instance_variable_get( "@#{varname}" )[0]
|
34
|
+
else
|
35
|
+
instance_variable_set( "@#{varname}", args )
|
36
|
+
end
|
37
|
+
end
|
38
|
+
end
|
39
|
+
|
40
|
+
def strip_braces(str=nil)
|
41
|
+
str ? str.gsub(/\{/,"").gsub(/\}/,"") : nil
|
42
|
+
end
|
43
|
+
|
44
|
+
# use djb hash function to generate temp object id
|
45
|
+
def djb_hash(str, len)
|
46
|
+
hash = 5381
|
47
|
+
for i in (0..len)
|
48
|
+
hash = ((hash << 5) + hash) + str[i].to_i
|
49
|
+
end
|
50
|
+
return hash
|
51
|
+
end
|
52
|
+
end # RhomObject
|
53
|
+
end # Rhom
|
@@ -0,0 +1,246 @@
|
|
1
|
+
#
|
2
|
+
# rhom_object_factory.rb
|
3
|
+
# rhodes
|
4
|
+
# Returns an array of RhomObjects
|
5
|
+
#
|
6
|
+
# Copyright (C) 2008 Lars Burgess. All rights reserved.
|
7
|
+
#
|
8
|
+
# This program is free software: you can redistribute it and/or modify
|
9
|
+
# it under the terms of the GNU General Public License as published by
|
10
|
+
# the Free Software Foundation, either version 3 of the License, or
|
11
|
+
# (at your option) any later version.
|
12
|
+
#
|
13
|
+
# This program is distributed in the hope that it will be useful,
|
14
|
+
# but WITHOUT ANY WARRANTY; without even the implied warranty of
|
15
|
+
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
16
|
+
# GNU General Public License for more details.
|
17
|
+
#
|
18
|
+
# You should have received a copy of the GNU General Public License
|
19
|
+
# along with this program. If not, see <http://www.gnu.org/licenses/>.
|
20
|
+
#
|
21
|
+
require 'rhom'
|
22
|
+
require 'rho'
|
23
|
+
|
24
|
+
module Rhom
|
25
|
+
class RhomObjectFactory
|
26
|
+
|
27
|
+
def initialize
|
28
|
+
unless not defined? Rho::RhoConfig::sources
|
29
|
+
init_source_attribs
|
30
|
+
init_objects
|
31
|
+
end
|
32
|
+
end
|
33
|
+
|
34
|
+
def init_source_attribs
|
35
|
+
# merge source attributes into config hash
|
36
|
+
# TODO: This shouldn't reference 'source[1]' directly
|
37
|
+
Rho::RhoConfig::sources.each do |source|
|
38
|
+
src_attribs = ::Rhom::RhomDbAdapter::select_from_table(::Rhom::TABLE_NAME,
|
39
|
+
'attrib',
|
40
|
+
{"source_id"=>source[1]['source_id'].to_s},
|
41
|
+
{"distinct"=>true})
|
42
|
+
# update our source with the proper attributes
|
43
|
+
source[1].merge!({"attribs"=>src_attribs})
|
44
|
+
end
|
45
|
+
end
|
46
|
+
|
47
|
+
# Initialize new object with dynamic attributes
|
48
|
+
def init_objects
|
49
|
+
Rho::RhoConfig::sources.each do |classname,source|
|
50
|
+
unless Object::const_defined?(classname.intern)
|
51
|
+
Object::const_set(classname.intern,
|
52
|
+
Class::new do
|
53
|
+
include ::Rhom::RhomObject
|
54
|
+
extend ::Rhom::RhomObject
|
55
|
+
|
56
|
+
def initialize(obj=nil)
|
57
|
+
if obj
|
58
|
+
# create a temp id for the create type
|
59
|
+
# TODO: This is duplicative of get_new_obj
|
60
|
+
temp_objid = djb_hash(obj.values.to_s, 10).to_s
|
61
|
+
self.send 'object'.to_sym, "#{temp_objid}"
|
62
|
+
self.send 'source_id'.to_sym, obj['source_id'].to_s
|
63
|
+
self.send 'update_type'.to_sym, 'create'
|
64
|
+
obj.each do |key,value|
|
65
|
+
val = self.inst_strip_braces(value)
|
66
|
+
self.send key, val
|
67
|
+
end
|
68
|
+
end
|
69
|
+
|
70
|
+
end
|
71
|
+
|
72
|
+
class << self
|
73
|
+
|
74
|
+
def get_source_id
|
75
|
+
Rho::RhoConfig::sources[self.name.to_s]['source_id'].to_s
|
76
|
+
end
|
77
|
+
# retrieve a single record if object id provided, otherwise return
|
78
|
+
# full list corresponding to factory's source id
|
79
|
+
def find(*args)
|
80
|
+
list = []
|
81
|
+
if args.first == :all
|
82
|
+
result = ::Rhom::RhomDbAdapter::select_from_table(::Rhom::TABLE_NAME,
|
83
|
+
'*',
|
84
|
+
{"source_id"=>get_source_id,"update_type"=>'query'},
|
85
|
+
{"order by"=>'object'})
|
86
|
+
else
|
87
|
+
obj = strip_braces(args.first.to_s)
|
88
|
+
result = ::Rhom::RhomDbAdapter::select_from_table(::Rhom::TABLE_NAME,
|
89
|
+
'*',
|
90
|
+
{"object"=>obj})
|
91
|
+
end
|
92
|
+
list = get_list(result)
|
93
|
+
if list.length == 1
|
94
|
+
return list[0]
|
95
|
+
end
|
96
|
+
list
|
97
|
+
end
|
98
|
+
|
99
|
+
def find_by(*args)
|
100
|
+
# TODO: implement
|
101
|
+
end
|
102
|
+
|
103
|
+
# returns an array of objects based on an existing array
|
104
|
+
def get_list(objs)
|
105
|
+
new_list = []
|
106
|
+
if objs and defined? Rho::RhoConfig::sources[self.name.to_s]
|
107
|
+
attrib_length = Rho::RhoConfig::sources[self.name.to_s]['attribs'].length
|
108
|
+
list_length = 0
|
109
|
+
list_length = (objs.length / attrib_length) unless attrib_length == 0
|
110
|
+
new_obj = nil
|
111
|
+
# iterate over the array and determine object
|
112
|
+
# structure based on attribute/value pairs
|
113
|
+
list_length.times do |i|
|
114
|
+
new_obj = get_new_obj(objs[i*attrib_length])
|
115
|
+
attrib_length.times do |j|
|
116
|
+
# setup index and assign accessors
|
117
|
+
idx = i*attrib_length+j
|
118
|
+
begin
|
119
|
+
# only update attributes if they belong
|
120
|
+
# to the current object
|
121
|
+
if objs[idx]['object'] == strip_braces((new_obj.send 'object'.to_sym))
|
122
|
+
attrib = objs[idx]['attrib'].to_s
|
123
|
+
value = objs[idx]['value'].to_s
|
124
|
+
new_obj.send attrib.to_sym, value
|
125
|
+
end
|
126
|
+
rescue
|
127
|
+
puts "failed to reference objs[#{idx}]..."
|
128
|
+
end
|
129
|
+
end
|
130
|
+
new_list << new_obj
|
131
|
+
end
|
132
|
+
else
|
133
|
+
# source attributes are not initialized,
|
134
|
+
# try again
|
135
|
+
RhomObjectFactory::init_sources
|
136
|
+
end
|
137
|
+
new_list
|
138
|
+
end
|
139
|
+
|
140
|
+
# returns new model instance with a temp object id
|
141
|
+
def get_new_obj(obj, type='query')
|
142
|
+
tmp_obj = self.new
|
143
|
+
tmp_obj.send 'object'.to_sym, "{#{obj['object'].to_s}}"
|
144
|
+
tmp_obj.send 'source_id'.to_sym, get_source_id
|
145
|
+
tmp_obj.send 'update_type'.to_sym, type
|
146
|
+
tmp_obj
|
147
|
+
end
|
148
|
+
end #class methods
|
149
|
+
|
150
|
+
# deletes the record from the viewable list as well as
|
151
|
+
# adding a delete record to the list of sync operations
|
152
|
+
def destroy
|
153
|
+
result = nil
|
154
|
+
obj = self.inst_strip_braces(self.object)
|
155
|
+
if obj
|
156
|
+
# first delete the record from viewable list
|
157
|
+
result = ::Rhom::RhomDbAdapter::delete_from_table(::Rhom::TABLE_NAME,
|
158
|
+
{"object"=>obj})
|
159
|
+
# now add delete operation
|
160
|
+
result = ::Rhom::RhomDbAdapter::insert_into_table(::Rhom::TABLE_NAME,
|
161
|
+
{"source_id"=>self.get_inst_source_id,
|
162
|
+
"object"=>obj,
|
163
|
+
"update_type"=>'delete'})
|
164
|
+
end
|
165
|
+
result
|
166
|
+
end
|
167
|
+
|
168
|
+
# saves the current object to the database as a create type
|
169
|
+
def save
|
170
|
+
result = nil
|
171
|
+
# iterate over each instance variable and insert create row to table
|
172
|
+
obj = self.inst_strip_braces(self.object)
|
173
|
+
self.instance_variables.each do |method|
|
174
|
+
method = method.to_s.gsub(/@/,"")
|
175
|
+
# Don't save objects with braces to database
|
176
|
+
val = self.inst_strip_braces(self.send(method.to_sym))
|
177
|
+
# add rows excluding object, source_id and update_type
|
178
|
+
unless self.method_name_reserved?(method) or val.nil?
|
179
|
+
result = ::Rhom::RhomDbAdapter::insert_into_table(::Rhom::TABLE_NAME,
|
180
|
+
{"source_id"=>self.get_inst_source_id,
|
181
|
+
"object"=>obj,
|
182
|
+
"attrib"=>method,
|
183
|
+
"value"=>val,
|
184
|
+
"update_type"=>'create'})
|
185
|
+
end
|
186
|
+
end
|
187
|
+
# Create a temporary query record to display in the list
|
188
|
+
Rho::RhoConfig::sources[self.class.name.to_s]['attribs'].each do |attrib|
|
189
|
+
result = ::Rhom::RhomDbAdapter::insert_into_table(::Rhom::TABLE_NAME,
|
190
|
+
{"source_id"=>self.get_inst_source_id,
|
191
|
+
"object"=>obj,
|
192
|
+
"attrib"=>attrib['attrib'],
|
193
|
+
"value"=>self.send(attrib['attrib'].to_sym),
|
194
|
+
"update_type"=>'query'})
|
195
|
+
end
|
196
|
+
result
|
197
|
+
end
|
198
|
+
|
199
|
+
# updates the current record in the viewable list and adds
|
200
|
+
# a sync operation to update
|
201
|
+
def update_attributes(attrs)
|
202
|
+
result = nil
|
203
|
+
obj = self.inst_strip_braces(self.object)
|
204
|
+
self.instance_variables.each do |method|
|
205
|
+
method = method.to_s.gsub(/@/,"")
|
206
|
+
val = self.send method.to_sym
|
207
|
+
# Don't save objects with braces to database
|
208
|
+
new_val = self.inst_strip_braces(attrs[method])
|
209
|
+
# if the object's value doesn't match the database record
|
210
|
+
# then we procede with update
|
211
|
+
if new_val and val != new_val
|
212
|
+
unless self.method_name_reserved?(method) or new_val.length == 0
|
213
|
+
# update viewable list
|
214
|
+
result = ::Rhom::RhomDbAdapter::update_into_table(::Rhom::TABLE_NAME,
|
215
|
+
{"value"=>new_val},
|
216
|
+
{"object"=>obj, "attrib"=>method})
|
217
|
+
# update sync list
|
218
|
+
result = ::Rhom::RhomDbAdapter::insert_into_table(::Rhom::TABLE_NAME,
|
219
|
+
{"source_id"=>self.get_inst_source_id,
|
220
|
+
"object"=>obj,
|
221
|
+
"attrib"=>method,
|
222
|
+
"value"=>new_val,
|
223
|
+
"update_type"=>'update'})
|
224
|
+
end
|
225
|
+
end
|
226
|
+
end
|
227
|
+
result
|
228
|
+
end
|
229
|
+
|
230
|
+
def get_inst_source_id
|
231
|
+
Rho::RhoConfig::sources[self.class.name.to_s]['source_id'].to_s
|
232
|
+
end
|
233
|
+
|
234
|
+
def inst_strip_braces(str=nil)
|
235
|
+
str ? str.gsub(/\{/,"").gsub(/\}/,"") : nil
|
236
|
+
end
|
237
|
+
|
238
|
+
def method_name_reserved?(method)
|
239
|
+
method =~ /object|source_id|update_type/
|
240
|
+
end
|
241
|
+
end)
|
242
|
+
end
|
243
|
+
end
|
244
|
+
end
|
245
|
+
end # RhomObjectFactory
|
246
|
+
end # Rhom
|
data/lib/singleton.rb
ADDED
@@ -0,0 +1,313 @@
|
|
1
|
+
# The Singleton module implements the Singleton pattern.
|
2
|
+
#
|
3
|
+
# Usage:
|
4
|
+
# class Klass
|
5
|
+
# include Singleton
|
6
|
+
# # ...
|
7
|
+
# end
|
8
|
+
#
|
9
|
+
# * this ensures that only one instance of Klass lets call it
|
10
|
+
# ``the instance'' can be created.
|
11
|
+
#
|
12
|
+
# a,b = Klass.instance, Klass.instance
|
13
|
+
# a == b # => true
|
14
|
+
# a.new # NoMethodError - new is private ...
|
15
|
+
#
|
16
|
+
# * ``The instance'' is created at instantiation time, in other
|
17
|
+
# words the first call of Klass.instance(), thus
|
18
|
+
#
|
19
|
+
# class OtherKlass
|
20
|
+
# include Singleton
|
21
|
+
# # ...
|
22
|
+
# end
|
23
|
+
# ObjectSpace.each_object(OtherKlass){} # => 0.
|
24
|
+
#
|
25
|
+
# * This behavior is preserved under inheritance and cloning.
|
26
|
+
#
|
27
|
+
#
|
28
|
+
#
|
29
|
+
# This is achieved by marking
|
30
|
+
# * Klass.new and Klass.allocate - as private
|
31
|
+
#
|
32
|
+
# Providing (or modifying) the class methods
|
33
|
+
# * Klass.inherited(sub_klass) and Klass.clone() -
|
34
|
+
# to ensure that the Singleton pattern is properly
|
35
|
+
# inherited and cloned.
|
36
|
+
#
|
37
|
+
# * Klass.instance() - returning ``the instance''. After a
|
38
|
+
# successful self modifying (normally the first) call the
|
39
|
+
# method body is a simple:
|
40
|
+
#
|
41
|
+
# def Klass.instance()
|
42
|
+
# return @singleton__instance__
|
43
|
+
# end
|
44
|
+
#
|
45
|
+
# * Klass._load(str) - calling Klass.instance()
|
46
|
+
#
|
47
|
+
# * Klass._instantiate?() - returning ``the instance'' or
|
48
|
+
# nil. This hook method puts a second (or nth) thread calling
|
49
|
+
# Klass.instance() on a waiting loop. The return value
|
50
|
+
# signifies the successful completion or premature termination
|
51
|
+
# of the first, or more generally, current "instantiation thread".
|
52
|
+
#
|
53
|
+
#
|
54
|
+
# The instance method of Singleton are
|
55
|
+
# * clone and dup - raising TypeErrors to prevent cloning or duping
|
56
|
+
#
|
57
|
+
# * _dump(depth) - returning the empty string. Marshalling strips
|
58
|
+
# by default all state information, e.g. instance variables and
|
59
|
+
# taint state, from ``the instance''. Providing custom _load(str)
|
60
|
+
# and _dump(depth) hooks allows the (partially) resurrections of
|
61
|
+
# a previous state of ``the instance''.
|
62
|
+
|
63
|
+
require 'thread'
|
64
|
+
|
65
|
+
module Singleton
|
66
|
+
# disable build-in copying methods
|
67
|
+
def clone
|
68
|
+
raise TypeError, "can't clone instance of singleton #{self.class}"
|
69
|
+
end
|
70
|
+
def dup
|
71
|
+
raise TypeError, "can't dup instance of singleton #{self.class}"
|
72
|
+
end
|
73
|
+
|
74
|
+
# default marshalling strategy
|
75
|
+
def _dump(depth = -1)
|
76
|
+
''
|
77
|
+
end
|
78
|
+
|
79
|
+
module SingletonClassMethods
|
80
|
+
# properly clone the Singleton pattern - did you know
|
81
|
+
# that duping doesn't copy class methods?
|
82
|
+
def clone
|
83
|
+
Singleton.__init__(super)
|
84
|
+
end
|
85
|
+
|
86
|
+
def _load(str)
|
87
|
+
instance
|
88
|
+
end
|
89
|
+
|
90
|
+
private
|
91
|
+
|
92
|
+
# ensure that the Singleton pattern is properly inherited
|
93
|
+
def inherited(sub_klass)
|
94
|
+
super
|
95
|
+
Singleton.__init__(sub_klass)
|
96
|
+
end
|
97
|
+
end
|
98
|
+
|
99
|
+
class << Singleton
|
100
|
+
def __init__(klass)
|
101
|
+
klass.instance_eval {
|
102
|
+
@singleton__instance__ = nil
|
103
|
+
@singleton__mutex__ = Mutex.new
|
104
|
+
}
|
105
|
+
def klass.instance
|
106
|
+
return @singleton__instance__ if @singleton__instance__
|
107
|
+
@singleton__mutex__.synchronize {
|
108
|
+
return @singleton__instance__ if @singleton__instance__
|
109
|
+
@singleton__instance__ = new()
|
110
|
+
}
|
111
|
+
@singleton__instance__
|
112
|
+
end
|
113
|
+
klass
|
114
|
+
end
|
115
|
+
|
116
|
+
private
|
117
|
+
|
118
|
+
# extending an object with Singleton is a bad idea
|
119
|
+
undef_method :extend_object
|
120
|
+
|
121
|
+
def append_features(mod)
|
122
|
+
# help out people counting on transitive mixins
|
123
|
+
unless mod.instance_of?(Class)
|
124
|
+
raise TypeError, "Inclusion of the OO-Singleton module in module #{mod}"
|
125
|
+
end
|
126
|
+
super
|
127
|
+
end
|
128
|
+
|
129
|
+
def included(klass)
|
130
|
+
super
|
131
|
+
klass.private_class_method :new, :allocate
|
132
|
+
klass.extend SingletonClassMethods
|
133
|
+
Singleton.__init__(klass)
|
134
|
+
end
|
135
|
+
end
|
136
|
+
|
137
|
+
end
|
138
|
+
|
139
|
+
|
140
|
+
if __FILE__ == $0
|
141
|
+
|
142
|
+
def num_of_instances(klass)
|
143
|
+
"#{ObjectSpace.each_object(klass){}} #{klass} instance(s)"
|
144
|
+
end
|
145
|
+
|
146
|
+
# The basic and most important example.
|
147
|
+
|
148
|
+
class SomeSingletonClass
|
149
|
+
include Singleton
|
150
|
+
end
|
151
|
+
puts "There are #{num_of_instances(SomeSingletonClass)}"
|
152
|
+
|
153
|
+
a = SomeSingletonClass.instance
|
154
|
+
b = SomeSingletonClass.instance # a and b are same object
|
155
|
+
puts "basic test is #{a == b}"
|
156
|
+
|
157
|
+
begin
|
158
|
+
SomeSingletonClass.new
|
159
|
+
rescue NoMethodError => mes
|
160
|
+
puts mes
|
161
|
+
end
|
162
|
+
|
163
|
+
|
164
|
+
|
165
|
+
puts "\nThreaded example with exception and customized #_instantiate?() hook"; p
|
166
|
+
Thread.abort_on_exception = false
|
167
|
+
|
168
|
+
class Ups < SomeSingletonClass
|
169
|
+
def initialize
|
170
|
+
self.class.__sleep
|
171
|
+
puts "initialize called by thread ##{Thread.current[:i]}"
|
172
|
+
end
|
173
|
+
end
|
174
|
+
|
175
|
+
class << Ups
|
176
|
+
def _instantiate?
|
177
|
+
@enter.push Thread.current[:i]
|
178
|
+
while false.equal?(@singleton__instance__)
|
179
|
+
@singleton__mutex__.unlock
|
180
|
+
sleep 0.08
|
181
|
+
@singleton__mutex__.lock
|
182
|
+
end
|
183
|
+
@leave.push Thread.current[:i]
|
184
|
+
@singleton__instance__
|
185
|
+
end
|
186
|
+
|
187
|
+
def __sleep
|
188
|
+
sleep(rand(0.08))
|
189
|
+
end
|
190
|
+
|
191
|
+
def new
|
192
|
+
begin
|
193
|
+
__sleep
|
194
|
+
raise "boom - thread ##{Thread.current[:i]} failed to create instance"
|
195
|
+
ensure
|
196
|
+
# simple flip-flop
|
197
|
+
class << self
|
198
|
+
remove_method :new
|
199
|
+
end
|
200
|
+
end
|
201
|
+
end
|
202
|
+
|
203
|
+
def instantiate_all
|
204
|
+
@enter = []
|
205
|
+
@leave = []
|
206
|
+
1.upto(9) {|i|
|
207
|
+
Thread.new {
|
208
|
+
begin
|
209
|
+
Thread.current[:i] = i
|
210
|
+
__sleep
|
211
|
+
instance
|
212
|
+
rescue RuntimeError => mes
|
213
|
+
puts mes
|
214
|
+
end
|
215
|
+
}
|
216
|
+
}
|
217
|
+
puts "Before there were #{num_of_instances(self)}"
|
218
|
+
sleep 3
|
219
|
+
puts "Now there is #{num_of_instances(self)}"
|
220
|
+
puts "#{@enter.join '; '} was the order of threads entering the waiting loop"
|
221
|
+
puts "#{@leave.join '; '} was the order of threads leaving the waiting loop"
|
222
|
+
end
|
223
|
+
end
|
224
|
+
|
225
|
+
|
226
|
+
Ups.instantiate_all
|
227
|
+
# results in message like
|
228
|
+
# Before there were 0 Ups instance(s)
|
229
|
+
# boom - thread #6 failed to create instance
|
230
|
+
# initialize called by thread #3
|
231
|
+
# Now there is 1 Ups instance(s)
|
232
|
+
# 3; 2; 1; 8; 4; 7; 5 was the order of threads entering the waiting loop
|
233
|
+
# 3; 2; 1; 7; 4; 8; 5 was the order of threads leaving the waiting loop
|
234
|
+
|
235
|
+
|
236
|
+
puts "\nLets see if class level cloning really works"
|
237
|
+
Yup = Ups.clone
|
238
|
+
def Yup.new
|
239
|
+
begin
|
240
|
+
__sleep
|
241
|
+
raise "boom - thread ##{Thread.current[:i]} failed to create instance"
|
242
|
+
ensure
|
243
|
+
# simple flip-flop
|
244
|
+
class << self
|
245
|
+
remove_method :new
|
246
|
+
end
|
247
|
+
end
|
248
|
+
end
|
249
|
+
Yup.instantiate_all
|
250
|
+
|
251
|
+
|
252
|
+
puts "\n\n","Customized marshalling"
|
253
|
+
class A
|
254
|
+
include Singleton
|
255
|
+
attr_accessor :persist, :die
|
256
|
+
def _dump(depth)
|
257
|
+
# this strips the @die information from the instance
|
258
|
+
Marshal.dump(@persist,depth)
|
259
|
+
end
|
260
|
+
end
|
261
|
+
|
262
|
+
def A._load(str)
|
263
|
+
instance.persist = Marshal.load(str)
|
264
|
+
instance
|
265
|
+
end
|
266
|
+
|
267
|
+
a = A.instance
|
268
|
+
a.persist = ["persist"]
|
269
|
+
a.die = "die"
|
270
|
+
a.taint
|
271
|
+
|
272
|
+
stored_state = Marshal.dump(a)
|
273
|
+
# change state
|
274
|
+
a.persist = nil
|
275
|
+
a.die = nil
|
276
|
+
b = Marshal.load(stored_state)
|
277
|
+
p a == b # => true
|
278
|
+
p a.persist # => ["persist"]
|
279
|
+
p a.die # => nil
|
280
|
+
|
281
|
+
|
282
|
+
puts "\n\nSingleton with overridden default #inherited() hook"
|
283
|
+
class Up
|
284
|
+
end
|
285
|
+
def Up.inherited(sub_klass)
|
286
|
+
puts "#{sub_klass} subclasses #{self}"
|
287
|
+
end
|
288
|
+
|
289
|
+
|
290
|
+
class Middle < Up
|
291
|
+
include Singleton
|
292
|
+
end
|
293
|
+
|
294
|
+
class Down < Middle; end
|
295
|
+
|
296
|
+
puts "and basic \"Down test\" is #{Down.instance == Down.instance}\n
|
297
|
+
Various exceptions"
|
298
|
+
|
299
|
+
begin
|
300
|
+
module AModule
|
301
|
+
include Singleton
|
302
|
+
end
|
303
|
+
rescue TypeError => mes
|
304
|
+
puts mes #=> Inclusion of the OO-Singleton module in module AModule
|
305
|
+
end
|
306
|
+
|
307
|
+
begin
|
308
|
+
'aString'.extend Singleton
|
309
|
+
rescue NoMethodError => mes
|
310
|
+
puts mes #=> undefined method `extend_object' for Singleton:Module
|
311
|
+
end
|
312
|
+
|
313
|
+
end
|