dbox 0.6.13 → 0.6.14

Sign up to get free protection for your applications and to get access to all the features.
@@ -0,0 +1,332 @@
1
+ # An example use of the /delta API call. Maintains a local cache of
2
+ # the App Folder's contents. Use the 'update' sub-command to update
3
+ # the local cache. Use the 'find' sub-command to search the local
4
+ # cache.
5
+ #
6
+ # Example usage:
7
+ #
8
+ # 1. Link to your Dropbox account
9
+ # > ruby search_cache.rb link
10
+ #
11
+ # 2. Go to Dropbox and make changes to the contents.
12
+ #
13
+ # 3. Update the local cache to match what's on Dropbox.
14
+ # > ruby search_cache.rb update
15
+ #
16
+ # 4. Search the local cache.
17
+ # > ruby search_cache.rb find 'txt'
18
+
19
+ # Repeat steps 2-4 any number of times.
20
+
21
+
22
+
23
+ require './lib/dropbox_sdk'
24
+ require 'json'
25
+
26
+ # You must use your Dropbox App key and secret to use the API.
27
+ # Find this at https://www.dropbox.com/developers
28
+ APP_KEY = ''
29
+ APP_SECRET = ''
30
+ ACCESS_TYPE = :app_folder #The two valid values here are :app_folder and :dropbox
31
+ #The default is :app_folder, but your application might be
32
+ #set to have full :dropbox access. Check your app at
33
+ #https://www.dropbox.com/developers/apps
34
+
35
+
36
+ STATE_FILE = 'search_cache.json'
37
+
38
+ def main()
39
+ if APP_KEY == '' or APP_SECRET == ''
40
+ warn "ERROR: Set your APP_KEY and APP_SECRET at the top of search_cache.rb"
41
+ exit
42
+ end
43
+ prog_name = __FILE__
44
+ args = ARGV
45
+ if args.size == 0
46
+ warn("Usage:\n")
47
+ warn(" #{prog_name} link Link to a user's account.")
48
+ warn(" #{prog_name} update Update cache to the latest on Dropbox.")
49
+ warn(" #{prog_name} update <num> Update cache, limit to <num> pages of /delta.")
50
+ warn(" #{prog_name} find <term> Search cache for <term>.")
51
+ warn(" #{prog_name} find Display entire cache contents")
52
+ warn(" #{prog_name} reset Delete the cache.")
53
+ exit
54
+ end
55
+
56
+ command = args[0]
57
+ if command == 'link'
58
+ command_link(args)
59
+ elsif command == 'update'
60
+ command_update(args)
61
+ elsif command == 'find'
62
+ command_find(args)
63
+ elsif command == 'reset'
64
+ command_reset(args)
65
+ else
66
+ warn "ERROR: Unknown command: #{command}"
67
+ warn "Run with no arguments for help."
68
+ exit(1)
69
+ end
70
+ end
71
+
72
+
73
+
74
+ def command_link(args)
75
+ if args.size != 1
76
+ warn "ERROR: \"link\" doesn't take any arguments"
77
+ exit
78
+ end
79
+
80
+ sess = DropboxSession.new(APP_KEY, APP_SECRET)
81
+ sess.get_request_token
82
+
83
+ # Make the user log in and authorize this token
84
+ url = sess.get_authorize_url
85
+ puts "1. Go to: #{url}"
86
+ puts "2. Authorize this app."
87
+ puts "After you're done, press ENTER."
88
+ STDIN.gets
89
+
90
+ # This will fail if the user didn't visit the above URL and hit 'Allow'
91
+ sess.get_access_token
92
+ access_token = sess.access_token
93
+ puts "Link successful."
94
+
95
+ save_state({
96
+ 'access_token' => [access_token.key, access_token.secret],
97
+ 'tree' => {}
98
+ })
99
+ end
100
+
101
+
102
+ def command_update(args)
103
+ if args.size == 1
104
+ page_limit = nil
105
+ elsif args.size == 2
106
+ page_limit = Integer(args[1])
107
+ else
108
+ warn "ERROR: \"update\" takes either zero or one argument."
109
+ exit
110
+ end
111
+
112
+ # Load state
113
+ state = load_state()
114
+ access_token = state['access_token']
115
+ cursor = state['cursor']
116
+ tree = state['tree']
117
+
118
+ # Connect to Dropbox
119
+ sess = DropboxSession.new(APP_KEY, APP_SECRET)
120
+ sess.set_access_token(*access_token)
121
+ c = DropboxClient.new(sess, ACCESS_TYPE)
122
+
123
+ page = 0
124
+ changed = false
125
+ while (page_limit == nil) or (page < page_limit)
126
+ # Get /delta results from Dropbox
127
+ result = c.delta(cursor)
128
+ page += 1
129
+ if result['reset'] == true
130
+ puts 'reset'
131
+ changed = true
132
+ tree = {}
133
+ end
134
+ cursor = result['cursor']
135
+ # Apply the entries one by one to our cached tree.
136
+ for delta_entry in result['entries']
137
+ changed = true
138
+ apply_delta(tree, delta_entry)
139
+ end
140
+ cursor = result['cursor']
141
+ if not result['has_more']
142
+ break
143
+ end
144
+ end
145
+
146
+ # Save state
147
+ if changed
148
+ state['cursor'] = cursor
149
+ state['tree'] = tree
150
+ save_state(state)
151
+ else
152
+ puts "No updates."
153
+ end
154
+
155
+ end
156
+
157
+ # We track folder state as a tree of Node objects.
158
+ class Node
159
+ attr_accessor :path, :content
160
+ def initialize(path, content)
161
+ # The "original" page (i.e. not the lower-case path)
162
+ @path = path
163
+ # For files, content is a pair (size, modified)
164
+ # For folders, content is a hash of children Nodes, keyed by lower-case file names.
165
+ @content = content
166
+ end
167
+ def folder?()
168
+ @content.is_a? Hash
169
+ end
170
+ def to_json()
171
+ [@path, Node.to_json_content(@content)]
172
+ end
173
+ def self.from_json(jnode)
174
+ path, jcontent = jnode
175
+ Node.new(path, Node.from_json_content(jcontent))
176
+ end
177
+ def self.to_json_content(content)
178
+ if content.is_a? Hash
179
+ map_hash_values(content) { |child| child.to_json }
180
+ else
181
+ content
182
+ end
183
+ end
184
+ def self.from_json_content(jcontent)
185
+ if jcontent.is_a? Hash
186
+ map_hash_values(jcontent) { |jchild| Node.from_json jchild }
187
+ else
188
+ jcontent
189
+ end
190
+ end
191
+ end
192
+
193
+ # Run a mapping function over every value in a Hash, returning a new Hash.
194
+ def map_hash_values(h)
195
+ new = {}
196
+ h.each { |k,v| new[k] = yield v }
197
+ new
198
+ end
199
+
200
+
201
+ def apply_delta(root, e)
202
+ path, metadata = e
203
+ branch, leaf = split_path(path)
204
+
205
+ if metadata != nil
206
+ puts "+ #{path}"
207
+ # Traverse down the tree until we find the parent folder of the entry
208
+ # we want to add. Create any missing folders along the way.
209
+ children = root
210
+ branch.each do |part|
211
+ node = get_or_create_child(children, part)
212
+ # If there's no folder here, make an empty one.
213
+ if not node.folder?
214
+ node.content = {}
215
+ end
216
+ children = node.content
217
+ end
218
+
219
+ # Create the file/folder.
220
+ node = get_or_create_child(children, leaf)
221
+ node.path = metadata['path'] # Save the un-lower-cased path.
222
+ if metadata['is_dir']
223
+ # Only create a folder if there isn't one there already.
224
+ node.content = {} if not node.folder?
225
+ else
226
+ node.content = metadata['size'], metadata['modified']
227
+ end
228
+ else
229
+ puts "- #{path}"
230
+ # Traverse down the tree until we find the parent of the entry we
231
+ # want to delete.
232
+ children = root
233
+ missing_parent = false
234
+ branch.each do |part|
235
+ node = children[part]
236
+ # If one of the parent folders is missing, then we're done.
237
+ if node == nil or not node.folder?
238
+ missing_parent = true
239
+ break
240
+ end
241
+ children = node.content
242
+ end
243
+ # If we made it all the way, delete the file/folder.
244
+ if not missing_parent
245
+ children.delete(leaf)
246
+ end
247
+ end
248
+ end
249
+
250
+ def get_or_create_child(children, name)
251
+ child = children[name]
252
+ if child == nil
253
+ children[name] = child = Node.new(nil, nil)
254
+ end
255
+ child
256
+ end
257
+
258
+ def split_path(path)
259
+ bad, *parts = path.split '/'
260
+ [parts, parts.pop]
261
+ end
262
+
263
+
264
+ def command_find(args)
265
+ if args.size == 1
266
+ term = ''
267
+ elsif args.size == 2
268
+ term = args[1]
269
+ else
270
+ warn("ERROR: \"find\" takes either zero or one arguments.")
271
+ exit
272
+ end
273
+
274
+ state = load_state()
275
+ results = []
276
+ search_tree(results, state['tree'], term)
277
+ for r in results
278
+ puts("#{r}")
279
+ end
280
+ puts("[Matches: #{results.size}]")
281
+ end
282
+
283
+
284
+ def command_reset(args)
285
+ if args.size != 1
286
+ warn("ERROR: \"reset\" takes no arguments.")
287
+ exit
288
+ end
289
+
290
+ # Delete cursor, empty tree.
291
+ state = load_state()
292
+ if state.has_key?('cursor')
293
+ state.delete('cursor')
294
+ end
295
+ state['tree'] = {}
296
+ save_state(state)
297
+ end
298
+
299
+
300
+ # Recursively search 'tree' for files that contain the string in 'term'.
301
+ # Print out any matches.
302
+ def search_tree(results, tree, term)
303
+ tree.each do |name_lc, node|
304
+ path = node.path
305
+ if (path != nil) and path.include?(term)
306
+ if node.folder?
307
+ results.push("#{path}")
308
+ else
309
+ size, modified = node.content
310
+ results.push("#{path} (#{size}, #{modified})")
311
+ end
312
+ end
313
+ if node.folder?
314
+ search_tree(results, node.content, term)
315
+ end
316
+ end
317
+ end
318
+
319
+ def save_state(state)
320
+ state['tree'] = Node.to_json_content(state['tree'])
321
+ File.open(STATE_FILE,"w") do |f|
322
+ f.write(JSON.pretty_generate(state, :max_nesting => false))
323
+ end
324
+ end
325
+
326
+ def load_state()
327
+ state = JSON.parse(File.read(STATE_FILE), :max_nesting => false)
328
+ state['tree'] = Node.from_json_content(state['tree'])
329
+ state
330
+ end
331
+
332
+ main()
metadata CHANGED
@@ -1,13 +1,13 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: dbox
3
3
  version: !ruby/object:Gem::Version
