rhosync 2.0.0.beta1

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 (200) hide show
  1. data/CHANGELOG +5 -0
  2. data/LICENSE +674 -0
  3. data/README.md +26 -0
  4. data/Rakefile +109 -0
  5. data/bench/bench +6 -0
  6. data/bench/benchapp/Rakefile +14 -0
  7. data/bench/benchapp/application.rb +13 -0
  8. data/bench/benchapp/config.ru +32 -0
  9. data/bench/benchapp/settings/license.key +1 -0
  10. data/bench/benchapp/settings/settings.yml +18 -0
  11. data/bench/benchapp/sources/mock_adapter.rb +55 -0
  12. data/bench/benchapp/sources/queue_mock_adapter.rb +2 -0
  13. data/bench/benchapp/vendor/rhosync/lib/rhosync.rb +7 -0
  14. data/bench/lib/bench/cli.rb +16 -0
  15. data/bench/lib/bench/logging.rb +18 -0
  16. data/bench/lib/bench/mock_client.rb +41 -0
  17. data/bench/lib/bench/result.rb +50 -0
  18. data/bench/lib/bench/runner.rb +44 -0
  19. data/bench/lib/bench/session.rb +65 -0
  20. data/bench/lib/bench/statistics.rb +56 -0
  21. data/bench/lib/bench/test_data.rb +55 -0
  22. data/bench/lib/bench/timer.rb +10 -0
  23. data/bench/lib/bench/utils.rb +49 -0
  24. data/bench/lib/bench.rb +128 -0
  25. data/bench/lib/testdata/100-data.txt +148 -0
  26. data/bench/lib/testdata/5-data.txt +11 -0
  27. data/bench/scripts/cud_script.rb +77 -0
  28. data/bench/scripts/helpers.rb +101 -0
  29. data/bench/scripts/query_md_script.rb +46 -0
  30. data/bench/scripts/query_script.rb +46 -0
  31. data/bench/spec/bench_spec_helper.rb +65 -0
  32. data/bench/spec/logging_spec.rb +19 -0
  33. data/bench/spec/mock_adapter_spec.rb +61 -0
  34. data/bench/spec/mock_client_spec.rb +64 -0
  35. data/bench/spec/result_spec.rb +59 -0
  36. data/bench/spec/utils_spec.rb +35 -0
  37. data/bin/rhosync +34 -0
  38. data/doc/protocol.html +1901 -0
  39. data/doc/public/css/print.css +29 -0
  40. data/doc/public/css/screen.css +257 -0
  41. data/doc/public/css/style.css +20 -0
  42. data/examples/simple/application.rb +13 -0
  43. data/examples/simple/sources/sample_adapter.rb +5 -0
  44. data/examples/simple/sources/simple_adapter.rb +5 -0
  45. data/examples/simple/vendor/rhosync/lib/rhosync.rb +7 -0
  46. data/generators/rhosync.rb +98 -0
  47. data/generators/templates/application/Rakefile +19 -0
  48. data/generators/templates/application/application.rb +27 -0
  49. data/generators/templates/application/config.ru +33 -0
  50. data/generators/templates/application/settings/license.key +1 -0
  51. data/generators/templates/application/settings/settings.yml +14 -0
  52. data/generators/templates/source/source_adapter.rb +49 -0
  53. data/lib/rhosync/api/create_client.rb +3 -0
  54. data/lib/rhosync/api/create_user.rb +7 -0
  55. data/lib/rhosync/api/delete_client.rb +5 -0
  56. data/lib/rhosync/api/delete_user.rb +5 -0
  57. data/lib/rhosync/api/get_api_token.rb +7 -0
  58. data/lib/rhosync/api/get_client_params.rb +3 -0
  59. data/lib/rhosync/api/get_db_doc.rb +7 -0
  60. data/lib/rhosync/api/get_license_info.rb +7 -0
  61. data/lib/rhosync/api/get_source_params.rb +3 -0
  62. data/lib/rhosync/api/list_client_docs.rb +12 -0
  63. data/lib/rhosync/api/list_clients.rb +3 -0
  64. data/lib/rhosync/api/list_source_docs.rb +10 -0
  65. data/lib/rhosync/api/list_sources.rb +15 -0
  66. data/lib/rhosync/api/list_users.rb +3 -0
  67. data/lib/rhosync/api/ping.rb +7 -0
  68. data/lib/rhosync/api/push_deletes.rb +6 -0
  69. data/lib/rhosync/api/push_objects.rb +6 -0
  70. data/lib/rhosync/api/reset.rb +10 -0
  71. data/lib/rhosync/api/set_db_doc.rb +8 -0
  72. data/lib/rhosync/api/set_refresh_time.rb +8 -0
  73. data/lib/rhosync/api/update_user.rb +4 -0
  74. data/lib/rhosync/api/upload_file.rb +4 -0
  75. data/lib/rhosync/api_token.rb +19 -0
  76. data/lib/rhosync/app.rb +69 -0
  77. data/lib/rhosync/bulk_data/bulk_data.rb +75 -0
  78. data/lib/rhosync/bulk_data/syncdb.index.schema +3 -0
  79. data/lib/rhosync/bulk_data/syncdb.schema +37 -0
  80. data/lib/rhosync/bulk_data.rb +2 -0
  81. data/lib/rhosync/client.rb +74 -0
  82. data/lib/rhosync/client_sync.rb +296 -0
  83. data/lib/rhosync/console/app/helpers/auth_helper.rb +18 -0
  84. data/lib/rhosync/console/app/helpers/extensions.rb +19 -0
  85. data/lib/rhosync/console/app/helpers/helpers.rb +52 -0
  86. data/lib/rhosync/console/app/public/main.css +7 -0
  87. data/lib/rhosync/console/app/public/text.txt +0 -0
  88. data/lib/rhosync/console/app/routes/auth.rb +29 -0
  89. data/lib/rhosync/console/app/routes/client.rb +32 -0
  90. data/lib/rhosync/console/app/routes/docs.rb +84 -0
  91. data/lib/rhosync/console/app/routes/home.rb +22 -0
  92. data/lib/rhosync/console/app/routes/user.rb +63 -0
  93. data/lib/rhosync/console/app/views/client.erb +30 -0
  94. data/lib/rhosync/console/app/views/doc.erb +56 -0
  95. data/lib/rhosync/console/app/views/docs.erb +29 -0
  96. data/lib/rhosync/console/app/views/index.erb +50 -0
  97. data/lib/rhosync/console/app/views/layout.erb +12 -0
  98. data/lib/rhosync/console/app/views/newuser.erb +17 -0
  99. data/lib/rhosync/console/app/views/ping.erb +28 -0
  100. data/lib/rhosync/console/app/views/result.erb +11 -0
  101. data/lib/rhosync/console/app/views/user.erb +32 -0
  102. data/lib/rhosync/console/app/views/users.erb +14 -0
  103. data/lib/rhosync/console/rhosync_api.rb +102 -0
  104. data/lib/rhosync/console/server.rb +27 -0
  105. data/lib/rhosync/credential.rb +9 -0
  106. data/lib/rhosync/document.rb +43 -0
  107. data/lib/rhosync/indifferent_access.rb +132 -0
  108. data/lib/rhosync/jobs/bulk_data_job.rb +104 -0
  109. data/lib/rhosync/jobs/ping_job.rb +19 -0
  110. data/lib/rhosync/jobs/source_job.rb +16 -0
  111. data/lib/rhosync/license.rb +79 -0
  112. data/lib/rhosync/lock_ops.rb +11 -0
  113. data/lib/rhosync/model.rb +410 -0
  114. data/lib/rhosync/ping/blackberry.rb +55 -0
  115. data/lib/rhosync/ping/iphone.rb +44 -0
  116. data/lib/rhosync/ping.rb +2 -0
  117. data/lib/rhosync/read_state.rb +27 -0
  118. data/lib/rhosync/server/views/index.erb +12 -0
  119. data/lib/rhosync/server.rb +242 -0
  120. data/lib/rhosync/source.rb +112 -0
  121. data/lib/rhosync/source_adapter.rb +95 -0
  122. data/lib/rhosync/source_sync.rb +245 -0
  123. data/lib/rhosync/store.rb +199 -0
  124. data/lib/rhosync/tasks.rb +151 -0
  125. data/lib/rhosync/user.rb +83 -0
  126. data/lib/rhosync/version.rb +3 -0
  127. data/lib/rhosync.rb +251 -0
  128. data/spec/api/api_helper.rb +44 -0
  129. data/spec/api/create_client_spec.rb +13 -0
  130. data/spec/api/create_user_spec.rb +16 -0
  131. data/spec/api/delete_client_spec.rb +13 -0
  132. data/spec/api/delete_user_spec.rb +18 -0
  133. data/spec/api/get_api_token_spec.rb +25 -0
  134. data/spec/api/get_client_params_spec.rb +18 -0
  135. data/spec/api/get_db_doc_spec.rb +21 -0
  136. data/spec/api/get_license_info_spec.rb +16 -0
  137. data/spec/api/get_source_params_spec.rb +26 -0
  138. data/spec/api/list_client_docs_spec.rb +33 -0
  139. data/spec/api/list_clients_spec.rb +23 -0
  140. data/spec/api/list_source_docs_spec.rb +26 -0
  141. data/spec/api/list_sources_spec.rb +27 -0
  142. data/spec/api/list_users_spec.rb +21 -0
  143. data/spec/api/ping_spec.rb +24 -0
  144. data/spec/api/push_deletes_spec.rb +16 -0
  145. data/spec/api/push_objects_spec.rb +27 -0
  146. data/spec/api/reset_spec.rb +22 -0
  147. data/spec/api/set_db_doc_spec.rb +20 -0
  148. data/spec/api/set_refresh_time_spec.rb +43 -0
  149. data/spec/api/update_user_spec.rb +31 -0
  150. data/spec/api/upload_file_spec.rb +26 -0
  151. data/spec/api_token_spec.rb +13 -0
  152. data/spec/app_spec.rb +20 -0
  153. data/spec/apps/rhotestapp/Rakefile +1 -0
  154. data/spec/apps/rhotestapp/application.rb +16 -0
  155. data/spec/apps/rhotestapp/config.ru +1 -0
  156. data/spec/apps/rhotestapp/settings/apple_fake_cert.pem +1 -0
  157. data/spec/apps/rhotestapp/settings/license.key +1 -0
  158. data/spec/apps/rhotestapp/settings/settings.yml +23 -0
  159. data/spec/apps/rhotestapp/sources/base_adapter.rb +9 -0
  160. data/spec/apps/rhotestapp/sources/sample_adapter.rb +66 -0
  161. data/spec/apps/rhotestapp/sources/simple_adapter.rb +39 -0
  162. data/spec/apps/rhotestapp/sources/sub_adapter.rb +7 -0
  163. data/spec/apps/rhotestapp/vendor/mygem-0.1.0/lib/mygem/mygem.rb +8 -0
  164. data/spec/apps/rhotestapp/vendor/mygem-0.1.0/lib/mygem.rb +1 -0
  165. data/spec/bulk_data/bulk_data_spec.rb +79 -0
  166. data/spec/client_spec.rb +58 -0
  167. data/spec/client_sync_spec.rb +377 -0
  168. data/spec/doc/base.html +72 -0
  169. data/spec/doc/doc_spec.rb +303 -0
  170. data/spec/doc/footer.html +4 -0
  171. data/spec/doc/header.html +30 -0
  172. data/spec/document_spec.rb +27 -0
  173. data/spec/generator/generator_spec.rb +53 -0
  174. data/spec/generator/generator_spec_helper.rb +8 -0
  175. data/spec/jobs/bulk_data_job_spec.rb +76 -0
  176. data/spec/jobs/ping_job_spec.rb +26 -0
  177. data/spec/jobs/source_job_spec.rb +25 -0
  178. data/spec/license_spec.rb +48 -0
  179. data/spec/model_spec.rb +269 -0
  180. data/spec/perf/bulk_data_perf_spec.rb +33 -0
  181. data/spec/perf/perf_spec_helper.rb +51 -0
  182. data/spec/perf/store_perf_spec.rb +28 -0
  183. data/spec/ping/blackberry_spec.rb +62 -0
  184. data/spec/ping/iphone_spec.rb +50 -0
  185. data/spec/read_state_spec.rb +25 -0
  186. data/spec/rhosync_spec.rb +43 -0
  187. data/spec/server/server_spec.rb +341 -0
  188. data/spec/source_adapter_spec.rb +114 -0
  189. data/spec/source_spec.rb +77 -0
  190. data/spec/source_sync_spec.rb +248 -0
  191. data/spec/spec_helper.rb +240 -0
  192. data/spec/store_spec.rb +149 -0
  193. data/spec/sync_states_spec.rb +101 -0
  194. data/spec/testdata/1000-data.txt +1414 -0
  195. data/spec/testdata/compressed/compress-data.txt +1 -0
  196. data/spec/testdata/upload1.txt +1 -0
  197. data/spec/testdata/upload2.txt +1 -0
  198. data/spec/user_spec.rb +79 -0
  199. data/tasks/redis.rake +134 -0
  200. metadata +545 -0
