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
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