dbox 0.6.13 → 0.6.14
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/History.txt +5 -0
- data/README.md +9 -0
- data/VERSION +1 -1
- data/dbox.gemspec +15 -11
- data/lib/dbox/syncer.rb +8 -3
- data/vendor/dropbox-ruby-sdk-1.3.1/.search_cache.rb.swp +0 -0
- data/vendor/{dropbox-ruby-sdk → dropbox-ruby-sdk-1.3.1}/CHANGELOG +8 -0
- data/vendor/{dropbox-ruby-sdk → dropbox-ruby-sdk-1.3.1}/LICENSE +0 -0
- data/vendor/{dropbox-ruby-sdk → dropbox-ruby-sdk-1.3.1}/README +0 -0
- data/vendor/{dropbox-ruby-sdk → dropbox-ruby-sdk-1.3.1}/cli_example.rb +0 -0
- data/vendor/dropbox-ruby-sdk-1.3.1/copy_between_accounts.rb +155 -0
- data/vendor/{dropbox-ruby-sdk → dropbox-ruby-sdk-1.3.1}/dropbox_controller.rb +0 -0
- data/vendor/{dropbox-ruby-sdk → dropbox-ruby-sdk-1.3.1}/gemspec.rb +2 -2
- data/vendor/{dropbox-ruby-sdk → dropbox-ruby-sdk-1.3.1}/lib/dropbox_sdk.rb +119 -19
- data/vendor/{dropbox-ruby-sdk → dropbox-ruby-sdk-1.3.1}/lib/trusted-certs.crt +0 -0
- data/vendor/dropbox-ruby-sdk-1.3.1/search_cache.json +42830 -0
- data/vendor/dropbox-ruby-sdk-1.3.1/search_cache.rb +332 -0
- data/vendor/{dropbox-ruby-sdk → dropbox-ruby-sdk-1.3.1}/web_file_browser.rb +0 -0
- metadata +17 -13
@@ -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()
|
File without changes
|
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:
|
4
|
+
hash: 27
|
5
5
|
prerelease:
|
6
6
|
segments:
|
7
7
|
- 0
|
8
8
|
- 6
|
9
|
-
-
|
10
|
-
version: 0.6.
|
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-
|
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
|
131
|
-
- vendor/dropbox-ruby-sdk/
|
132
|
-
- vendor/dropbox-ruby-sdk/
|
133
|
-
- vendor/dropbox-ruby-sdk/
|
134
|
-
- vendor/dropbox-ruby-sdk/
|
135
|
-
- vendor/dropbox-ruby-sdk/
|
136
|
-
- vendor/dropbox-ruby-sdk/
|
137
|
-
- vendor/dropbox-ruby-sdk
|
138
|
-
- vendor/dropbox-ruby-sdk/
|
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
|