@@ -0,0 +1,199 @@
1
+ module Rhosync
2
+ class Store
3
+ RESERVED_ATTRIB_NAMES = ["attrib_type", "id"] unless defined? RESERVED_ATTRIB_NAMES
4
+ @@db = nil
5
+
6
+ class << self
7
+ def db; @@db || @@db = _get_redis end
8
+
9
+ def db=(server=nil)
10
+ @@db = _get_redis(server)
11
+ end
12
+
13
+ def create(server=nil)
14
+ @@db ||= _get_redis(server)
15
+ raise "Error connecting to Redis store." unless @@db and (@@db.is_a?(Redis) or @@db.is_a?(Redis::Client))
16
+ end
17
+
18
+ # Adds set with given data, replaces existing set
19
+ # if it exists or appends data to the existing set
20
+ # if append flag set to true
21
+ def put_data(dockey,data={},append=false)
22
+ if dockey and data
23
+ flash_data(dockey) unless append
24
+ # Inserts a hash or array
25
+ if data.is_a?(Hash)
26
+ @@db.pipelined do |pipeline|
27
+ data.each do |key,value|
28
+ value.each do |attrib,value|
29
+ unless _is_reserved?(attrib,value)
30
+ pipeline.sadd(dockey,setelement(key,attrib,value))
31
+ end
32
+ end
33
+ end
34
+ end
35
+ else
36
+ @@db.pipelined do |pipeline|
37
+ data.each do |value|
38
+ pipeline.sadd(dockey,value)
39
+ end
40
+ end
41
+ end
42
+ end
43
+ true
44
+ end
45
+
46
+ # Adds a simple key/value pair
47
+ def put_value(dockey,value)
48
+ if dockey
49
+ @@db.del(dockey)
50
+ @@db.set(dockey,value.to_s) if value
51
+ end
52
+ end
53
+
54
+ # Retrieves value for a given key
55
+ def get_value(dockey)
56
+ @@db.get(dockey) if dockey
57
+ end
58
+
59
+ # Retrieves set for given dockey,source,user
60
+ def get_data(dockey,type=Hash)
61
+ res = type == Hash ? {} : []
62
+ if dockey
63
+ @@db.smembers(dockey).each do |element|
64
+ if type == Hash
65
+ key,attrib,value = getelement(element)
66
+ res[key] = {} unless res[key]
67
+ res[key].merge!({attrib => value})
68
+ else
69
+ res << element
70
+ end
71
+ end
72
+ res
73
+ end
74
+ end
75
+
76
+ # Retrieves diff data hash between two sets
77
+ def get_diff_data(src_dockey,dst_dockey,p_size=nil)
78
+ res = {}
79
+ if src_dockey and dst_dockey
80
+ @@db.sdiff(dst_dockey,src_dockey).each do |element|
81
+ key,attrib,value = getelement(element)
82
+ res[key] = {} unless res[key]
83
+ res[key].merge!({attrib => value})
84
+ end
85
+ end
86
+ if p_size
87
+ diff = {}
88
+ page_size = p_size
89
+ res.each do |key,item|
90
+ diff[key] = item
91
+ page_size -= 1
92
+ break if page_size <= 0
93
+ end
94
+ [diff,res.size]
95
+ else
96
+ [res,res.size]
97
+ end
98
+ end
99
+
100
+ # Deletes data from a given doctype,source,user
101
+ def delete_data(dockey,data={})
102
+ if dockey and data
103
+ @@db.pipelined do |pipeline|
104
+ data.each do |key,value|
105
+ value.each do |attrib,val|
106
+ pipeline.srem(dockey,setelement(key,attrib,val))
107
+ end
108
+ end
109
+ end
110
+ end
111
+ true
112
+ end
113
+
114
+ # Deletes all keys matching a given mask
115
+ def flash_data(keymask)
116
+ @@db.keys(keymask).each do |key|
117
+ @@db.del(key)
118
+ end
119
+ end
120
+
121
+ # Returns array of keys matching a given keymask
122
+ def get_keys(keymask)
123
+ @@db.keys(keymask)
124
+ end
125
+
126
+ # Returns true if given item is a member of the given set
127
+ def ismember?(setkey,item)
128
+ @@db.sismember(setkey,item)
129
+ end
130
+
131
+ # Lock a given key and release when provided block is finished
132
+ def lock(dockey,timeout=0)
133
+ m_lock = get_lock(dockey,timeout)
134
+ res = yield
135
+ release_lock(dockey,m_lock)
136
+ res
137
+ end
138
+
139
+ def get_lock(dockey,timeout=0)
140
+ lock_key = _lock_key(dockey)
141
+ current_time = Time.now.to_i
142
+ if not @@db.setnx(lock_key,current_time+timeout+1)
143
+ loop do
144
+ if @@db.get(lock_key).to_i <= current_time and
145
+ @@db.getset(lock_key,current_time+timeout+1).to_i <= current_time
146
+ break
147
+ end
148
+ sleep(1)
149
+ current_time = Time.now.to_i
150
+ end
151
+ end
152
+ current_time+timeout+1
153
+ end
154
+
155
+ # Due to redis bug #140, setnx always returns true so this doesn't work
156
+ # def get_lock(dockey,timeout=0)
157
+ # lock_key = _lock_key(dockey)
158
+ # until @@db.setnx(lock_key,1) do
159
+ # sleep(1)
160
+ # end
161
+ # @@db.expire(lock_key,timeout+1)
162
+ # Time.now.to_i+timeout+1
163
+ # end
164
+
165
+ def release_lock(dockey,lock)
166
+ @@db.del(_lock_key(dockey)) if (lock >= Time.now.to_i)
167
+ end
168
+
169
+ # Create a copy of srckey in dstkey
170
+ def clone(srckey,dstkey)
171
+ @@db.sdiffstore(dstkey,srckey,'')
172
+ end
173
+
174
+ # Rename srckey to dstkey
175
+ def rename(srckey,dstkey)
176
+ @@db.rename(srckey,dstkey) if @@db.exists(srckey)
177
+ end
178
+
179
+ private
180
+ def _get_redis(server=nil)
181
+ if server and server.is_a?(String)
182
+ host,port,db,password = server.split(':')
183
+ Redis.new(:thread_safe => true, :host => host,
184
+ :port => port, :db => db, :password => password)
185
+ else
186
+ Redis.new(:thread_safe => true)
187
+ end
188
+ end
189
+
190
+ def _lock_key(dockey)
191
+ "#{dockey}:lock"
192
+ end
193
+
194
+ def _is_reserved?(attrib,value) #:nodoc:
195
+ RESERVED_ATTRIB_NAMES.include? attrib
196
+ end
197
+ end
198
+ end
199
+ end
@@ -0,0 +1,151 @@
1
+ require 'json'
2
+ require 'mechanize'
3
+ require 'zip/zip'
4
+ require 'uri'
5
+ require File.join(File.dirname(__FILE__),'console','rhosync_api')
6
+
7
+ module Rhosync
8
+ module TaskHelper
9
+ def post(path,params)
10
+ req = Net::HTTP.new($host,$port)
11
+ resp = req.post(path, params.to_json, 'Content-Type' => 'application/json')
12
+ print_resp(resp, resp.is_a?(Net::HTTPSuccess) ? true : false)
13
+ end
14
+
15
+ def print_resp(resp,success=true)
16
+ if success
17
+ puts "=> OK"
18
+ else
19
+ puts "=> FAILED"
20
+ end
21
+ puts "=> " + resp.body if resp and resp.body and resp.body.length > 0
22
+ end
23
+
24
+ def archive(path)
25
+ File.join(path,File.basename(path))+'.zip'
26
+ end
27
+
28
+ def ask(msg)
29
+ print msg
30
+ STDIN.gets.chomp
31
+ end
32
+
33
+ def load_settings(file)
34
+ begin
35
+ $settings = YAML.load_file(file)
36
+ rescue Exception => e
37
+ puts "Error opening settings file #{file}: #{e}."
38
+ puts e.backtrace.join("\n")
39
+ raise e
40
+ end
41
+ end
42
+
43
+ def rhosync_socket
44
+ "/tmp/rhosync.dtach"
45
+ end
46
+
47
+ def rhosync_pid
48
+ "/tmp/rhosync.pid"
49
+ end
50
+ end
51
+ end
52
+
53
+ namespace :rhosync do
54
+ include Rhosync::TaskHelper
55
+ include RhosyncApi
56
+
57
+ task :config do
58
+ $settings = load_settings(File.join(ENV['PWD'],'settings','settings.yml'))
59
+ env = (ENV['RHO_ENV'] || :development).to_sym
60
+ uri = URI.parse($settings[env][:syncserver])
61
+ $url = "#{uri.scheme}://#{uri.host}"
62
+ $url = "#{$url}:#{uri.port}" if uri.port && uri.port != 80
63
+ $host = uri.host
64
+ $port = uri.port
65
+ $agent = Mechanize.new
66
+ $appname = $settings[env][:syncserver].split('/').last
67
+ $token_file = File.join(ENV['HOME'],'.rhosync_token')
68
+ $token = File.read($token_file) if File.exist?($token_file)
69
+ end
70
+
71
+ desc "Reset the rhosync database (you will need to run rhosync:get_api_token afterwards)"
72
+ task :reset => :config do
73
+ RhosyncApi.reset($url,$token)
74
+ end
75
+
76
+ desc "Fetches current api token from rhosync"
77
+ task :get_token => :config do
78
+ login = ask "admin login: "
79
+ password = ask "admin password: "
80
+ $token = RhosyncApi.get_token($url,login,password)
81
+ File.open($token_file,'w') {|f| f.write $token}
82
+ puts "Token is saved in: #{$token_file}"
83
+ end
84
+
85
+ desc "Clean rhosync, get token, and create new user"
86
+ task :clean_start => [:get_token, :reset, :get_token, :create_user]
87
+
88
+ desc "Alias for `rake rhosync:stop; rake rhosync:start`"
89
+ task :restart => [:stop, :start]
90
+
91
+ desc "Creates and subscribes user for application in rhosync"
92
+ task :create_user => :config do
93
+ login = ask "new user login: "
94
+ password = ask "new user password: "
95
+ RhosyncApi.create_user($url,$appname,$token,login,password)
96
+ end
97
+
98
+ desc "Deletes the user from rhosync"
99
+ task :delete_user => :config do
100
+ login = ask "user to delete: "
101
+ RhosyncApi.delete_user($url,$appname,$token,login)
102
+ end
103
+
104
+ # desc "Updates an existing user in rhosync"
105
+ # task :update_user => :config do
106
+ # login = ask "login: "
107
+ # password = ask "password: "
108
+ # new_password = ask "new password: "
109
+ # post("/api/update_user", {:app_name => $appname, :api_token => $token,
110
+ # :login => login, :password => password, :attributes => {:new_password => new_password}})
111
+ # end
112
+
113
+ # desc "Reset source refresh time"
114
+ # task :reset_refresh_time => :config do
115
+ # user = ask "user: "
116
+ # source_name = ask "source name: "
117
+ # post("/api/set_refresh_time", {:api_token => $token, :app_name => $appname,
118
+ # :user_name => user, :source_name => source_name})
119
+ # end
120
+
121
+ desc "Run rhosync source adapter specs"
122
+ task :spec do
123
+ files = File.join($app_basedir,'rhosync/spec/sources/*_spec.rb')
124
+ Spec::Rake::SpecTask.new('rhosync:spec') do |t|
125
+ t.spec_files = FileList[files]
126
+ t.spec_opts = %w(-fs --color)
127
+ t.rcov = true
128
+ t.rcov_opts = ['--exclude', 'spec/*,gems/*']
129
+ end
130
+ end
131
+
132
+ desc "Start rhosync server"
133
+ task :start do
134
+ puts 'Detach with Ctrl+\ Re-attach with rake rhosync:attach'
135
+ sleep 1
136
+ command = "dtach -A #{rhosync_socket} rackup config.ru -P #{rhosync_pid}"
137
+ sh command
138
+ end
139
+
140
+ desc "Stop rhosync server"
141
+ task :stop do
142
+ sh "cat #{rhosync_pid} | xargs kill -3"
143
+ end
144
+
145
+ desc "Attach to rhosync console"
146
+ task :attach do
147
+ sh "dtach -a #{rhosync_socket}"
148
+ end
149
+ end
150
+
151
+ load File.join(File.dirname(__FILE__),'..','..','tasks','redis.rake')
@@ -0,0 +1,83 @@
1
+ require 'digest/sha1'
2
+
3
+ module Rhosync
4
+ # Inspired by sinatra-authentication
5
+ # Password uses simple sha1 digest for hashing
6
+ class User < Model
7
+ field :login,:string
8
+ field :email,:string
9
+ field :salt,:string
10
+ field :hashed_password,:string
11
+ set :clients, :string
12
+ field :admin, :int
13
+ field :token_id, :string
14
+
15
+ class << self
16
+ def create(fields={})
17
+ fields[:id] = fields[:login]
18
+ super(fields)
19
+ end
20
+
21
+ def authenticate(login,password)
22
+ return unless is_exist?(login)
23
+ current_user = load(login)
24
+ return if current_user.nil?
25
+ return current_user if User.encrypt(password, current_user.salt) == current_user.hashed_password
26
+ end
27
+ end
28
+
29
+ def new_password=(pass)
30
+ self.password=(pass)
31
+ end
32
+
33
+ def password=(pass)
34
+ @password = pass
35
+ self.salt = User.random_string(10) if !self.salt
36
+ self.hashed_password = User.encrypt(@password, self.salt)
37
+ end
38
+
39
+ def delete
40
+ clients.members.each do |client_id|
41
+ Client.load(client_id,{:source_name => '*'}).delete
42
+ end
43
+ self.token.delete if self.token
44
+ super
45
+ end
46
+
47
+ def create_token
48
+ if self.token_id && ApiToken.is_exist?(self.token_id)
49
+ self.token.delete
50
+ end
51
+ self.token_id = ApiToken.create(:user_id => self.login).id
52
+ end
53
+
54
+ def token
55
+ ApiToken.load(self.token_id)
56
+ end
57
+
58
+ def token=(value)
59
+ if self.token_id && ApiToken.is_exist?(self.token_id)
60
+ self.token.delete
61
+ end
62
+ self.token_id = ApiToken.create(:user_id => self.login, :value => value).id
63
+ end
64
+
65
+ def update(fields)
66
+ fields.each do |key,value|
67
+ self.send("#{key.to_sym}=", value) unless key == 'login'
68
+ end
69
+ end
70
+
71
+ protected
72
+ def self.encrypt(pass, salt)
73
+ Digest::SHA1.hexdigest(pass+salt)
74
+ end
75
+
76
+ def self.random_string(len)
77
+ chars = ("a".."z").to_a + ("A".."Z").to_a + ("0".."9").to_a
78
+ newpass = ""
79
+ 1.upto(len) { |i| newpass << chars[rand(chars.size-1)] }
80
+ return newpass
81
+ end
82
+ end
83
+ end
@@ -0,0 +1,3 @@
1
+ module Rhosync
2
+ VERSION = '2.0.0.beta1'
3
+ end
data/lib/rhosync.rb ADDED
@@ -0,0 +1,251 @@
1
+ require 'redis'
2
+ require 'json'
3
+ require 'base64'
4
+ require 'zip/zip'
5
+ require 'yaml'
6
+ require 'rhosync/license'
7
+ require 'rhosync/version'
8
+ require 'rhosync/document'
9
+ require 'rhosync/lock_ops'
10
+ require 'rhosync/model'
11
+ require 'rhosync/source'
12
+ require 'rhosync/user'
13
+ require 'rhosync/api_token'
14
+ require 'rhosync/app'
15
+ require 'rhosync/store'
16
+ require 'rhosync/client'
17
+ require 'rhosync/read_state'
18
+ require 'rhosync/client_sync'
19
+ require 'rhosync/source_adapter'
20
+ require 'rhosync/source_sync'
21
+ require 'rhosync/indifferent_access'
22
+ require 'rhosync/jobs/source_job'
23
+ require 'rhosync/jobs/ping_job'
24
+ require 'rhosync/bulk_data'
25
+
26
+ # Various module utilities for the store
27
+ module Rhosync
28
+ APP_NAME = 'application' unless defined? APP_NAME
29
+
30
+ class InvalidArgumentError < RuntimeError; end
31
+ class RhosyncServerError < RuntimeError; end
32
+ extend self
33
+
34
+ class << self
35
+ attr_accessor :base_directory, :app_directory, :data_directory,
36
+ :vendor_directory, :blackberry_bulk_sync, :redis, :environment,
37
+ :log_disabled, :license
38
+ end
39
+
40
+ ### Begin Rhosync setup methods
41
+ # Server hook to initialize Rhosync
42
+ def bootstrap(basedir)
43
+ config = get_config(basedir)
44
+ #Load environment
45
+ environment = (ENV['RHO_ENV'] || :development).to_sym
46
+ # Initialize Rhosync and Resque
47
+ Rhosync.base_directory = basedir
48
+ Rhosync.app_directory = get_setting(config,environment,:app_directory)
49
+ Rhosync.data_directory = get_setting(config,environment,:data_directory)
50
+ Rhosync.vendor_directory = get_setting(config,environment,:vendor_directory)
51
+ Rhosync.blackberry_bulk_sync = get_setting(config,environment,:blackberry_bulk_sync,false)
52
+ Rhosync.redis = get_setting(config,environment,:redis,false)
53
+ Rhosync.log_disabled = get_setting(config,environment,:log_disabled,false)
54
+ Rhosync.environment = environment
55
+ yield self if block_given?
56
+ Store.create(Rhosync.redis)
57
+ Resque.redis = Store.db
58
+ Rhosync.base_directory ||= File.join(File.dirname(__FILE__),'..')
59
+ Rhosync.app_directory ||= Rhosync.base_directory
60
+ Rhosync.data_directory ||= File.join(Rhosync.base_directory,'data')
61
+ Rhosync.vendor_directory ||= File.join(Rhosync.base_directory,'vendor')
62
+ Rhosync.blackberry_bulk_sync ||= false
63
+ Rhosync.log_disabled ||= false
64
+ Rhosync.license = License.new
65
+
66
+ check_and_add(File.join(Rhosync.app_directory,'sources'))
67
+ start_app(config)
68
+ create_admin_user
69
+ check_hsql_lib! if Rhosync.blackberry_bulk_sync
70
+ end
71
+
72
+ def start_app(config)
73
+ if config and config[Rhosync.environment]
74
+ app = nil
75
+ app_name = APP_NAME
76
+ if App.is_exist?(app_name)
77
+ app = App.load(app_name)
78
+ else
79
+ app = App.create(:name => app_name)
80
+ end
81
+ sources = config[:sources] || []
82
+ sources.each do |source_name,fields|
83
+ if Source.is_exist?(source_name)
84
+ s = Source.load(source_name,{:app_id => app.name,:user_id => '*'})
85
+ s.update(fields)
86
+ else
87
+ fields[:name] = source_name
88
+ Source.create(fields,{:app_id => app.name})
89
+ end
90
+ unless app.sources.members.include?(source_name)
91
+ app.sources << source_name
92
+ end
93
+ # load ruby file for source adapter to re-load class
94
+ load under_score(source_name+'.rb')
95
+ end
96
+ end
97
+ end
98
+
99
+ # Generate admin user on first load
100
+ def create_admin_user
101
+ unless User.is_exist?('admin')
102
+ admin = User.create({:login => 'admin', :admin => 1})
103
+ admin.password = ''
104
+ admin.create_token
105
+ end
106
+ end
107
+
108
+ # Add path to load_path unless it has been added already
109
+ def check_and_add(path)
110
+ $:.unshift path unless $:.include?(path)
111
+ end
112
+
113
+ def get_config(basedir)
114
+ # Load settings
115
+ settings_file = File.join(basedir,'settings','settings.yml') if basedir
116
+ config = YAML.load_file(settings_file) if settings_file and File.exist?(settings_file)
117
+ end
118
+ ### End Rhosync setup methods
119
+
120
+
121
+ def check_default_secret!(secret)
122
+ if secret == '<changeme>'
123
+ log "*"*60+"\n\n"
124
+ log "WARNING: Change the session secret in config.ru from <changeme> to something secure."
125
+ log " i.e. running `rake secret` in a rails app will generate a secret you could use.\n\n"
126
+ log "*"*60
127
+ end
128
+ end
129
+
130
+ # Serializes oav to set element
131
+ def setelement(obj,attrib,value)
132
+ "#{obj}:#{attrib}:#{Base64.encode64(value.to_s)}"
133
+ end
134
+
135
+ # De-serializes oav from set element
136
+ def getelement(element)
137
+ res = element.split(':')
138
+ [res[0], res[1], Base64.decode64(res[2].to_s)]
139
+ end
140
+
141
+ # Get random UUID string
142
+ def get_random_uuid
143
+ UUIDTools::UUID.random_create.to_s.gsub(/\-/,'')
144
+ end
145
+
146
+ # Generates new token (64-bit integer) based on # of
147
+ # microseconds since Jan 1 2009
148
+ def get_token
149
+ ((Time.now.to_f - Time.mktime(2009,"jan",1,0,0,0,0).to_f) * 10**6).to_i
150
+ end
151
+
152
+ # Computes token for a single client request
153
+ def compute_token(doc_key)
154
+ token = get_token
155
+ Store.put_value(doc_key,token)
156
+ token.to_s
157
+ end
158
+
159
+ # Returns require-friendly filename for a class
160
+ def under_score(camel_cased_word)
161
+ camel_cased_word.to_s.gsub(/::/, '/').
162
+ gsub(/([A-Z]+)([A-Z][a-z])/,'\1_\2').
163
+ gsub(/([a-z\d])([A-Z])/,'\1_\2').
164
+ tr("-", "_").
165
+ downcase
166
+ end
167
+
168
+ # Taken from rails inflector
169
+ def camelize(lower_case_and_underscored_word, first_letter_in_uppercase = true)
170
+ if first_letter_in_uppercase
171
+ lower_case_and_underscored_word.to_s.gsub(/\/(.?)/) { "::#{$1.upcase}" }.gsub(/(?:^|_)(.)/) { $1.upcase }
172
+ end
173
+ end
174
+
175
+ def check_hsql_lib!
176
+ unless File.exists?(File.join(Rhosync.vendor_directory,'hsqldata.jar'))
177
+ log "*"*60
178
+ log ""
179
+ log "WARNING: Missing vendor/hsqldata.jar, please install it for BlackBerry bulk sync support."
180
+ log ""
181
+ log "*"*60
182
+ end
183
+ end
184
+
185
+ def unzip_file(file_dir,params)
186
+ uploaded_file = File.join(file_dir, params[:filename])
187
+ begin
188
+ File.open(uploaded_file, 'wb') do |file|
189
+ file.write(params[:tempfile].read)
190
+ end
191
+ Zip::ZipFile.open(uploaded_file) do |zip_file|
192
+ zip_file.each do |f|
193
+ f_path = File.join(file_dir,f.name)
194
+ FileUtils.mkdir_p(File.dirname(f_path))
195
+ zip_file.extract(f, f_path) { true }
196
+ end
197
+ end
198
+ rescue Exception => e
199
+ log "Failed to unzip `#{uploaded_file}`"
200
+ raise e
201
+ ensure
202
+ FileUtils.rm_f(uploaded_file)
203
+ end
204
+ end
205
+
206
+ def lap_timer(msg,start)
207
+ duration = timenow - start
208
+ log "#{msg}: #{duration}"
209
+ timenow
210
+ end
211
+
212
+ def start_timer(msg='starting')
213
+ log "#{msg}"
214
+ timenow
215
+ end
216
+
217
+ def timenow
218
+ (Time.now.to_f * 1000)
219
+ end
220
+
221
+ def log(*args)
222
+ now = Time.now.strftime('%I:%M:%S %p %Y-%m-%d')
223
+ puts "[#{now}] #{args.join}" unless Rhosync.log_disabled
224
+ end
225
+
226
+ # Base rhosync application class
227
+ class Base
228
+ # Add everything in vendor to load path
229
+ # TODO: Integrate with 3rd party dependency management
230
+ def self.initializer(path=nil)
231
+ Dir["vendor/*"].each do |dir|
232
+ $:.unshift File.join(dir,'lib')
233
+ end
234
+ require 'rhosync'
235
+ require 'rhosync/server'
236
+ # Bootstrap Rhosync system
237
+ Rhosync.bootstrap(path || ENV['PWD'])
238
+ end
239
+
240
+ def self.store_blob(obj,field_name,blob)
241
+ blob[:tempfile].path if blob[:tempfile]
242
+ end
243
+ end
244
+
245
+ protected
246
+ def get_setting(config,environment,setting,default=nil)
247
+ res = nil
248
+ res = config[environment][setting] if config and environment
249
+ res || default
250
+ end
251
+ end