skypager 0.1.2 → 0.2.0

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 (101) hide show
  1. checksums.yaml +4 -4
  2. data/Rakefile +0 -11
  3. data/bin/skypager +16 -10
  4. data/lib/skypager.rb +1 -69
  5. data/lib/skypager/cli.rb +14 -0
  6. data/lib/skypager/cli/01_extensions.rb +25 -0
  7. data/lib/skypager/cli/config.rb +41 -0
  8. data/lib/skypager/proxy.rb +0 -10
  9. data/lib/skypager/version.rb +1 -1
  10. data/skypager.gemspec +1 -15
  11. metadata +9 -216
  12. data/ARCHITECTURE.md +0 -34
  13. data/CONTRIBUTING.md +0 -7
  14. data/LICENSE.txt +0 -22
  15. data/bin/bootstrap.sh +0 -29
  16. data/examples/.gitignore +0 -4
  17. data/examples/blog-site/.gitignore +0 -18
  18. data/examples/blog-site/.pryrc +0 -4
  19. data/examples/blog-site/Gemfile +0 -8
  20. data/examples/blog-site/config.rb +0 -17
  21. data/examples/blog-site/data/dropbox.json +0 -1
  22. data/examples/blog-site/source/images/background.png +0 -0
  23. data/examples/blog-site/source/images/middleman.png +0 -0
  24. data/examples/blog-site/source/index.html.erb +0 -10
  25. data/examples/blog-site/source/javascripts/all.js +0 -1
  26. data/examples/blog-site/source/layouts/layout.erb +0 -19
  27. data/examples/blog-site/source/posts/introduction-to-skypager.html.md +0 -23
  28. data/examples/blog-site/source/posts/skypager-and-dnsimple-and-amazon-web-services-combo.html.md +0 -9
  29. data/examples/blog-site/source/stylesheets/all.css +0 -55
  30. data/examples/blog-site/source/stylesheets/normalize.css +0 -375
  31. data/examples/gallery-site/.gitignore +0 -18
  32. data/examples/gallery-site/.pryrc +0 -4
  33. data/examples/gallery-site/Gemfile +0 -11
  34. data/examples/gallery-site/config.rb +0 -38
  35. data/examples/gallery-site/data/dropbox.json +0 -1
  36. data/examples/gallery-site/data/galleries.json +0 -1
  37. data/examples/gallery-site/source/gallery.html.erb +0 -7
  38. data/examples/gallery-site/source/images/background.png +0 -0
  39. data/examples/gallery-site/source/images/galleries/cristian-gallery-1/001.jpg +0 -0
  40. data/examples/gallery-site/source/images/galleries/cristian-gallery-1/002.jpg +0 -0
  41. data/examples/gallery-site/source/images/galleries/cristian-gallery-1/003.jpg +0 -0
  42. data/examples/gallery-site/source/images/galleries/cristian-gallery-1/004.jpg +0 -0
  43. data/examples/gallery-site/source/images/galleries/luca-gallery-1/001.jpg +0 -0
  44. data/examples/gallery-site/source/images/galleries/luca-gallery-1/002.JPG +0 -0
  45. data/examples/gallery-site/source/images/galleries/luca-gallery-1/003.jpg +0 -0
  46. data/examples/gallery-site/source/images/galleries/luca-gallery-1/004.JPG +0 -0
  47. data/examples/gallery-site/source/images/middleman.png +0 -0
  48. data/examples/gallery-site/source/index.html.erb +0 -10
  49. data/examples/gallery-site/source/javascripts/all.js +0 -1
  50. data/examples/gallery-site/source/layouts/layout.erb +0 -20
  51. data/examples/gallery-site/source/stylesheets/all.css +0 -0
  52. data/examples/gallery-site/source/stylesheets/normalize.css +0 -375
  53. data/examples/gallery-site/source/tutorial.md +0 -151
  54. data/lib/skypager/builder.rb +0 -158
  55. data/lib/skypager/cli/commands/build.rb +0 -64
  56. data/lib/skypager/cli/commands/config.rb +0 -58
  57. data/lib/skypager/cli/commands/create.rb +0 -98
  58. data/lib/skypager/cli/commands/deploy.rb +0 -62
  59. data/lib/skypager/cli/commands/edit.rb +0 -32
  60. data/lib/skypager/cli/commands/list.rb +0 -12
  61. data/lib/skypager/cli/commands/setup.rb +0 -153
  62. data/lib/skypager/cli/commands/site.rb +0 -22
  63. data/lib/skypager/cli/commands/sync.rb +0 -20
  64. data/lib/skypager/data.rb +0 -8
  65. data/lib/skypager/data/excel_spreadsheet.rb +0 -8
  66. data/lib/skypager/data/google_spreadsheet.rb +0 -227
  67. data/lib/skypager/data/request.rb +0 -12
  68. data/lib/skypager/data/source.rb +0 -194
  69. data/lib/skypager/data/source_routes_proxy.rb +0 -30
  70. data/lib/skypager/dns.rb +0 -69
  71. data/lib/skypager/extension.rb +0 -271
  72. data/lib/skypager/middleman/commands/data.rb +0 -0
  73. data/lib/skypager/middleman/commands/deploy.rb +0 -0
  74. data/lib/skypager/middleman/commands/sync.rb +0 -0
  75. data/lib/skypager/sync.rb +0 -23
  76. data/lib/skypager/sync/amazon.rb +0 -181
  77. data/lib/skypager/sync/dropbox.rb +0 -173
  78. data/lib/skypager/sync/dropbox/delta.rb +0 -67
  79. data/lib/skypager/sync/folder.rb +0 -266
  80. data/lib/skypager/sync/github.rb +0 -55
  81. data/lib/skypager/sync/google.rb +0 -143
  82. data/spec/dummy/site-one/.gitignore +0 -18
  83. data/spec/dummy/site-one/Gemfile +0 -14
  84. data/spec/dummy/site-one/config.rb +0 -13
  85. data/spec/dummy/site-one/source/images/background.png +0 -0
  86. data/spec/dummy/site-one/source/images/middleman.png +0 -0
  87. data/spec/dummy/site-one/source/index.html.erb +0 -10
  88. data/spec/dummy/site-one/source/javascripts/all.js +0 -1
  89. data/spec/dummy/site-one/source/layouts/layout.erb +0 -19
  90. data/spec/dummy/site-one/source/stylesheets/all.css +0 -55
  91. data/spec/dummy/site-one/source/stylesheets/normalize.css +0 -375
  92. data/spec/lib/skypager/builder/server_spec.rb +0 -5
  93. data/spec/lib/skypager/configuration_spec.rb +0 -12
  94. data/spec/lib/skypager/data_spec.rb +0 -5
  95. data/spec/lib/skypager/site_spec.rb +0 -44
  96. data/spec/lib/skypager_spec.rb +0 -9
  97. data/spec/skypager-test-config.rb.example +0 -22
  98. data/spec/spec_helper.rb +0 -49
  99. data/spec/support/fixtures/cwd_config.json +0 -3
  100. data/spec/support/fixtures/home_config.json +0 -1
  101. data/spec/support/json_helper.rb +0 -7
