rhosync 2.0.0.beta1

Sign up to get free protection for your applications and to get access to all the features.
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