skypager 0.0.2

Sign up to get free protection for your applications and to get access to all the features.
Files changed (81) hide show
  1. checksums.yaml +7 -0
  2. data/.gitignore +20 -0
  3. data/Gemfile +9 -0
  4. data/LICENSE.txt +22 -0
  5. data/Rakefile +21 -0
  6. data/bin/skypager +17 -0
  7. data/examples/.gitignore +4 -0
  8. data/examples/blog-site/.gitignore +18 -0
  9. data/examples/blog-site/.pryrc +4 -0
  10. data/examples/blog-site/Gemfile +8 -0
  11. data/examples/blog-site/config.rb +17 -0
  12. data/examples/blog-site/data/dropbox.json +1 -0
  13. data/examples/blog-site/source/images/background.png +0 -0
  14. data/examples/blog-site/source/images/middleman.png +0 -0
  15. data/examples/blog-site/source/index.html.erb +10 -0
  16. data/examples/blog-site/source/javascripts/all.js +1 -0
  17. data/examples/blog-site/source/layouts/layout.erb +19 -0
  18. data/examples/blog-site/source/posts/introduction-to-skypager.html.md +23 -0
  19. data/examples/blog-site/source/posts/skypager-and-dnsimple-and-amazon-web-services-combo.html.md +9 -0
  20. data/examples/blog-site/source/stylesheets/all.css +55 -0
  21. data/examples/blog-site/source/stylesheets/normalize.css +375 -0
  22. data/examples/gallery-site/.gitignore +18 -0
  23. data/examples/gallery-site/.pryrc +4 -0
  24. data/examples/gallery-site/Gemfile +11 -0
  25. data/examples/gallery-site/config.rb +38 -0
  26. data/examples/gallery-site/data/dropbox.json +1 -0
  27. data/examples/gallery-site/data/galleries.json +1 -0
  28. data/examples/gallery-site/source/gallery.html.erb +7 -0
  29. data/examples/gallery-site/source/images/background.png +0 -0
  30. data/examples/gallery-site/source/images/galleries/cristian-gallery-1/001.jpg +0 -0
  31. data/examples/gallery-site/source/images/galleries/cristian-gallery-1/002.jpg +0 -0
  32. data/examples/gallery-site/source/images/galleries/cristian-gallery-1/003.jpg +0 -0
  33. data/examples/gallery-site/source/images/galleries/cristian-gallery-1/004.jpg +0 -0
  34. data/examples/gallery-site/source/images/galleries/luca-gallery-1/001.jpg +0 -0
  35. data/examples/gallery-site/source/images/galleries/luca-gallery-1/002.JPG +0 -0
  36. data/examples/gallery-site/source/images/galleries/luca-gallery-1/003.jpg +0 -0
  37. data/examples/gallery-site/source/images/galleries/luca-gallery-1/004.JPG +0 -0
  38. data/examples/gallery-site/source/images/middleman.png +0 -0
  39. data/examples/gallery-site/source/index.html.erb +10 -0
  40. data/examples/gallery-site/source/javascripts/all.js +1 -0
  41. data/examples/gallery-site/source/layouts/layout.erb +20 -0
  42. data/examples/gallery-site/source/stylesheets/all.css +0 -0
  43. data/examples/gallery-site/source/stylesheets/normalize.css +375 -0
  44. data/examples/gallery-site/source/tutorial.md +151 -0
  45. data/lib/skypager.rb +92 -0
  46. data/lib/skypager/build_server.rb +17 -0
  47. data/lib/skypager/cli/commands/config.rb +58 -0
  48. data/lib/skypager/cli/commands/create.rb +98 -0
  49. data/lib/skypager/cli/commands/deploy.rb +30 -0
  50. data/lib/skypager/cli/commands/edit.rb +32 -0
  51. data/lib/skypager/cli/commands/list.rb +12 -0
  52. data/lib/skypager/cli/commands/setup.rb +124 -0
  53. data/lib/skypager/cli/commands/sync.rb +18 -0
  54. data/lib/skypager/configuration.rb +173 -0
  55. data/lib/skypager/data.rb +8 -0
  56. data/lib/skypager/data/excel_spreadsheet.rb +8 -0
  57. data/lib/skypager/data/google_spreadsheet.rb +225 -0
  58. data/lib/skypager/data/request.rb +12 -0
  59. data/lib/skypager/data/source.rb +171 -0
  60. data/lib/skypager/data/source_routes_proxy.rb +30 -0
  61. data/lib/skypager/dns.rb +65 -0
  62. data/lib/skypager/extension.rb +203 -0
  63. data/lib/skypager/middleman/commands/data.rb +0 -0
  64. data/lib/skypager/middleman/commands/deploy.rb +0 -0
  65. data/lib/skypager/middleman/commands/sync.rb +0 -0
  66. data/lib/skypager/site.rb +208 -0
  67. data/lib/skypager/sync.rb +23 -0
  68. data/lib/skypager/sync/amazon.rb +171 -0
  69. data/lib/skypager/sync/dropbox.rb +173 -0
  70. data/lib/skypager/sync/dropbox/delta.rb +67 -0
  71. data/lib/skypager/sync/folder.rb +235 -0
  72. data/lib/skypager/sync/google.rb +143 -0
  73. data/lib/skypager/tar.rb +77 -0
  74. data/lib/skypager/version.rb +3 -0
  75. data/skypager.gemspec +40 -0
  76. data/spec/lib/skypager/configuration_spec.rb +5 -0
  77. data/spec/lib/skypager/data_spec.rb +5 -0
  78. data/spec/lib/skypager/site_spec.rb +5 -0
  79. data/spec/spec_helper.rb +14 -0
  80. data/spec/support/json_helper.rb +7 -0
  81. metadata +383 -0
