rhodes-framework 1.0.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 +37 -0
- data/Manifest.txt +66 -0
- data/README.rdoc +2 -0
- data/Rakefile +50 -0
- data/lib/ServeME.rb +7 -0
- data/lib/TestServe.rb +9 -0
- data/lib/bsearch.rb +120 -0
- data/lib/builtinME.rb +626 -0
- data/lib/date/format.rb +1339 -0
- data/lib/date.rb +1792 -0
- data/lib/dateME.rb +24 -0
- data/lib/erb.rb +896 -0
- data/lib/find.rb +81 -0
- data/lib/rational.rb +19 -0
- data/lib/rationalME.rb +530 -0
- data/lib/rho/render.rb +51 -0
- data/lib/rho/rho.rb +255 -0
- data/lib/rho/rhoapplication.rb +36 -0
- data/lib/rho/rhocontact.rb +110 -0
- data/lib/rho/rhocontroller.rb +35 -0
- data/lib/rho/rhofsconnector.rb +32 -0
- data/lib/rho/rhosupport.rb +146 -0
- data/lib/rho/rhoviewhelpers.rb +130 -0
- data/lib/rho.rb +1 -0
- data/lib/rhodes-framework.rb +2 -0
- data/lib/rhodes.rb +9 -0
- data/lib/rhoframework.rb +38 -0
- data/lib/rhofsconnector.rb +1 -0
- data/lib/rhom/rhom.rb +58 -0
- data/lib/rhom/rhom_db_adapter.rb +185 -0
- data/lib/rhom/rhom_db_adapterME.rb +93 -0
- data/lib/rhom/rhom_object.rb +69 -0
- data/lib/rhom/rhom_object_factory.rb +309 -0
- data/lib/rhom/rhom_source.rb +60 -0
- data/lib/rhom.rb +1 -0
- data/lib/singleton.rb +137 -0
- data/lib/time.rb +489 -0
- data/lib/version.rb +8 -0
- data/res/sqlite3/constants.rb +49 -0
- data/res/sqlite3/database.rb +715 -0
- data/res/sqlite3/driver/dl/api.rb +154 -0
- data/res/sqlite3/driver/dl/driver.rb +307 -0
- data/res/sqlite3/driver/native/driver.rb +257 -0
- data/res/sqlite3/errors.rb +68 -0
- data/res/sqlite3/pragmas.rb +271 -0
- data/res/sqlite3/resultset.rb +176 -0
- data/res/sqlite3/sqlite3_api.rb +0 -0
- data/res/sqlite3/statement.rb +230 -0
- data/res/sqlite3/translator.rb +109 -0
- data/res/sqlite3/value.rb +57 -0
- data/res/sqlite3/version.rb +14 -0
- data/rhodes-framework.gemspec +18 -0
- data/rhodes.gemspec +18 -0
- data/spec/app_manifest.txt +4 -0
- data/spec/configs/account.rb +3 -0
- data/spec/configs/case.rb +3 -0
- data/spec/configs/employee.rb +3 -0
- data/spec/rho_controller_spec.rb +144 -0
- data/spec/rho_spec.rb +75 -0
- data/spec/rhom_object_factory_spec.rb +372 -0
- data/spec/rhom_spec.rb +45 -0
- data/spec/spec.opts +1 -0
- data/spec/spec_helper.rb +49 -0
- data/spec/stubs.rb +39 -0
- data/spec/syncdbtest.sqlite +0 -0
- metadata +202 -0
@@ -0,0 +1,309 @@
|
|
1
|
+
#
|
2
|
+
# rhom_object_factory.rb
|
3
|
+
# rhodes
|
4
|
+
# Returns an array of RhomObjects
|
5
|
+
#
|
6
|
+
# Copyright (C) 2008 Rhomobile, Inc. 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
|
+
require 'rho/rhosupport'
|
24
|
+
|
25
|
+
module Rhom
|
26
|
+
class RhomObjectFactory
|
27
|
+
|
28
|
+
def initialize
|
29
|
+
unless not defined? Rho::RhoConfig::sources
|
30
|
+
init_objects
|
31
|
+
end
|
32
|
+
end
|
33
|
+
|
34
|
+
# Initialize new object with dynamic attributes
|
35
|
+
def init_objects
|
36
|
+
Rho::RhoConfig::sources.each do |classname,source|
|
37
|
+
unless Object::const_defined?(classname.intern)
|
38
|
+
Object::const_set(classname.intern,
|
39
|
+
Class::new do
|
40
|
+
include ::Rhom::RhomObject
|
41
|
+
extend ::Rhom::RhomObject
|
42
|
+
|
43
|
+
def initialize(obj=nil)
|
44
|
+
self.send("object=".to_sym(), "#{Time.now.to_i}")
|
45
|
+
if obj
|
46
|
+
self.send("source_id=".to_sym(), obj['source_id'].to_s)
|
47
|
+
self.send("update_type=".to_sym(), 'create')
|
48
|
+
obj.each do |key,value|
|
49
|
+
val = self.inst_strip_braces(value)
|
50
|
+
self.send("#{key}=".to_sym(), val)
|
51
|
+
end
|
52
|
+
end
|
53
|
+
|
54
|
+
end
|
55
|
+
|
56
|
+
class << self
|
57
|
+
|
58
|
+
def count
|
59
|
+
::Rhom::RhomDbAdapter::select_from_table(::Rhom::TABLE_NAME,
|
60
|
+
'object',
|
61
|
+
{"source_id"=>get_source_id},
|
62
|
+
{"distinct"=>true}).length
|
63
|
+
end
|
64
|
+
|
65
|
+
def get_source_id
|
66
|
+
Rho::RhoConfig::sources[self.name.to_s]['source_id'].to_s
|
67
|
+
end
|
68
|
+
|
69
|
+
# retrieve a single record if object id provided, otherwise return
|
70
|
+
# full list corresponding to factory's source id
|
71
|
+
def find(*args)
|
72
|
+
list = []
|
73
|
+
hash_list = {}
|
74
|
+
conditions = {}
|
75
|
+
|
76
|
+
# first find all query objects
|
77
|
+
if args.first == :all
|
78
|
+
conditions = {"source_id"=>get_source_id}
|
79
|
+
elsif args.first.is_a?(String)
|
80
|
+
conditions = {"object"=>strip_braces(args.first.to_s)}
|
81
|
+
end
|
82
|
+
|
83
|
+
# do we have conditions?
|
84
|
+
# if so, add them to the query
|
85
|
+
condition_hash = {}
|
86
|
+
condition_hash = get_conditions_hash(args[1][:conditions]) if args[1] and args[1][:conditions] and args[1][:conditions].is_a?(Hash)
|
87
|
+
conditions.merge!(condition_hash)
|
88
|
+
|
89
|
+
# process query, create, and update lists in order
|
90
|
+
["query", "create", "update"].each do |update_type|
|
91
|
+
conditions.merge!({"update_type"=>update_type})
|
92
|
+
objs = ::Rhom::RhomDbAdapter::select_from_table(::Rhom::TABLE_NAME, '*', conditions, {"order by"=>'object'})
|
93
|
+
|
94
|
+
# fetch the rest of the attributes if we're searching by specific attrib value
|
95
|
+
if condition_hash and condition_hash.size > 0
|
96
|
+
full_objects = []
|
97
|
+
objs.each do |obj|
|
98
|
+
full_objects += ::Rhom::RhomDbAdapter::select_from_table(::Rhom::TABLE_NAME, '*', {'object' => obj['object'].to_s})
|
99
|
+
end
|
100
|
+
objs = full_objects
|
101
|
+
end
|
102
|
+
|
103
|
+
# build up the object array where each
|
104
|
+
# row in this array is a rhom_object
|
105
|
+
objs.collect! do |obj|
|
106
|
+
object = obj['object']
|
107
|
+
attrib = obj['attrib']
|
108
|
+
value = obj['value']
|
109
|
+
hash_list[object] = get_new_obj(obj) if not hash_list[object]
|
110
|
+
if not method_name_reserved?(attrib) and hash_list[object].send(attrib.to_sym)
|
111
|
+
hash_list[object].remove_var(attrib)
|
112
|
+
end
|
113
|
+
hash_list[object].send("#{attrib}=".to_sym(), value) if not method_name_reserved?(attrib)
|
114
|
+
nil # remove the element from the array
|
115
|
+
end
|
116
|
+
|
117
|
+
end
|
118
|
+
|
119
|
+
# convert hash to array
|
120
|
+
list = hash_list.values
|
121
|
+
hash_list = nil
|
122
|
+
|
123
|
+
# setup order by if provided
|
124
|
+
order = extract_options(args)
|
125
|
+
order_value = order[:order] if order and order[:order]
|
126
|
+
if order_value
|
127
|
+
order_sym = order_value.to_sym
|
128
|
+
list.sort! {|x,y| x.send(order_sym) && y.send(order_sym) ? x.send(order_sym) <=> y.send(order_sym) : 0}
|
129
|
+
end
|
130
|
+
|
131
|
+
# return a single rhom object if searching for one
|
132
|
+
if args.first == :first or args.first.is_a?(String)
|
133
|
+
return list[0]
|
134
|
+
end
|
135
|
+
list
|
136
|
+
end
|
137
|
+
|
138
|
+
def find_all(args={})
|
139
|
+
find(args)
|
140
|
+
end
|
141
|
+
|
142
|
+
def set_notification(url,params)
|
143
|
+
SyncEngine.set_notification(get_source_id.to_i,url,params)
|
144
|
+
end
|
145
|
+
|
146
|
+
def clear_notification
|
147
|
+
SyncEngine.clear_notification(get_source_id.to_i)
|
148
|
+
end
|
149
|
+
|
150
|
+
def ask(question)
|
151
|
+
tmp_obj = get_new_obj(:object =>djb_hash("#{question}#{rand.to_s}", 10).to_s)
|
152
|
+
if question
|
153
|
+
# We only support one ask at a time!
|
154
|
+
::Rhom::RhomDbAdapter::delete_from_table(::Rhom::TABLE_NAME,
|
155
|
+
{"source_id"=>get_source_id,
|
156
|
+
"update_type"=>'ask'})
|
157
|
+
::Rhom::RhomDbAdapter::insert_into_table(::Rhom::TABLE_NAME,
|
158
|
+
{"source_id"=>get_source_id,
|
159
|
+
"object"=>tmp_obj.object,
|
160
|
+
"attrib"=>'question',
|
161
|
+
"value"=>Rho::RhoSupport.url_encode(question),
|
162
|
+
"update_type"=>'ask'})
|
163
|
+
SyncEngine::dosync
|
164
|
+
end
|
165
|
+
end
|
166
|
+
|
167
|
+
# deletes all records matching conditions (optionally nil)
|
168
|
+
def delete_all(conditions=nil)
|
169
|
+
if conditions
|
170
|
+
del_conditions = get_conditions_hash(conditions[:conditions])
|
171
|
+
# find all relevant objects, then delete them
|
172
|
+
del_objects = ::Rhom::RhomDbAdapter::select_from_table(::Rhom::TABLE_NAME,
|
173
|
+
'object',
|
174
|
+
del_conditions.merge!({"source_id"=>get_source_id}),
|
175
|
+
{"distinct"=>true})
|
176
|
+
del_objects.each do |obj|
|
177
|
+
::Rhom::RhomDbAdapter::delete_from_table(::Rhom::TABLE_NAME, {'object'=>obj['object']})
|
178
|
+
end
|
179
|
+
else
|
180
|
+
::Rhom::RhomDbAdapter::delete_from_table(::Rhom::TABLE_NAME, {"source_id"=>get_source_id})
|
181
|
+
end
|
182
|
+
end
|
183
|
+
|
184
|
+
private
|
185
|
+
# returns new model instance with a temp object id
|
186
|
+
def get_new_obj(obj)
|
187
|
+
tmp_obj = self.new
|
188
|
+
tmp_obj.send("object=".to_sym(), "{#{obj['object'].to_s}}")
|
189
|
+
tmp_obj
|
190
|
+
end
|
191
|
+
|
192
|
+
# get hash of conditions in sql form
|
193
|
+
def get_conditions_hash(conditions=nil)
|
194
|
+
if conditions
|
195
|
+
condition_hash = {}
|
196
|
+
conditions.each do |key,value|
|
197
|
+
condition_hash.merge!('attrib' => key.to_s, 'value' => value.to_s)
|
198
|
+
end
|
199
|
+
condition_hash
|
200
|
+
else
|
201
|
+
nil
|
202
|
+
end
|
203
|
+
end
|
204
|
+
end #class methods
|
205
|
+
|
206
|
+
# deletes the record from the viewable list as well as
|
207
|
+
# adding a delete record to the list of sync operations
|
208
|
+
def destroy
|
209
|
+
result = nil
|
210
|
+
obj = self.inst_strip_braces(self.object)
|
211
|
+
update_type=self.get_update_type_by_source('delete')
|
212
|
+
if obj
|
213
|
+
# first delete the record from viewable list
|
214
|
+
result = ::Rhom::RhomDbAdapter::delete_from_table(::Rhom::TABLE_NAME,
|
215
|
+
{"object"=>obj})
|
216
|
+
if update_type
|
217
|
+
# now add delete operation
|
218
|
+
result = ::Rhom::RhomDbAdapter::insert_into_table(::Rhom::TABLE_NAME,
|
219
|
+
{"source_id"=>self.get_inst_source_id,
|
220
|
+
"object"=>obj,
|
221
|
+
"update_type"=>update_type})
|
222
|
+
end
|
223
|
+
end
|
224
|
+
result
|
225
|
+
end
|
226
|
+
|
227
|
+
# saves the current object to the database as a create type
|
228
|
+
def save
|
229
|
+
result = nil
|
230
|
+
# iterate over each instance variable and insert create row to table
|
231
|
+
obj = self.inst_strip_braces(self.object)
|
232
|
+
update_type=self.get_update_type_by_source('create')
|
233
|
+
self.instance_variables.each do |method|
|
234
|
+
method = method.to_s.gsub(/@/,"")
|
235
|
+
# Don't save objects with braces to database
|
236
|
+
val = self.inst_strip_braces(self.send(method.to_sym))
|
237
|
+
# add rows excluding object, source_id and update_type
|
238
|
+
unless self.method_name_reserved?(method) or val.nil?
|
239
|
+
fields = {"source_id"=>self.get_inst_source_id,
|
240
|
+
"object"=>obj,
|
241
|
+
"attrib"=>method,
|
242
|
+
"value"=>val,
|
243
|
+
"update_type"=>update_type}
|
244
|
+
fields = method == "image_uri" ? fields.merge!({"attrib_type" => "blob.file"}) : fields
|
245
|
+
result = ::Rhom::RhomDbAdapter::insert_into_table(::Rhom::TABLE_NAME, fields)
|
246
|
+
end
|
247
|
+
end
|
248
|
+
result
|
249
|
+
end
|
250
|
+
|
251
|
+
# updates the current record in the viewable list and adds
|
252
|
+
# a sync operation to update
|
253
|
+
def update_attributes(attrs)
|
254
|
+
result = nil
|
255
|
+
obj = self.inst_strip_braces(self.object)
|
256
|
+
update_type=self.get_update_type_by_source('update')
|
257
|
+
attrs.each do |attrib,val|
|
258
|
+
attrib = attrib.to_s.gsub(/@/,"")
|
259
|
+
old_val = self.send attrib.to_sym unless self.method_name_reserved?(attrib)
|
260
|
+
|
261
|
+
# Don't save objects with braces to database
|
262
|
+
new_val = self.inst_strip_braces(val)
|
263
|
+
|
264
|
+
# if the object's value doesn't match the database record
|
265
|
+
# then we procede with update
|
266
|
+
if new_val and old_val != new_val
|
267
|
+
unless self.method_name_reserved?(attrib) or new_val.length == 0
|
268
|
+
::Rhom::RhomDbAdapter::delete_from_table(::Rhom::TABLE_NAME,
|
269
|
+
{"source_id"=>self.get_inst_source_id,
|
270
|
+
"object"=>obj,
|
271
|
+
"attrib"=>attrib,
|
272
|
+
"update_type"=>update_type})
|
273
|
+
# update sync list
|
274
|
+
result = ::Rhom::RhomDbAdapter::insert_into_table(::Rhom::TABLE_NAME,
|
275
|
+
{"source_id"=>self.get_inst_source_id,
|
276
|
+
"object"=>obj,
|
277
|
+
"attrib"=>attrib,
|
278
|
+
"value"=>new_val,
|
279
|
+
"update_type"=>update_type})
|
280
|
+
end
|
281
|
+
end
|
282
|
+
end
|
283
|
+
result
|
284
|
+
end
|
285
|
+
|
286
|
+
def get_inst_source_id
|
287
|
+
Rho::RhoConfig::sources[self.class.name.to_s]['source_id'].to_s
|
288
|
+
end
|
289
|
+
|
290
|
+
def get_update_type_by_source(update_type)
|
291
|
+
source_type = Rho::RhoConfig::sources[self.class.name.to_s]['type']
|
292
|
+
if source_type and source_type == "ask" and update_type == 'delete'
|
293
|
+
nil
|
294
|
+
elsif source_type and source_type == "ask"
|
295
|
+
"query"
|
296
|
+
else
|
297
|
+
update_type
|
298
|
+
end
|
299
|
+
end
|
300
|
+
|
301
|
+
def inst_strip_braces(str=nil)
|
302
|
+
str ? str.gsub(/\{/,"").gsub(/\}/,"") : nil
|
303
|
+
end
|
304
|
+
end)
|
305
|
+
end
|
306
|
+
end
|
307
|
+
end
|
308
|
+
end # RhomObjectFactory
|
309
|
+
end # Rhom
|
@@ -0,0 +1,60 @@
|
|
1
|
+
require 'time'
|
2
|
+
require 'rhom/rhom_object'
|
3
|
+
|
4
|
+
module Rhom
|
5
|
+
class RhomSource
|
6
|
+
include RhomObject
|
7
|
+
attr_accessor :source_url
|
8
|
+
attr_reader :source_id, :name, :last_updated, :last_inserted_size,
|
9
|
+
:last_deleted_size, :last_sync_duration,
|
10
|
+
:last_sync_success, :distinct_objects
|
11
|
+
|
12
|
+
def initialize(args,count=0)
|
13
|
+
# setup the name
|
14
|
+
# TODO: should really store this in the database
|
15
|
+
Rho::RhoConfig::sources.each do |key,value|
|
16
|
+
if value['source_id'].to_i == args['source_id'].to_i
|
17
|
+
@name = key
|
18
|
+
end
|
19
|
+
end
|
20
|
+
@source_id = args['source_id'].to_i
|
21
|
+
@source_url = args['source_url']
|
22
|
+
@last_updated = Time.at(args['last_updated'].to_i).to_s
|
23
|
+
@last_inserted_size = args['last_inserted_size'].to_i
|
24
|
+
@last_deleted_size = args['last_deleted_size'].to_i
|
25
|
+
@last_sync_duration = args['last_sync_duration'].to_i
|
26
|
+
@last_sync_success = args['last_sync_success'].to_i == 1 ? true : false
|
27
|
+
@distinct_objects = ::Rhom::RhomDbAdapter::select_from_table(
|
28
|
+
::Rhom::TABLE_NAME,
|
29
|
+
'object',
|
30
|
+
{"source_id"=>@source_id},
|
31
|
+
{"distinct"=>true}).length
|
32
|
+
end
|
33
|
+
|
34
|
+
class << self
|
35
|
+
include RhomObject
|
36
|
+
def find(*args)
|
37
|
+
list = []
|
38
|
+
if args.first == :all
|
39
|
+
results = ::Rhom::RhomDbAdapter::select_from_table('sources', '*')
|
40
|
+
|
41
|
+
results.each do |result|
|
42
|
+
list << RhomSource.new(result)
|
43
|
+
end
|
44
|
+
else
|
45
|
+
result = ::Rhom::RhomDbAdapter::select_from_table('sources', '*',
|
46
|
+
{"source_id" => strip_braces(args.first)}).first
|
47
|
+
list << RhomSource.new(result)
|
48
|
+
end
|
49
|
+
list.size > 1 ? list : list[0]
|
50
|
+
end
|
51
|
+
end
|
52
|
+
|
53
|
+
def update_attributes(params=nil)
|
54
|
+
if params
|
55
|
+
::Rhom::RhomDbAdapter::update_into_table('sources', {"source_url"=>params['source_url']},
|
56
|
+
{"source_id"=>strip_braces(params['source_id'])})
|
57
|
+
end
|
58
|
+
end
|
59
|
+
end
|
60
|
+
end
|
data/lib/rhom.rb
ADDED
@@ -0,0 +1 @@
|
|
1
|
+
require 'rhom/rhom'
|
data/lib/singleton.rb
ADDED
@@ -0,0 +1,137 @@
|
|
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
|