@@ -1,12 +0,0 @@
1
- module Skypager
2
- module Data
3
- class Request < Source
4
- requires :host,
5
- :path
6
-
7
- def initialize name, options={}
8
- super
9
- end
10
- end
11
- end
12
- end
@@ -1,194 +0,0 @@
1
- module Skypager
2
- module Data
3
- class Source
4
- attr_reader :options, :name
5
- attr_accessor :raw, :processed, :format, :scopes, :slug_column, :refreshed_at, :path
6
-
7
- class << self
8
- attr_accessor :required_options
9
- end
10
-
11
- def self.requires *args
12
- self.required_options = args
13
- end
14
-
15
- def initialize(name, options={})
16
- @name ||= name
17
- @options ||= options
18
- @format ||= options.fetch(:format, :json)
19
- @path ||= options.fetch(:path) { Pathname(Dir.pwd()) }
20
-
21
- @slug_column = options.fetch(:slug_column, :_id)
22
-
23
- ensure_valid_options!
24
- end
25
-
26
- def to_s
27
- data.to_json
28
- end
29
-
30
- # defines a scope for the records in this data source
31
- # a scope is a named filter, implemented in the form of a block
32
- # which is passed each record. if the block returns true, it returns
33
- # the record:
34
- #
35
- # Example:
36
- #
37
- # data_source(:galleries) do
38
- # scope :active, -> {|record| record.state == "active" }
39
- # end
40
- def scope(*args, block)
41
- name = args.first
42
- (self.scopes ||= {})[name.to_sym] = block
43
- end
44
-
45
- def has_scope?(scope_name)
46
- scope_name && (self.scopes ||= {}).key?(scope_name.to_sym)
47
- end
48
-
49
- # compute properties takes the raw data of each record
50
- # and sets additional properties on the records which may
51
- # not be persited in the data source
52
- def compute_properties
53
- self.processed && self.processed.map! do |row|
54
- if slug_column && row.respond_to?(slug_column)
55
- row.slug = row.send(slug_column).to_s.parameterize
56
- end
57
-
58
- row
59
- end
60
-
61
- processors.each do |processor|
62
- original = self.processed.dup
63
- modified = []
64
-
65
- original.each_with_index do |record, index|
66
- previous = original[index - 1]
67
- modified.push(processor.call(record, index, previous: previous, set: original))
68
- end
69
-
70
- self.processed = modified
71
- end
72
- end
73
-
74
- def processors &block
75
- @processors ||= []
76
- @processors << block if block_given?
77
- @processors
78
- end
79
-
80
- # makes sure that the required options for this data source
81
- # are passed for any instance of the data source
82
- def ensure_valid_options!
83
- missing_options = (Array(self.class.required_options) - options.keys.map(&:to_sym))
84
-
85
- missing_options.reject! do |key|
86
- respond_to?(key) && send(key).present?
87
- end
88
-
89
- if missing_options.length > 0
90
- raise 'Error: failure to supply the following options:' + missing_options.map(&:to_s).join(",")
91
- end
92
- end
93
-
94
- def select(&block)
95
- data.select(&block)
96
- end
97
-
98
- def refresh
99
- fetch
100
- process
101
- self.refreshed_at = Time.now.to_i
102
- self
103
- end
104
-
105
- def refresh_if_stale?
106
- refresh! if stale?
107
- end
108
-
109
- # A data source is stale if it has been populated
110
- # and the age is greater than the max age we allow.
111
- def stale?
112
- !need_to_refresh? && (age > max_age)
113
- end
114
-
115
- def fresh_on_server?
116
- need_to_refresh?
117
- end
118
-
119
- def max_age
120
- max = ENV['MAX_DATA_SOURCE_AGE']
121
- (max && max.to_i) || 120
122
- end
123
-
124
- # how long since this data source has been refreshed?
125
- def age
126
- Time.now.to_i - refreshed_at.to_i
127
- end
128
-
129
- def data
130
- refresh if need_to_refresh?
131
- processed
132
- end
133
-
134
- def refresh!
135
- refresh
136
- save_to_disk
137
- end
138
-
139
- def need_to_refresh?
140
- !(@fetched && @_processed)
141
- end
142
-
143
- def fetch
144
- @fetched = true
145
- self.raw = []
146
- end
147
-
148
- def preprocess
149
- self.raw.dup
150
- end
151
-
152
- def process
153
- @_processed = true
154
- self.processed = preprocess
155
- # set_id
156
- compute_properties
157
- self.processed
158
- end
159
-
160
- def refreshed_at
161
- return @refreshed_at if @refreshed_at.to_i > 0
162
-
163
- if path_to_file.exist?
164
- @refreshed_at = File.mtime(path.join(file)).to_i
165
- end
166
- end
167
-
168
- def save_to_disk
169
- unless path_to_file.dirname.exist?
170
- FileUtils.mkdir(path_to_file.dirname)
171
- end
172
-
173
- path_to_file.open('w+') {|fh| fh.write(to_s) }
174
- end
175
-
176
- def persisted?
177
- path_to_file && path_to_file.exist?
178
- end
179
-
180
- def file
181
- @file ||= name.parameterize if name.respond_to?(:parameterize)
182
- @file.gsub!("-","_")
183
- @file = "#{@file}.json" unless @file.match(/\.json/i)
184
- @file
185
- end
186
-
187
- def path_to_file
188
- Pathname(path).join("#{ file }")
189
- end
190
- end
191
-
192
- end
193
- end
194
-
@@ -1,30 +0,0 @@
1
- require 'uri_template'
2
-
3
- module Skypager
4
- module Data
5
- class SourceRoutesProxy
6
- def initialize(app, data_source, options={})
7
- template = URITemplate.new(:colon, options[:url])
8
-
9
- data = data_source.data
10
-
11
- # If we're passed a scope that means we should
12
- # limit the set of records we are mapping to by the
13
- # scope defined in this data source
14
- if options[:scope]
15
- tester = data_source.scopes[options[:scope]]
16
- end
17
-
18
- to = options[:template] || options[:to]
19
-
20
- as = options.fetch(:as, :current_record)
21
-
22
- data.each do |row|
23
- url = template.expand(row.to_hash)
24
- app.proxy(url, to, :locals => {as => row}, :ignore => true)
25
- end
26
-
27
- end
28
- end
29
- end
30
- end
@@ -1,69 +0,0 @@
1
- module Skypager::DNS
2
- class Manager
3
- include Singleton
4
-
5
- def self.method_missing(meth, *args, &block)
6
- if client.respond_to?(meth)
7
- client.send(meth, *args, &block)
8
- end
9
- end
10
-
11
- def self.client
12
- instance.tap {|i| i.authorize }
13
- end
14
-
15
- def setup(options={})
16
- unless Skypager.config.dnsimple_api_token
17
- if dnsimple_api_token = options.fetch(:dnsimple_api_token) { ask("What is the DNSimple API Token?", String) }
18
- Skypager.config.set 'dnsimple_api_token', dnsimple_api_token
19
- end
20
- end
21
-
22
- unless Skypager.config.dnsimple_username
23
- if dnsimple_username = options.fetch(:dnsimple_username) { ask("What is the DNSimple Username or email?", String) }
24
- Skypager.config.set 'dnsimple_username', dnsimple_username
25
- end
26
- end
27
- end
28
-
29
- def authorize
30
- @authorized ||= begin
31
- DNSimple::Client.api_token = Skypager.config.dnsimple_api_token
32
- DNSimple::Client.username = Skypager.config.dnsimple_username
33
- end
34
- end
35
-
36
- def domain
37
- parent_domain
38
- end
39
-
40
- def parent_domain
41
- @parent_domain ||= DNSimple::Domain.find(Skypager.config.domain)
42
- end
43
-
44
- def domain_records
45
- DNSimple::Record.all(parent_domain)
46
- end
47
-
48
- def cname_records
49
- domain_records.select do |record|
50
- record.record_type == "CNAME"
51
- end
52
- end
53
-
54
- def deployment_alias_records
55
- cname_records.select do |record|
56
- record.content.include? "s3-website-us-east-1.amazonaws.com"
57
- end
58
- end
59
-
60
- # Used to link up a skypager internal site to an S3 Bucket
61
- def setup_cname internal_name, external_host
62
- authorize
63
-
64
- unless cname_records.find {|r| r.name == internal_name }
65
- DNSimple::Record.create(parent_domain, internal_name, 'CNAME', external_host)
66
- end
67
- end
68
- end
69
- end
@@ -1,271 +0,0 @@
1
- require 'middleman-core' unless defined?(::Middleman)
2
-
3
- module Skypager
4
- class Extension < ::Middleman::Extension
5
-
6
- option :site_name, nil, 'The name of this site'
7
- option :synced_folders, {}, 'The settings for dropbox source syncing'
8
- option :data_sources, [], 'The data source mappings for this site'
9
- option :deploy_options, {}, 'Deploy options: domain, bucket_name, use_cdn, aliases, auto_deploy'
10
- option :skypager, {}, 'Options to be passed to Skypager.config'
11
-
12
- def initialize(app, options_hash={}, &block)
13
- # if only skypager/extension is required this will be necessary
14
- require 'skypager' unless defined?(Skypager::Data)
15
-
16
- super
17
-
18
- app.include(ClassMethods)
19
-
20
- options_hash.reverse_merge!(:data_sources => {}.to_mash,
21
- :synced_folders => {}.to_mash,
22
- :deploy_options => {}.to_mash,
23
- :site_name => File.basename(app.root))
24
-
25
- options_hash.each do |key, value|
26
- app.set(key, value)
27
- end
28
-
29
- unless ($skypager_command || ENV['DISABLE_SKYPAGER_SYNC']) && !$enable_skypager_sync_from_command
30
- app.ready do
31
- sync_folders()
32
- load_stores()
33
- # Load the data source and save it to disk
34
- end
35
-
36
- # In development, we will try to refresh remote data stores (e.g. google spreadsheets)
37
- # before every request once every minute or so
38
- app.before do
39
- if !Skypager.config.dropbox_setup? && !Skypager.config.google_setup? && !Skypager.config.amazon_setup?
40
- puts "\n\n== Use Skypager's one time setup script to automate huge parts of your workflow!"
41
- puts "Run 'skypager setup' now"
42
- puts "\n\n\n"
43
- raise 'Run skypager setup first'
44
- end
45
-
46
- data_sources.values.each do |data_source|
47
- data_source.refresh_if_stale?
48
- end if development?
49
- end
50
- end
51
-
52
- app.before_configuration do
53
- end
54
-
55
- app.after_build do
56
- site.requires_build!(false)
57
- deploy! if auto_deploy?
58
- end
59
-
60
- app.after_configuration do
61
- save_site_config()
62
- end
63
-
64
- end
65
-
66
- helpers do
67
- def deploy!
68
- site.deploy(build_path)
69
- end
70
-
71
- def auto_deploy?
72
- !!deploy_options[:auto_deploy]
73
- end
74
-
75
- def save_site_config
76
- syncable_config = site.config.syncables ||= {}.to_mash
77
-
78
- syncables.each do |folder|
79
- type = folder.type
80
- cfg = syncable_config[type] ||= {}.to_mash
81
- cfg.paths ||= []
82
- cfg.paths.push(syncable.remote_path.to_s)
83
- cfg.paths.uniq!
84
- end
85
-
86
- if Skypager.config.dropbox_setup?
87
- syncable_config.dropbox ||= {}
88
- syncable_config.dropbox.uid = Skypager.dropbox.account.uid
89
- end
90
-
91
- site.set(:syncables, syncable_config)
92
- end
93
-
94
- def site
95
- return @site if @site
96
-
97
- @site = Skypager::Site.new(site_name, deploy_options: deploy_options).tap do |site|
98
- site.load_config
99
- site.set(:deploy_options, deploy_options)
100
- site.set(:root, root) unless site.config.key?(:root)
101
- end
102
- end
103
- end
104
-
105
- module ClassMethods
106
- def build_path
107
- Pathname(build_dir)
108
- end
109
-
110
- def data_path
111
- Pathname(data_dir)
112
- end
113
-
114
- def source_path
115
- Pathname(source_dir)
116
- end
117
-
118
- def deploy_to(provider, options={})
119
- self.deploy_options ||= {}
120
-
121
- self.deploy_options[:auto_deploy] = !!options[:auto_deploy]
122
-
123
- if provider == :amazon || provider == :aws
124
- self.deploy_options[:domain] = options[:domain] || Skypager.config.domain
125
- self.deploy_options[:bucket_name] = options[:bucket_name] || "#{ site_name }.#{ deploy_options[:domain] }"
126
- self.deploy_options[:custom_domain] = options[:custom_domain]
127
- end
128
- end
129
-
130
- def dropbox_sync(*args)
131
- folder = args.first
132
- options = args.extract_options!
133
-
134
- local_path = folder
135
- remote_path ||= options[:to] || options[:with] || args[1] || local_path
136
-
137
- dropbox_data[:folders] ||= {}
138
-
139
- f = dropbox_data[:folders][folder] ||= {
140
- remote_path: remote_path,
141
- local_path: local_path,
142
- type: "dropbox",
143
- cursor: nil,
144
- site_name: site_name,
145
- folder: folder
146
- }
147
-
148
- dropbox_data[:folders][folder].merge!(options)
149
-
150
- self.synced_folders[folder] = Skypager::Sync::Folder.new(f.merge(app: self, root:Pathname(root)))
151
-
152
- save_dropbox_settings
153
-
154
- end
155
-
156
- def app
157
- self
158
- end
159
-
160
- def stores
161
- data_sources.values
162
- end
163
-
164
- def load_stores persist=false
165
- data_sources.each do |name, source|
166
- source.refresh
167
- source.save_to_disk if persist || !source.persisted?
168
- data.callbacks(name, -> { source.data })
169
- end
170
-
171
- after_stores_loaded.each do |callback|
172
- callback = app.method(callback) if callback.is_a?(Symbol)
173
- app.instance_eval(&callback) if callback.respond_to?(:call)
174
- end
175
- end
176
-
177
- # middleman does have an event system, we can piggyback on that instead
178
- def after_stores_loaded &block
179
- @after_stores_callbacks ||= []
180
- @after_stores_callbacks << block if block_given?
181
- @after_stores_callbacks
182
- end
183
-
184
- def sync_folders
185
- syncables.each(&:sync)
186
- end
187
-
188
- def syncables
189
- @syncables ||= Array(synced_folders.values)
190
- end
191
-
192
- def syncable
193
- syncables.first
194
- end
195
-
196
- def save_dropbox_settings
197
- json = dropbox_data.to_json
198
- dropbox_data_file.open('w+') {|fh| fh.write(json) }
199
- end
200
-
201
- def dropbox_data
202
- unless Pathname(data_dir).exist?
203
- FileUtils.mkdir_p Pathname(data_dir)
204
- end
205
-
206
- @dropbox_data ||= begin
207
- JSON.parse(dropbox_data_file.read).to_mash
208
- rescue
209
- {}.to_mash
210
- end
211
- end
212
-
213
- def dropbox_data_file
214
- Pathname(data_dir).join('dropbox.json').tap do |path|
215
- unless path.exist?
216
- path.open('w+') {|fh| fh.write("{}") }
217
- end
218
- end
219
- end
220
-
221
- def google_spreadsheets
222
- Skypager::Data::GoogleSpreadsheet
223
- end
224
-
225
- def map_data_source(name, *args)
226
- options = args.extract_options!
227
- options[:data_source] = name
228
- scope = options[:scope] = args.first
229
-
230
- _data_source = data_sources[name]
231
-
232
- if !_data_source
233
- raise "Could not find a data source named #{ name }"
234
- end
235
-
236
- if scope && !_data_source.has_scope?(scope)
237
- raise "Invalid scope for #{name} data source"
238
- end
239
-
240
- Skypager::Data::SourceRoutesProxy.new(self, _data_source, options)
241
- end
242
-
243
- def data_source(name, options=nil, &block)
244
- if options.nil? && !block_given? && self.data_sources[name.to_sym]
245
- return self.data_sources[name.to_sym]
246
- end
247
-
248
- source = case
249
- when options[:type].to_s == "google"
250
- unless existing = google_spreadsheets[options[:key] || options[:title]]
251
- raise 'Could not find a spreadsheet. You can use the `skypager list spreadsheets` command to find the right one, or the `skypager create data source` command to create a new one.'
252
- end
253
-
254
- existing
255
- when options[:type].to_s == "excel"
256
-
257
- when options[:type].to_s == "request"
258
-
259
- end
260
-
261
- (self.data_sources[name.to_sym] = source).tap do |ds|
262
- ds.instance_eval(&block) if block.respond_to?(:call)
263
- ds.slug_column = (options[:slug] || options[:slug_column]) if (options[:slug] || options[:slug_column])
264
- ds.path ||= Pathname(root).join(data_dir)
265
- end
266
- end
267
- end
268
- end
269
- end
270
-
271
- ::Middleman::Extensions.register(:skypager, Skypager::Extension)