skypager 0.0.2

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 (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
File without changes
File without changes
File without changes
@@ -0,0 +1,208 @@
1
+ module Skypager
2
+ class Site
3
+ class << self
4
+ def directory
5
+ Skypager.config.sites_directory
6
+ end
7
+ end
8
+
9
+ attr_accessor :options,
10
+ :name,
11
+ :config
12
+
13
+ def initialize(name, options = {})
14
+ @name = name.to_s.downcase
15
+ @options = options
16
+ @config = self.class.directory[@name] || {
17
+ name: @name
18
+ }
19
+ end
20
+
21
+ def set attribute, value
22
+ @config[attribute.to_sym] = value
23
+ save!
24
+ self
25
+ end
26
+
27
+ def get! attribute
28
+ val = get(attribute)
29
+
30
+ if val.nil? || val.to_s.length == 0
31
+ nil
32
+ else
33
+ val
34
+ end
35
+ end
36
+
37
+ def get attribute
38
+ @config[attribute.to_sym]
39
+ end
40
+
41
+ def hostname= value
42
+ set(:hostname, value)
43
+ end
44
+
45
+ def hostname
46
+ custom_domain || bucket_hostname
47
+ end
48
+
49
+ def bucket_hostname
50
+ "#{@config[:name] || @name}.#{ Skypager.config.domain }"
51
+ end
52
+
53
+ def bucket(create=false)
54
+ return @bucket if @bucket
55
+
56
+ b = Skypager.amazon.find_bucket_by_name(bucket_hostname) rescue nil
57
+
58
+ if create && !b
59
+ b = Skypager.amazon.create_bucket(bucket_hostname)
60
+ end
61
+
62
+ @bucket = b
63
+ end
64
+
65
+ def deploy(build_dir)
66
+ target = bucket || Skypager.amazon.s3_bucket
67
+
68
+ upload = lambda do |node|
69
+ next if node.to_s == ".DS_Store"
70
+
71
+ if node.directory?
72
+ Array(node.children).each(&upload)
73
+ elsif node.file?
74
+ rel = node.relative_path_from(build_dir).to_s
75
+ puts "Deploying #{ rel }"
76
+ target.files.create(key: rel, body: File.open(node), public: true)
77
+ end
78
+ end
79
+
80
+ build_dir.children.each(&upload)
81
+ end
82
+
83
+ def use_cdn?
84
+ get(:use_cdn) == true || get(:use_cdn) == "yes" || get(:use_cdn) == "true"
85
+ end
86
+
87
+ def paths_to_invalidate(app)
88
+ app.sitemap.resources.map {|r| "/#{r.request_path}" }
89
+ end
90
+
91
+ def after_setup
92
+ if use_cdn? && !has_cdn?
93
+ Skypager.amazon.create_cdn_for(self)
94
+ end
95
+
96
+ if Skypager.config.dnsimple_setup?
97
+ setup_cname() unless custom_domain?
98
+ else
99
+ puts "You will need to setup the DNS for this bucket yourself."
100
+
101
+ if has_cdn?
102
+ puts "You will want to configure the DNS for #{ custom_domain }"
103
+ puts "Create a CNAME and point to #{ cname_value }"
104
+ else
105
+ puts "Create a CNAME for #{ cname_value } on #{ Skypager.config.domain } and point to #{ bucket_host }"
106
+ end
107
+ end
108
+ end
109
+
110
+ def custom_domain?
111
+ get!(:custom_domain)
112
+ end
113
+
114
+ def deploy_options_config_string
115
+ custom_domain = get!(:custom_domain)
116
+
117
+ cfg = []
118
+
119
+ if custom_domain.length > 0
120
+ cfg.push(":custom_domain => '#{ custom_domain }'")
121
+ cfg.push(":use_cdn => true")
122
+ else
123
+ cfg.push(":domain => '#{ Skypager.config.domain }'")
124
+ end
125
+
126
+ cfg.push(":bucket_name => '#{ bucket_name }'")
127
+
128
+ "\ndeploy_to(:amazon, #{cfg.join(", ")})\n"
129
+ end
130
+
131
+ def bucket_key
132
+ fetch(:bucket_key) { bucket && bucket.key }
133
+ end
134
+
135
+ def bucket_name
136
+ bucket_key
137
+ end
138
+
139
+ def custom_domain
140
+ value = get(:custom_domain).to_s
141
+ value if value.length > 0
142
+ end
143
+
144
+ def fetch(key, &block)
145
+ @config.fetch(key, &block)
146
+ end
147
+
148
+ def bucket_url
149
+ @bucket_url ||= fetch(:bucket_url) { Skypager.amazon.website_url_for(bucket) }
150
+ end
151
+
152
+ def bucket_host
153
+ @bucket_host ||= fetch(:bucket_host) { URI.parse("http://#{ bucket_url }").host }
154
+ end
155
+
156
+ def setup_cname
157
+ puts "Creating an alias for #{ cname_value } to #{ bucket_host }"
158
+ Skypager.dns.setup_cname(cname_value, bucket_host)
159
+ end
160
+
161
+ def cname_value
162
+ if custom_domain && has_cdn?
163
+ cdn.domain
164
+ else
165
+ name
166
+ end
167
+ end
168
+
169
+ def description
170
+ "#{ name }.#{ Skypager.config.domain }"
171
+ end
172
+
173
+ def has_cdn?
174
+ @cdn_distribution ||= Skypager.amazon.cdn.distributions.find do |cdn|
175
+ cdn.comment == description
176
+ end
177
+ end
178
+
179
+ def cdn
180
+ @cdn ||= Skypager.amazon.cdn.distributions.find do |cdn|
181
+ cdn.comment == description
182
+ end
183
+ end
184
+
185
+ def aliases
186
+ if custom_domain
187
+ [custom_domain, "www.#{ custom_domain }"]
188
+ end
189
+ end
190
+
191
+ def delete!
192
+ directory = self.class.directory
193
+ directory.delete(name)
194
+ Skypager.config.set :sites_directory, directory
195
+ end
196
+
197
+ def save!
198
+ directory = self.class.directory
199
+ directory[name] = to_config
200
+ Skypager.config.set :sites_directory, directory
201
+ end
202
+
203
+ def to_config
204
+ @config
205
+ end
206
+
207
+ end
208
+ end
@@ -0,0 +1,23 @@
1
+ module Skypager
2
+ module Sync
3
+ def self.sync_dropbox_path(path, options={})
4
+ dropbox.sync_path(path, options)
5
+ end
6
+
7
+ def self.amazon(options={})
8
+ require 'skypager/sync/amazon'
9
+ Skypager::Sync::Amazon.client
10
+ end
11
+
12
+ def self.google(options={})
13
+ require 'skypager/sync/google'
14
+ Skypager::Sync::Google.client
15
+ end
16
+
17
+ def self.dropbox(options={})
18
+ require 'skypager/sync/dropbox'
19
+ Skypager::Sync::Dropbox.client(options)
20
+ end
21
+ end
22
+ end
23
+
@@ -0,0 +1,171 @@
1
+ module Skypager
2
+ module Sync
3
+ class Amazon
4
+ include Singleton
5
+
6
+ require 'fog'
7
+
8
+ def aws_access_key_id
9
+ options[:aws_access_key_id] || options[:access_key_id] || Skypager.config.aws_access_key_id
10
+ end
11
+
12
+ def aws_secret_access_key
13
+ options[:aws_secret_access_key] || options[:secret_access_key] || Skypager.config.aws_secret_access_key
14
+ end
15
+
16
+ def storage
17
+ @storage ||= Fog::Storage.new({
18
+ provider: 'AWS',
19
+ aws_access_key_id: aws_access_key_id,
20
+ aws_secret_access_key: aws_secret_access_key,
21
+ path_style: true
22
+ })
23
+ end
24
+
25
+ def cdn
26
+ @cdn ||= Fog::CDN.new({
27
+ provider: 'AWS',
28
+ aws_access_key_id: aws_access_key_id,
29
+ aws_secret_access_key: aws_secret_access_key
30
+ })
31
+ end
32
+
33
+ def s3_bucket_website_url
34
+ if s3_bucket.is_a?(Fog::Storage::AWS::Directory)
35
+ website_url_for(s3_bucket)
36
+ end
37
+ end
38
+
39
+ def site_description
40
+ options[:description] || options[:site_name] || s3_bucket.key
41
+ end
42
+
43
+ # the domain, and the domain with www
44
+ def site_domain_aliases
45
+ options[:aliases]
46
+ end
47
+
48
+ def s3_bucket
49
+ if bucket_name = options[:bucket_name] || Skypager.config.get("bucket_name") || Skypager.app.deploy_options[:bucket_name]
50
+ if bucket = find_bucket_by_name(bucket_name)
51
+ return bucket
52
+ else
53
+ "There is no bucket named: #{ bucket_name }. You can create one by running 'skypager setup amazon --create-bucket=BUCKET_NAME"
54
+ end
55
+ else
56
+ puts "Can't determine what bucket name to use."
57
+ end
58
+ end
59
+
60
+ def create_cdn_for(site)
61
+ o = cdn_options(website_url: site.bucket_host,
62
+ comment: site.description,
63
+ aliases: site.aliases.join(","))
64
+
65
+ existing = cdn.distributions.find do |distribution|
66
+ distribution.comment == site.description
67
+ end
68
+
69
+ return existing if existing
70
+
71
+ cdn.distributions.create(o).tap do |distribution|
72
+ site.set distribution.domain
73
+ end
74
+ end
75
+
76
+ def cdn_options(o={})
77
+ {
78
+ enabled: true,
79
+ custom_origin: {
80
+ 'DNSName'=> o.fetch(:website_url) { s3_bucket_website_url },
81
+ 'OriginProtocolPolicy'=>'match-viewer'
82
+ },
83
+ comment: o.fetch(:description) { site_description },
84
+ caller_reference: Time.now.to_i.to_s,
85
+ cname: o.fetch(:domain_aliases) { site_domain_aliases },
86
+ default_root_object: 'index.html'
87
+ }
88
+ end
89
+
90
+ def self.client(options={})
91
+ @client ||= begin
92
+ instance.with_options(options)
93
+ end
94
+ end
95
+
96
+
97
+ def self.method_missing(meth, *args, &block)
98
+ if client.respond_to?(meth)
99
+ return client.send(meth, *args, &block)
100
+ end
101
+
102
+ super
103
+ end
104
+
105
+ def website_host_for(bucket_or_bucket_name)
106
+ URI.parse(webite_url_for(bucket_or_bucket_name)).host
107
+ end
108
+
109
+ def website_url_for(bucket_or_bucket_name)
110
+ bucket = bucket_or_bucket_name
111
+
112
+ if bucket_or_bucket_name.is_a?(String)
113
+ bucket = storage.directories.get(bucket_or_bucket_name)
114
+ end
115
+
116
+ if bucket
117
+ "http://#{bucket.key}.s3-website-#{ bucket.location }.amazonaws.com"
118
+ end
119
+ end
120
+
121
+ def find_or_create_bucket(bucket_name)
122
+ find_bucket_by_name(bucket_name) || create_bucket(bucket_name)
123
+ end
124
+
125
+ def find_bucket_by_name(bucket_name)
126
+ storage.directories.get(bucket_name) rescue nil
127
+ end
128
+
129
+ def create_bucket(bucket_name)
130
+ storage.directories.create(key: bucket_name, public: true).tap do |bucket|
131
+ storage.put_bucket_website(bucket_name, 'index.html', key: 'error.html')
132
+ storage.put_bucket_cors(bucket_name, {"AllowedOrigin"=>"*","AllowedMethod"=>"GET","AllowedHeader"=>"Authorization"})
133
+ end
134
+ end
135
+
136
+ def create_redirect_bucket(bucket_name, redirect_to_bucket_name)
137
+ create_bucket(redirect_to_bucket_name) unless find_bucket_by_name(redirect_to_bucket_name)
138
+ create_bucket(bucket_name)
139
+ end
140
+
141
+ def with_options(opts={})
142
+ options.merge!(opts)
143
+ self
144
+ end
145
+
146
+ def options
147
+ @options ||= {}
148
+ end
149
+
150
+ def has_application_keys?
151
+ (Skypager.config.aws_access_key_id.to_s.length > 0 && Skypager.config.aws_secret_access_key.to_s.length > 0)
152
+ end
153
+
154
+ def setup(options={})
155
+ return if has_application_keys?
156
+
157
+ unless Skypager.config.aws_access_key_id.to_s.length > 0
158
+ if value = options[:access_key_id] || ask("What is the AWS Access Key ID?", String)
159
+ Skypager.config.set 'aws_access_key_id', value
160
+ end
161
+ end
162
+
163
+ unless Skypager.config.aws_secret_access_key.to_s.length > 0
164
+ if value = options[:secret_access_key] || ask("What is the AWS Secret Access Key?", String)
165
+ Skypager.config.set 'aws_secret_access_key', value
166
+ end
167
+ end
168
+ end
169
+ end
170
+ end
171
+ end
@@ -0,0 +1,173 @@
1
+ module Skypager
2
+ module Sync
3
+ class Dropbox
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
+ ::Dropbox::API::Config.app_key = options.fetch(:dropbox_app_key) { Skypager.config.dropbox_app_key }
17
+ ::Dropbox::API::Config.app_secret = options.fetch(:dropbox_app_secret) { Skypager.config.dropbox_app_secret }
18
+ ::Dropbox::API::Config.mode = options.fetch(:dropbox_app_type) { Skypager.config.dropbox_app_type }
19
+ instance.with_options(options)
20
+ end
21
+ end
22
+
23
+ def sandboxed_app?
24
+ entry = api.ls.first
25
+ entry && entry.root == "app_folder"
26
+ end
27
+
28
+ def api
29
+ @api ||= begin
30
+ token = options.fetch(:token) { Skypager.config.dropbox_client_token }
31
+ secret = options.fetch(:secret) { Skypager.config.dropbox_client_secret }
32
+ ::Dropbox::API::Client.new(token: token , secret: secret)
33
+ end
34
+ end
35
+
36
+ def find(path)
37
+ api.find(path)
38
+ rescue ::Dropbox::API::Error::NotFound
39
+ nil
40
+ end
41
+
42
+ def push_from(local_path, options={})
43
+ path_prefix = options.fetch(:prefix)
44
+ root = options[:root]
45
+
46
+ unless find(path_prefix)
47
+ api.mkdir(path_prefix)
48
+ end
49
+
50
+ uploader = lambda do |node|
51
+ next if node.to_s == ".DS_Store"
52
+
53
+ if node.directory?
54
+ Array(node.children).each(&uploader)
55
+ elsif node.file?
56
+ relative = node.relative_path_from(local_path)
57
+ target = "#{path_prefix}/#{relative}"
58
+ target = target.gsub(/^\//,'')
59
+ api.upload(target, node.read)
60
+ end
61
+ end
62
+
63
+ Pathname(local_path).children.each(&uploader)
64
+ end
65
+
66
+ def create_site_folder(folder, allow_overwrite=false)
67
+ found = find(folder)
68
+
69
+ unless (!found || (found && !allow_overwrite))
70
+ api.mkdir("/#{folder}")
71
+ end
72
+ end
73
+
74
+ def sync(local_path, remote_path, options={})
75
+ sync_folders(local_path, remote_path, options)
76
+ end
77
+
78
+ # Instructs Skypager to create a syncable folder
79
+ # between the local and remote path, and optionally
80
+ # modifies the middleman config for the site
81
+ def sync_folders(local_path, remote_path, options={})
82
+ app = options[:app]
83
+
84
+ folder = Skypager::Sync::Folder.new(local_path: local_path,
85
+ remote_path: remote_path,
86
+ app: app).synced
87
+
88
+ if !!options[:append_config] == true
89
+ Skypager.append_config(folder.config_line)
90
+ end
91
+
92
+ folder
93
+ end
94
+
95
+ def method_missing meth, *args, &block
96
+ if api.respond_to?(meth)
97
+ return api.send(meth, *args, &block)
98
+ end
99
+
100
+ super
101
+ end
102
+
103
+ def authorize(token, secret)
104
+ @api = nil if @api
105
+ options[:token] = token
106
+ options[:secret] = secret
107
+ self
108
+ end
109
+
110
+ def options
111
+ @options ||= {}
112
+ end
113
+
114
+ def with_options(opts={})
115
+ options.merge!(opts)
116
+ self
117
+ end
118
+
119
+ def requires_setup?
120
+ !(dropbox_app_key.length > 0 && dropbox_app_secret.length > 0)
121
+ end
122
+
123
+ def dropbox_app_key
124
+ Skypager.config.dropbox_app_key.to_s
125
+ end
126
+
127
+ def dropbox_app_secret
128
+ Skypager.config.dropbox_app_secret.to_s
129
+ end
130
+
131
+ def setup(options={})
132
+ if requires_setup?
133
+ if dropbox_app_key.length == 0
134
+ if answer = options[:dropbox_app_key] || ask("What is the dropbox app key?", String)
135
+ Skypager.config.set("dropbox_app_key", answer)
136
+ end
137
+ if answer = options[:dropbox_app_secret] || ask("What is the dropbox app secret?", String)
138
+ Skypager.config.set("dropbox_app_secret", answer)
139
+ end
140
+ end
141
+ end
142
+
143
+ raise 'Missing dropbox application values' if requires_setup?
144
+
145
+ ::Dropbox::API::Config.app_key = Skypager.config.dropbox_app_key
146
+ ::Dropbox::API::Config.app_secret = Skypager.config.dropbox_app_secret
147
+
148
+ consumer = ::Dropbox::API::OAuth.consumer(:authorize)
149
+ request_token = consumer.get_request_token
150
+ puts "\nGo to this url and click 'Authorize' to get the token:"
151
+ puts request_token.authorize_url
152
+ Launchy.open(request_token.authorize_url)
153
+
154
+ query = request_token.authorize_url.split('?').last
155
+ params = CGI.parse(query)
156
+ token = params['oauth_token'].first
157
+ print "\nOnce you authorize the app on Dropbox, press enter... "
158
+ STDIN.gets.chomp
159
+
160
+ access_token = request_token.get_access_token(:oauth_verifier => token)
161
+
162
+ Skypager.config.set 'dropbox_client_token', access_token.token
163
+ Skypager.config.set 'dropbox_client_secret', access_token.secret
164
+
165
+ puts "\nAuthorization complete!:\n\n"
166
+ puts " Dropbox::API::Config.app_key = '#{consumer.key}'"
167
+ puts " Dropbox::API::Config.app_secret = '#{consumer.secret}'"
168
+ puts " client = Dropbox::API::Client.new(:token => '#{access_token.token}', :secret => '#{access_token.secret}')"
169
+ puts "\n"
170
+ end
171
+ end
172
+ end
173
+ end