@@ -0,0 +1,67 @@
1
+ module Skypager
2
+ module Sync
3
+ class DropboxDelta
4
+
5
+ attr_accessor :client,
6
+ :data,
7
+ :cursor,
8
+ :entries,
9
+ :path_prefix
10
+
11
+ def initialize(client, cursor, path_prefix=nil)
12
+ @client = client
13
+ @cursor = cursor
14
+ @path_prefix = path_prefix
15
+ end
16
+
17
+ def processed!
18
+ # TODO
19
+ # Should update cursor
20
+ end
21
+
22
+ def entries
23
+ return @entries if @entries
24
+ fetch
25
+ @entries
26
+ end
27
+
28
+ def _dropbox_delta at=nil
29
+ at ||= cursor
30
+ response = client.delta(at, path_prefix)
31
+ self.cursor = response["cursor"]
32
+ response
33
+ end
34
+
35
+ def data
36
+ @data ||= fetch
37
+ end
38
+
39
+ def on_reset path_prefix, cursor
40
+ end
41
+
42
+ def fetch
43
+ return @response if @response
44
+
45
+ response = _dropbox_delta
46
+
47
+ if response["reset"] == true
48
+ on_reset(path_prefix, cursor)
49
+ end
50
+
51
+ self.entries = Hashie::Mash.new({})
52
+
53
+ response["entries"].each do |entry|
54
+ path, meta = entry
55
+ self.entries[path] = meta
56
+ end
57
+
58
+ if response["has_more"] == true
59
+ # TODO Implement
60
+ end
61
+
62
+ @response = response
63
+ end
64
+
65
+ end
66
+ end
67
+ end
@@ -0,0 +1,235 @@
1
+ module Skypager
2
+ module Sync
3
+ class Folder
4
+ attr_accessor :options,
5
+ :cursor,
6
+ :app
7
+
8
+ def initialize(options={})
9
+ @options = options
10
+ @cursor = options.fetch(:cursor, nil)
11
+ @app = options[:app]
12
+ end
13
+
14
+ # Used to generate the config line in the
15
+ # middleman config.rb
16
+ def config_line
17
+ "dropbox_sync('#{ relative_local_path }','#{ remote_path }')"
18
+ end
19
+
20
+ # Gets the reference to the dropbox folder settings for the app
21
+ # which allows us to persist info about the sync state of the folder
22
+ def folder_settings
23
+ data = app.data.dropbox
24
+ data[:folders][options[:folder]].tap {|h| h && h.symbolize_keys! }
25
+ end
26
+
27
+ def cursor= value
28
+ folder_settings[:cursor] = value
29
+ save_folder_settings
30
+ end
31
+
32
+ def save_folder_settings
33
+ data = app.data.dropbox.dup
34
+
35
+ Pathname(app.data_dir).join("dropbox.json").open('w+') do |fh|
36
+ fh.write(data.to_json)
37
+ end
38
+ end
39
+
40
+ def auto_prefix
41
+ options[:site_name]
42
+ end
43
+
44
+ def dropbox?
45
+ options[:type] == "dropbox"
46
+ end
47
+
48
+ def google?
49
+ options[:type] == "google"
50
+ end
51
+
52
+ def sync
53
+ return dropbox_pull if dropbox?
54
+ return google_sync if google?
55
+ end
56
+
57
+ def dropbox_pull
58
+ delta = dropbox_delta
59
+
60
+ delta.entries.each do |entry|
61
+ local = matching_local_path_for(entry.path)
62
+
63
+ if entry.is_deleted
64
+ puts "Removing #{ local }"
65
+ local.rmtree
66
+ elsif entry.is_dir == true
67
+ unless local.exist?
68
+ puts "Creating #{ local } directory"
69
+ local.mkdir
70
+ end
71
+ elsif entry.is_dir == false || entry.is_dir == nil
72
+ puts "Downloading #{ entry.size } bytes to #{ local }"
73
+ local.open('wb+') do |fh|
74
+ fh.write(entry.download)
75
+ end
76
+ end
77
+ end
78
+
79
+ self.cursor = delta.cursor
80
+ end
81
+
82
+ def google_sync
83
+ # TODO
84
+ # Implement google drive folder cloning
85
+ end
86
+
87
+ def newer_local_files
88
+ remotes = all_remote_files
89
+
90
+ if dropbox?
91
+ all_local_files.select do |e|
92
+ remote = remotes.find do |x|
93
+ "#{x[:path]}".match(/#{ e[:path] }$/)
94
+ end
95
+
96
+ remote.nil? || (remote[:mtime] < e[:mtime])
97
+ end
98
+ end
99
+ end
100
+
101
+ def all_dropbox_files
102
+ h = lambda do |node|
103
+ base = [{
104
+ path: node.path,
105
+ mtime: DateTime.parse(node.modified).to_i
106
+ }]
107
+
108
+ base += node.ls.map(&h) if node.is_dir
109
+
110
+ base
111
+ end
112
+
113
+ dropbox_folder_files.map(&h).flatten
114
+ end
115
+
116
+ def dropbox_folder_files
117
+ Array((dropbox_folder rescue nil).try(:ls))
118
+ end
119
+
120
+ def matching_local_path_for(remote)
121
+ if sandboxed?
122
+ parts = remote.to_s.split("/")
123
+ parts.shift
124
+ part = parts.join("/")
125
+ root.join(part)
126
+ else
127
+ parts = remote.to_s.split("/")
128
+ parts.shift
129
+ part = parts.join("/")
130
+ local_path.join(part)
131
+ end
132
+ end
133
+
134
+ def root
135
+ Pathname(options[:root] || Dir.pwd)
136
+ end
137
+
138
+ def all_local_files
139
+ h = lambda do |path|
140
+ base = [{
141
+ path: path.relative_path_from(local_path).to_s,
142
+ realpath: path,
143
+ mtime: path.mtime.to_i
144
+ }]
145
+
146
+ base += path.children.map(&h) if path.directory?
147
+
148
+ base
149
+ end
150
+
151
+ local_path.children.map(&h).flatten
152
+ end
153
+
154
+ def all_remote_files
155
+ if dropbox?
156
+ all_dropbox_files
157
+ else
158
+
159
+ end
160
+ end
161
+
162
+ def relative_local_path
163
+ local_path.relative_path_from(root).to_s
164
+ end
165
+
166
+ def local_path
167
+ root.join(options[:local_path]).tap do |folder|
168
+ folder.mkdir unless folder.exist?
169
+ end
170
+ end
171
+
172
+ def synced
173
+ push_sync
174
+ self
175
+ end
176
+
177
+ def push_sync
178
+ return dropbox_push_sync if dropbox?
179
+ return google_push_sync if google?
180
+ end
181
+
182
+ def dropbox_push_sync
183
+ api = Skypager.dropbox.api
184
+
185
+ newer_local_files.each do |entry|
186
+ target = "#{ path_prefix }/#{ entry[:path] }"
187
+ target = target.gsub(/^\//,'')
188
+ api.upload(target, entry[:realpath].read, overwrite: true)
189
+ end
190
+ end
191
+
192
+ def google_push_sync
193
+
194
+ end
195
+
196
+ def dropbox_folder
197
+ Skypager.dropbox.find(path_prefix)
198
+ end
199
+
200
+ def remote_path
201
+ options[:remote_path].to_s
202
+ end
203
+
204
+ def sandboxed?
205
+ Skypager.config.dropbox_app_type == "sandbox"
206
+ end
207
+
208
+ # Which path do we scope our delta calls to?
209
+ def path_prefix
210
+ if sandboxed?
211
+ if remote_path.match(/^\/#{ options[:site_name] }/)
212
+ remote_path
213
+ else
214
+ "/#{ options[:site_name] }/#{ remote_path.gsub(/^\//,'') }"
215
+ end
216
+ else
217
+ unless remote_path.match(/^\//)
218
+ return "/#{ remote_path }"
219
+ end
220
+
221
+ remote_path
222
+ end
223
+ end
224
+
225
+ def has_remote_changes?
226
+ dropbox_delta.entries.length > 0
227
+ end
228
+
229
+ def dropbox_delta
230
+ @dropbox_delta ||= Skypager.dropbox.delta(cursor, path_prefix: path_prefix)
231
+ end
232
+
233
+ end
234
+ end
235
+ end
@@ -0,0 +1,143 @@
1
+ module Skypager
2
+ module Sync
3
+ class Google
4
+ include Singleton
5
+
6
+ def self.method_missing(meth, *args, &block)
7
+ if client.respond_to?(meth)
8
+ return client.send(meth, *args, &block)
9
+ end
10
+
11
+ super
12
+ end
13
+
14
+ def self.client(options={})
15
+ @client ||= begin
16
+ instance.with_options(options)
17
+ end
18
+ end
19
+
20
+ def refreshable?
21
+ has_application_keys? && has_refresh_token?
22
+ end
23
+
24
+ # Runs through an interactive session where we get the
25
+ # necessary tokens needed to integrate with google drive.
26
+ def setup(options={})
27
+ get_application_keys unless has_application_keys?
28
+
29
+ if options[:client_id]
30
+ Skypager.config.set "google_client_id", options[:client_id]
31
+ end
32
+
33
+ if options[:client_secret]
34
+ Skypager.config.set "google_client_secret", options[:client_secret]
35
+ end
36
+
37
+ if has_refresh_token?
38
+ refresh_access_token!
39
+ else
40
+ Launchy.open(auth_client.authorization_uri)
41
+ say("\n1. Open this page:\n%s\n\n" % auth_client.authorization_uri)
42
+ auth_client.code = ask("2. Enter the authorization code shown in the page: ", String)
43
+ auth_client.fetch_access_token!
44
+ Skypager.config.set "google_refresh_token", auth_client.refresh_token
45
+ Skypager.config.set "google_access_token", auth_client.access_token
46
+ end
47
+ end
48
+
49
+ def session
50
+ api
51
+ end
52
+
53
+ def api
54
+ @api ||= begin
55
+ refresh_access_token!
56
+ GoogleDrive.login_with_oauth(Skypager.config.google_access_token)
57
+ end
58
+ end
59
+
60
+ def method_missing meth, *args, &block
61
+ if api.respond_to?(meth)
62
+ return api.send(meth, *args, &block)
63
+ end
64
+
65
+ super
66
+ end
67
+
68
+ def authorize(token, secret)
69
+ @api = nil if @api
70
+ options[:token] = token
71
+ options[:secret] = secret
72
+ self
73
+ end
74
+
75
+ def options
76
+ @options ||= {}
77
+ end
78
+
79
+ def with_options(opts={})
80
+ options.merge!(opts)
81
+ self
82
+ end
83
+
84
+ def has_application_keys?
85
+ (Skypager.config.google_client_id.to_s.length > 0 && Skypager.config.google_client_secret.to_s.length > 0)
86
+ end
87
+
88
+ def get_application_keys
89
+ unless Skypager.config.google_client_id.to_s.length > 0
90
+ google_client_id = ask("What is the Google Client ID?", String)
91
+ Skypager.config.set "google_client_id", google_client_id
92
+ end
93
+
94
+ unless Skypager.config.google_client_secret.to_s.length > 0
95
+ google_client_secret = ask("What is the Google Client Secret?", String)
96
+ Skypager.config.set "google_client_secret", google_client_secret
97
+ end
98
+ end
99
+
100
+ def auth_client
101
+ return @auth_client if @auth_client
102
+
103
+ client = ::Google::APIClient.new(
104
+ :application_name => "google_drive Ruby library",
105
+ :application_version => "0.3.11"
106
+ )
107
+
108
+ client_id = "452925651630-egr1f18o96acjjvphpbbd1qlsevkho1d.apps.googleusercontent.com"
109
+ client_secret = "1U3-Krii5x1oLPrwD5zgn-ry"
110
+
111
+ @auth_client = auth = client.authorization
112
+ auth.client_id = client_id #Skypager.config.google_client_id
113
+ auth.client_secret = client_secret #Skypager.config.google_client_secret
114
+ auth.scope =
115
+ "https://www.googleapis.com/auth/drive " +
116
+ "https://spreadsheets.google.com/feeds/ " +
117
+ "https://docs.google.com/feeds/ " +
118
+ "https://docs.googleusercontent.com/"
119
+
120
+ auth.redirect_uri = "urn:ietf:wg:oauth:2.0:oob"
121
+
122
+ auth
123
+ end
124
+
125
+ def refresh_token
126
+ Skypager.config.google_refresh_token.to_s
127
+ end
128
+
129
+ def has_refresh_token?
130
+ refresh_token.length > 0
131
+ end
132
+
133
+ def refresh_access_token!
134
+ if has_refresh_token?
135
+ auth_client.refresh_token = refresh_token
136
+ auth_client.fetch_access_token!
137
+ Skypager.config.set "google_access_token", auth_client.access_token
138
+ end
139
+ end
140
+
141
+ end
142
+ end
143
+ end
@@ -0,0 +1,77 @@
1
+ require 'rubygems'
2
+ require 'rubygems/package'
3
+ require 'zlib'
4
+ require 'fileutils'
5
+
6
+ module Util
7
+ module Tar
8
+ # Creates a tar file in memory recursively
9
+ # from the given path.
10
+ #
11
+ # Returns a StringIO whose underlying String
12
+ # is the contents of the tar file.
13
+ def tar(path)
14
+ tarfile = StringIO.new("")
15
+ Gem::Package::TarWriter.new(tarfile) do |tar|
16
+ Dir[File.join(path, "**/*")].each do |file|
17
+ mode = File.stat(file).mode
18
+ relative_file = file.sub /^#{Regexp::escape path}\/?/, ''
19
+
20
+ if File.directory?(file)
21
+ tar.mkdir relative_file, mode
22
+ else
23
+ tar.add_file relative_file, mode do |tf|
24
+ File.open(file, "rb") { |f| tf.write f.read }
25
+ end
26
+ end
27
+ end
28
+ end
29
+
30
+ tarfile.rewind
31
+ tarfile
32
+ end
33
+
34
+ # gzips the underlying string in the given StringIO,
35
+ # returning a new StringIO representing the
36
+ # compressed file.
37
+ def gzip(tarfile)
38
+ gz = StringIO.new("")
39
+ z = Zlib::GzipWriter.new(gz)
40
+ z.write tarfile.string
41
+ z.close # this is necessary!
42
+
43
+ # z was closed to write the gzip footer, so
44
+ # now we need a new StringIO
45
+ StringIO.new gz.string
46
+ end
47
+
48
+ # un-gzips the given IO, returning the
49
+ # decompressed version as a StringIO
50
+ def ungzip(tarfile)
51
+ z = Zlib::GzipReader.new(tarfile)
52
+ unzipped = StringIO.new(z.read)
53
+ z.close
54
+ unzipped
55
+ end
56
+
57
+ # untars the given IO into the specified
58
+ # directory
59
+ def untar(io, destination)
60
+ Gem::Package::TarReader.new io do |tar|
61
+ tar.each do |tarfile|
62
+ destination_file = File.join destination, tarfile.full_name
63
+
64
+ if tarfile.directory?
65
+ FileUtils.mkdir_p destination_file
66
+ else
67
+ destination_directory = File.dirname(destination_file)
68
+ FileUtils.mkdir_p destination_directory unless File.directory?(destination_directory)
69
+ File.open destination_file, "wb" do |f|
70
+ f.print tarfile.read
71
+ end
72
+ end
73
+ end
74
+ end
75
+ end
76
+ end
77
+ end