4
- hash: 29
4
+ hash: 27
5
5
  prerelease:
6
6
  segments:
7
7
  - 0
8
8
  - 6
9
- - 13
10
- version: 0.6.13
9
+ - 14
10
+ version: 0.6.14
11
11
  platform: ruby
12
12
  authors:
13
13
  - Ken Pratt
@@ -15,7 +15,7 @@ autorequire:
15
15
  bindir: bin
16
16
  cert_chain: []
17
17
 
18
- date: 2012-06-04 00:00:00 Z
18
+ date: 2012-07-26 00:00:00 Z
19
19
  dependencies:
20
20
  - !ruby/object:Gem::Dependency
21
21
  name: multipart-post
@@ -127,15 +127,19 @@ files:
127
127
  - sample_polling_script.rb
128
128
  - spec/dbox_spec.rb
129
129
  - spec/spec_helper.rb
130
- - vendor/dropbox-ruby-sdk/CHANGELOG
131
- - vendor/dropbox-ruby-sdk/LICENSE
132
- - vendor/dropbox-ruby-sdk/README
133
- - vendor/dropbox-ruby-sdk/cli_example.rb
134
- - vendor/dropbox-ruby-sdk/dropbox_controller.rb
135
- - vendor/dropbox-ruby-sdk/gemspec.rb
136
- - vendor/dropbox-ruby-sdk/lib/dropbox_sdk.rb
137
- - vendor/dropbox-ruby-sdk/lib/trusted-certs.crt
138
- - vendor/dropbox-ruby-sdk/web_file_browser.rb
130
+ - vendor/dropbox-ruby-sdk-1.3.1/.search_cache.rb.swp
131
+ - vendor/dropbox-ruby-sdk-1.3.1/CHANGELOG
132
+ - vendor/dropbox-ruby-sdk-1.3.1/LICENSE
133
+ - vendor/dropbox-ruby-sdk-1.3.1/README
134
+ - vendor/dropbox-ruby-sdk-1.3.1/cli_example.rb
135
+ - vendor/dropbox-ruby-sdk-1.3.1/copy_between_accounts.rb
136
+ - vendor/dropbox-ruby-sdk-1.3.1/dropbox_controller.rb
137
+ - vendor/dropbox-ruby-sdk-1.3.1/gemspec.rb
138
+ - vendor/dropbox-ruby-sdk-1.3.1/lib/dropbox_sdk.rb
139
+ - vendor/dropbox-ruby-sdk-1.3.1/lib/trusted-certs.crt
140
+ - vendor/dropbox-ruby-sdk-1.3.1/search_cache.json
141
+ - vendor/dropbox-ruby-sdk-1.3.1/search_cache.rb
142
+ - vendor/dropbox-ruby-sdk-1.3.1/web_file_browser.rb
139
143
  homepage: http://github.com/kenpratt/dbox
140
144
  licenses:
141
145
  - MIT