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.
Files changed (67) hide show
  1. data/.gitignore +2 -0
  2. data/History.txt +37 -0
  3. data/Manifest.txt +66 -0
  4. data/README.rdoc +2 -0
  5. data/Rakefile +50 -0
  6. data/lib/ServeME.rb +7 -0
  7. data/lib/TestServe.rb +9 -0
  8. data/lib/bsearch.rb +120 -0
  9. data/lib/builtinME.rb +626 -0
  10. data/lib/date/format.rb +1339 -0
  11. data/lib/date.rb +1792 -0
  12. data/lib/dateME.rb +24 -0
  13. data/lib/erb.rb +896 -0
  14. data/lib/find.rb +81 -0
  15. data/lib/rational.rb +19 -0
  16. data/lib/rationalME.rb +530 -0
  17. data/lib/rho/render.rb +51 -0
  18. data/lib/rho/rho.rb +255 -0
  19. data/lib/rho/rhoapplication.rb +36 -0
  20. data/lib/rho/rhocontact.rb +110 -0
  21. data/lib/rho/rhocontroller.rb +35 -0
  22. data/lib/rho/rhofsconnector.rb +32 -0
  23. data/lib/rho/rhosupport.rb +146 -0
  24. data/lib/rho/rhoviewhelpers.rb +130 -0
  25. data/lib/rho.rb +1 -0
  26. data/lib/rhodes-framework.rb +2 -0
  27. data/lib/rhodes.rb +9 -0
  28. data/lib/rhoframework.rb +38 -0
  29. data/lib/rhofsconnector.rb +1 -0
  30. data/lib/rhom/rhom.rb +58 -0
  31. data/lib/rhom/rhom_db_adapter.rb +185 -0
  32. data/lib/rhom/rhom_db_adapterME.rb +93 -0
  33. data/lib/rhom/rhom_object.rb +69 -0
  34. data/lib/rhom/rhom_object_factory.rb +309 -0
  35. data/lib/rhom/rhom_source.rb +60 -0
  36. data/lib/rhom.rb +1 -0
  37. data/lib/singleton.rb +137 -0
  38. data/lib/time.rb +489 -0
  39. data/lib/version.rb +8 -0
  40. data/res/sqlite3/constants.rb +49 -0
  41. data/res/sqlite3/database.rb +715 -0
  42. data/res/sqlite3/driver/dl/api.rb +154 -0
  43. data/res/sqlite3/driver/dl/driver.rb +307 -0
  44. data/res/sqlite3/driver/native/driver.rb +257 -0
  45. data/res/sqlite3/errors.rb +68 -0
  46. data/res/sqlite3/pragmas.rb +271 -0
  47. data/res/sqlite3/resultset.rb +176 -0
  48. data/res/sqlite3/sqlite3_api.rb +0 -0
  49. data/res/sqlite3/statement.rb +230 -0
  50. data/res/sqlite3/translator.rb +109 -0
  51. data/res/sqlite3/value.rb +57 -0
  52. data/res/sqlite3/version.rb +14 -0
  53. data/rhodes-framework.gemspec +18 -0
  54. data/rhodes.gemspec +18 -0
  55. data/spec/app_manifest.txt +4 -0
  56. data/spec/configs/account.rb +3 -0
  57. data/spec/configs/case.rb +3 -0
  58. data/spec/configs/employee.rb +3 -0
  59. data/spec/rho_controller_spec.rb +144 -0
  60. data/spec/rho_spec.rb +75 -0
  61. data/spec/rhom_object_factory_spec.rb +372 -0
  62. data/spec/rhom_spec.rb +45 -0
  63. data/spec/spec.opts +1 -0
  64. data/spec/spec_helper.rb +49 -0
  65. data/spec/stubs.rb +39 -0
  66. data/spec/syncdbtest.sqlite +0 -0
  67. 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