posthaven_theme 0.1.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.
checksums.yaml ADDED
@@ -0,0 +1,7 @@
1
+ ---
2
+ SHA1:
3
+ metadata.gz: ed80c2c7459827fc24ad85f8a2dc41c022b0bd86
4
+ data.tar.gz: 9e5db4f8afb073fe26d361df97988833264369a6
5
+ SHA512:
6
+ metadata.gz: d4e53ab12a6670aa455366f7a68ab5c64d36d5b3e3383b3cea282324b8e4707377194e58d6c84e6f19846b584df2a6f518cb610ca0c658cd4b6383e9c437c4c3
7
+ data.tar.gz: 29c343541e6c298ca40bdbf8196742d64408460e5224def74e30121848c0f1a065f536cd430bff0f9a6c58e414ff0c2d25a34a336c1cf787c7a589df50750d4d
data/.gitignore ADDED
@@ -0,0 +1,5 @@
1
+ *.gem
2
+ .bundle
3
+ Gemfile.lock
4
+ pkg/*
5
+ .project
data/Gemfile ADDED
@@ -0,0 +1,4 @@
1
+ source "https://rubygems.org"
2
+
3
+ # Specify your gem's dependencies in posthaven_theme.gemspec
4
+ gemspec
data/MIT-LICENSE ADDED
@@ -0,0 +1,21 @@
1
+ Copyright (c) 2011 Shopify
2
+ Copyright (c) 2016 Posthaven
3
+
4
+ Permission is hereby granted, free of charge, to any person obtaining
5
+ a copy of this software and associated documentation files (the
6
+ "Software"), to deal in the Software without restriction, including
7
+ without limitation the rights to use, copy, modify, merge, publish,
8
+ distribute, sublicense, and/or sell copies of the Software, and to
9
+ permit persons to whom the Software is furnished to do so, subject to
10
+ the following conditions:
11
+
12
+ The above copyright notice and this permission notice shall be
13
+ included in all copies or substantial portions of the Software.
14
+
15
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
16
+ EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
17
+ MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
18
+ NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
19
+ LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
20
+ OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
21
+ WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
data/README.md ADDED
@@ -0,0 +1,87 @@
1
+ # Posthaven Theme
2
+
3
+ The `posthaven_theme` gem provides command line tools for developing a Posthaven theme on your computer and pushing updates to Posthaven.
4
+
5
+ # Requirements
6
+
7
+ ## Ruby
8
+
9
+ This gem requires Ruby 2.0 or above.
10
+
11
+ ## Posthaven API Key
12
+
13
+ Log in and get your [Posthaven account API key here](https://posthaven.com/account/theme_api_key).
14
+
15
+
16
+ # Installation
17
+
18
+ To install the `posthaven_theme` gem use 'gem install' (you might have use 'sudo gem install')
19
+
20
+ ```
21
+ gem install posthaven_theme
22
+ ```
23
+
24
+ to update to the latest version
25
+
26
+ ```
27
+ gem update posthaven_theme
28
+ ```
29
+
30
+ # Usage
31
+
32
+ The gem installs the `phtheme` CLI program.
33
+
34
+ ### List available commands
35
+ ```
36
+ phtheme help
37
+ ```
38
+
39
+ ### Generate a configuration file. For your API key see [above](#posthaven_account).
40
+
41
+ ```
42
+ phtheme configure api-key
43
+
44
+ ```
45
+ ### Upload all files
46
+
47
+ ```
48
+ phtheme upload
49
+ ```
50
+
51
+ ### Upload a single theme file
52
+
53
+ ```
54
+ phtheme upload layouts/theme.liquid
55
+ ```
56
+
57
+ ### Remove a theme file
58
+
59
+ ```
60
+ phtheme remove assets/layout.liquid
61
+ ```
62
+
63
+ ### Completely remove all old theme files and replace them with current local versions
64
+
65
+ ```
66
+ phtheme replace
67
+ ```
68
+
69
+ ### Watch the current theme directory and upload any files as they change
70
+ ```
71
+ phtheme watch
72
+ ```
73
+
74
+ # Configuration
75
+
76
+ Running `phtheme configure` generates `config.yml` file in the base directory of your theme. If you are storing your theme in version control it is **highly recommended that you DO NOT** store this file in version control, e.g. in git add it to your `.gitignore`.
77
+
78
+ `config.yml` has the following options:
79
+
80
+ * `api_key` – Your Posthaven API key
81
+ * `theme_id` – The ID of the theme to edit. The easiest way to populate the theme id is via the `configure` command above.
82
+
83
+ See the `phtheme configure` command above for one step setup of the `config.yml` file.
84
+
85
+ # Thanks
86
+
87
+ A huge thanks to [Shopify](https://www.shopify.com) for their [shopify_theme](https://github.com/shopify/shopify_theme) gem upon which this is based.
data/Rakefile ADDED
@@ -0,0 +1,14 @@
1
+ require 'bundler'
2
+ require 'bundler/gem_tasks'
3
+ require 'rake/testtask'
4
+
5
+ task :default => [:spec]
6
+
7
+ Rake::TestTask.new 'spec' do |t|
8
+ ENV['test'] = 'true'
9
+ t.libs = ['lib', 'spec']
10
+ t.ruby_opts << '-rubygems'
11
+ t.verbose = true
12
+ t.test_files = FileList['spec/**/*_spec.rb']
13
+ end
14
+
data/bin/phtheme ADDED
@@ -0,0 +1,24 @@
1
+ #!/usr/bin/env ruby
2
+
3
+ # This allows posthaven_theme to run easily from a git checkout without install.
4
+ # Hat tip to chriseppstein of Compass fame for the code for this
5
+ def fallback_load_path(path)
6
+ retried = false
7
+ begin
8
+ yield
9
+ rescue LoadError
10
+ unless retried
11
+ $: << path
12
+ retried = true
13
+ retry
14
+ end
15
+ raise
16
+ end
17
+ end
18
+
19
+ fallback_load_path(File.join(File.dirname(__FILE__), '..', 'lib')) do
20
+ require 'posthaven_theme'
21
+ require 'posthaven_theme/cli'
22
+ end
23
+
24
+ PosthavenTheme::Cli.start(ARGV)
@@ -0,0 +1,332 @@
1
+ require 'thor'
2
+ require 'yaml'
3
+ YAML::ENGINE.yamler = 'syck' if defined? Syck
4
+ require 'abbrev'
5
+ require 'base64'
6
+ require 'fileutils'
7
+ require 'json'
8
+ require 'filewatcher'
9
+ require 'mimemagic'
10
+
11
+ module PosthavenTheme
12
+ EXTENSIONS = [
13
+ {mimetype: 'application/x-liquid', extensions: %w(liquid), parents: 'text/plain'},
14
+ {mimetype: 'application/json', extensions: %w(json), parents: 'text/plain'},
15
+ {mimetype: 'application/js', extensions: %w(map), parents: 'text/plain'},
16
+ {mimetype: 'application/vnd.ms-fontobject', extensions: %w(eot)},
17
+ {mimetype: 'image/svg+xml', extensions: %w(svg svgz)}
18
+ ]
19
+
20
+ def self.configure_mime_magic
21
+ PosthavenTheme::EXTENSIONS.each do |extension|
22
+ MimeMagic.add(extension.delete(:mimetype), extension)
23
+ end
24
+ end
25
+
26
+ class Cli < Thor
27
+ include Thor::Actions
28
+
29
+ IGNORE = %w(config.yml)
30
+ DEFAULT_WHITELIST = %w(layouts/ assets/ config/ snippets/ templates/)
31
+ TIMEFORMAT = "%H:%M:%S"
32
+
33
+ NON_CONFIG_COMMANDS = %w{help configure systeminfo}
34
+
35
+ def initialize(args = [], local_options = {}, config = {})
36
+ unless NON_CONFIG_COMMANDS.include?(config[:current_command].name)
37
+ setup_config
38
+ validate_config!
39
+ end
40
+ super
41
+ end
42
+
43
+ desc "check", "Check configuration"
44
+ def check
45
+ say('Configuration [OK]', :green) if PosthavenTheme.asset_list
46
+ rescue PosthavenTheme::APIError => e
47
+ report_error('Configuration [FAIL]', e)
48
+ end
49
+
50
+ desc 'configure API_KEY',
51
+ 'Generate a config.yml file – See https://github.com/posthaven/posthaven_theme/#posthaven ' +
52
+ 'for information on how to get your api key. Interactively selects (or creates) a theme' +
53
+ 'on your account to edit.'
54
+ def configure(api_key=nil, theme_id=nil)
55
+ if api_key.nil?
56
+ say('An api key is required!', :red)
57
+ help(:configure)
58
+ else
59
+ config = {api_key: api_key, theme_id: theme_id}
60
+ PosthavenTheme.config = config
61
+ themes = PosthavenTheme.theme_list
62
+ config[:theme_id] ||= select_theme(themes)
63
+ create_file('config.yml', config.to_yaml)
64
+ end
65
+ rescue PosthavenTheme::APIError
66
+ say('Configuration Failed – Your API Key is Likely Incorrect', :red)
67
+ end
68
+
69
+ desc 'upload FILE', 'Upload all theme assets to site'
70
+ method_option :quiet, type: :boolean, default: false
71
+ def upload(*paths)
72
+ assets = paths.empty? ? local_assets_list : paths
73
+ assets.each do |asset|
74
+ send_asset(asset, options['quiet'])
75
+ end
76
+ say("Done.", :green) unless options['quiet']
77
+ rescue PosthavenTheme::APIError => e
78
+ report_error('Upload Failed.', e)
79
+ end
80
+
81
+ desc 'replace FILE', 'Completely replace site theme assets with local theme assets'
82
+ method_option :quiet, type: :boolean, default: false
83
+ def replace(*paths)
84
+ say('Are you sure you want to completely replace your site theme assets? ' +
85
+ 'This is not undoable.',
86
+ :yellow)
87
+ if ask('Continue? (Y/N): ') == 'Y'
88
+ # only delete files on remote that are not present locally
89
+ # files present on remote and present locally get overridden anyway
90
+ remote_assets = if paths.empty?
91
+ (PosthavenTheme.asset_list.map { |a| a['path'] } - local_assets_list)
92
+ else
93
+ paths
94
+ end
95
+ remote_assets.each do |asset|
96
+ unless PosthavenTheme.ignore_files.any? { |regex| regex =~ asset }
97
+ delete_asset(asset, options['quiet'])
98
+ end
99
+ end
100
+ local_assets = paths.empty? ? local_assets_list : paths
101
+ local_assets.each do |asset|
102
+ send_asset(asset, options['quiet'])
103
+ end
104
+ say("Done.", :green) unless options['quiet']
105
+ end
106
+ rescue PosthavenTheme::APIError => e
107
+ report_error('Replacement failed.', e)
108
+ end
109
+
110
+ desc 'remove FILE', 'Remove theme asset'
111
+ method_option :quiet, type: :boolean, default: false
112
+ def remove(*paths)
113
+ paths.each do |path|
114
+ delete_asset(path, options['quiet'])
115
+ end
116
+ say("Done.", :green) unless options['quiet']
117
+ rescue PosthavenTheme::APIError => e
118
+ report_error("Could not remove.", e)
119
+ end
120
+
121
+ desc 'watch',
122
+ 'upload and delete individual theme assets as they change, ' +
123
+ 'use the --keep_files flag to disable remote file deletion'
124
+ method_option :quiet, type: :boolean, default: false
125
+ method_option :keep_files, type: :boolean, default: false
126
+ def watch
127
+ puts "Watching current folder: #{Dir.pwd}"
128
+ watcher do |filename, event|
129
+ filename = filename.gsub("#{Dir.pwd}/", '')
130
+
131
+ next unless local_assets_list.include?(filename)
132
+ action = if [:changed, :new].include?(event)
133
+ :send_asset
134
+ elsif event == :delete
135
+ :delete_asset
136
+ else
137
+ raise NotImplementedError, "Unknown event -- #{event} -- #{filename}"
138
+ end
139
+
140
+ begin
141
+ send(action, filename, options['quiet'])
142
+ rescue PosthavenTheme::APIError => e
143
+ verb = action == :send_asset ? 'save' : 'delete'
144
+ report_error("Unable to #{verb} asset.", e)
145
+ end
146
+ end
147
+ end
148
+
149
+ desc 'systeminfo',
150
+ 'print out system information and actively loaded libraries for aiding in submitting bug reports'
151
+ def systeminfo
152
+ ruby_version = "#{RUBY_VERSION}"
153
+ ruby_version += "-p#{RUBY_PATCHLEVEL}" if RUBY_PATCHLEVEL
154
+ puts "Ruby: v#{ruby_version}"
155
+ puts "Operating System: #{RUBY_PLATFORM}"
156
+ %w(Thor Listen HTTParty).each do |lib|
157
+ require "#{lib.downcase}/version"
158
+ puts "#{lib}: v" + Kernel.const_get("#{lib}::VERSION")
159
+ end
160
+ end
161
+
162
+ protected
163
+
164
+ def config
165
+ @config ||= YAML.load_file 'config.yml'
166
+ end
167
+
168
+ def site_theme_url
169
+ url = config[:site]
170
+ url += "?preview_theme_id=#{config[:theme_id]}" if config[:theme_id] && config[:theme_id].to_i > 0
171
+ url
172
+ end
173
+
174
+ private
175
+
176
+ def watcher
177
+ FileWatcher.new(Dir.pwd).watch() do |filename, event|
178
+ yield(filename, event)
179
+ end
180
+ end
181
+
182
+ def local_assets_list
183
+ local_files.reject do |p|
184
+ @permitted_files ||= (DEFAULT_WHITELIST | PosthavenTheme.whitelist_files).map{|pattern| Regexp.new(pattern)}
185
+ @permitted_files.none? { |regex| regex =~ p } || PosthavenTheme.ignore_files.any? { |regex| regex =~ p }
186
+ end
187
+ end
188
+
189
+ def local_files
190
+ Dir.glob(File.join('**', '*')).reject do |f|
191
+ File.directory?(f)
192
+ end
193
+ end
194
+
195
+ def download_asset(path)
196
+ return unless valid?(path)
197
+ asset = PosthavenTheme.get_asset(path)
198
+ if asset['value']
199
+ # For CRLF line endings
200
+ content = asset['value'].gsub("\r", "")
201
+ format = "w"
202
+ elsif asset['attachment']
203
+ content = Base64.decode64(asset['attachment'])
204
+ format = "w+b"
205
+ end
206
+
207
+ FileUtils.mkdir_p(File.dirname(path))
208
+ File.open(path, format) {|f| f.write content} if content
209
+ rescue PosthavenTheme::APIError => e
210
+ report_error(Time.now, "Could not download #{path}", e)
211
+ end
212
+
213
+ def send_asset(asset, quiet=false)
214
+ return unless valid?(asset)
215
+ data = {path: asset}
216
+ content = File.read(asset)
217
+ if binary_file?(asset) || PosthavenTheme.is_binary_data?(content)
218
+ content = File.open(asset, "rb") { |io| io.read }
219
+ data.merge!(attachment: Base64.encode64(content))
220
+ else
221
+ data.merge!(value: content)
222
+ end
223
+
224
+ response = show_during("[#{timestamp}] Uploading: #{asset}", quiet) do
225
+ PosthavenTheme.send_asset(data)
226
+ end
227
+ if response.success?
228
+ say("[#{timestamp}] Uploaded: #{asset}", :green) unless quiet
229
+ end
230
+ end
231
+
232
+ def delete_asset(path, quiet=false)
233
+ return unless valid?(path)
234
+ response = show_during("[#{timestamp}] Removing: #{path}", quiet) do
235
+ PosthavenTheme.delete_asset(path)
236
+ end
237
+ if response.success?
238
+ say("[#{timestamp}] Removed: #{path}", :green) unless quiet
239
+ end
240
+ end
241
+
242
+ def select_theme(existing)
243
+ opt = ask_with_options('Configure the theme to edit:', [
244
+ 'Create a new theme',
245
+ 'Select an existing theme',
246
+ ])
247
+ case opt
248
+ when 0
249
+ create_theme['id']
250
+ when 1
251
+ existing.sort_by! { |t| t['name'] }
252
+ n = ask_with_options("Select from your existing themes:", existing.map { |t| t['name'] })
253
+ existing[n]['id']
254
+ end
255
+ end
256
+
257
+ # Options are strings to be numbered. Index of selected option is returned.
258
+ def ask_with_options(prompt, options)
259
+ say(prompt)
260
+ options.each.with_index do |o, idx|
261
+ say(" %2d: %s" % [idx + 1, o])
262
+ end
263
+ n = ask('Select>', limited_to: (1..options.size).map(&:to_s))
264
+ n.to_i - 1
265
+ end
266
+
267
+ def create_theme
268
+ name = ''
269
+ begin
270
+ if (name = ask('What would you like to name your theme?')).size == 0
271
+ say('Oops, the theme needs a name (you can change it later).', :red)
272
+ end
273
+ end until name.size > 0
274
+ PosthavenTheme.create_theme(name: name)
275
+ end
276
+
277
+ def notify_and_sleep(message)
278
+ say(message, :red)
279
+ PosthavenTheme.sleep
280
+ end
281
+
282
+ def valid?(path)
283
+ return true if DEFAULT_WHITELIST.include?(path.split('/').first + "/")
284
+ say("'#{path}' is not in a valid file for theme uploads", :yellow)
285
+ say("Files need to be in one of the following subdirectories: #{DEFAULT_WHITELIST.join(' ')}", :yellow)
286
+ false
287
+ end
288
+
289
+ def binary_file?(path)
290
+ mime = MimeMagic.by_path(path)
291
+ say("'#{path}' is an unknown file-type, uploading asset as binary", :yellow) if mime.nil? && ENV['TEST'] != 'true'
292
+ mime.nil? || !mime.text?
293
+ end
294
+
295
+ def report_error(message, error)
296
+ say("[#{timestamp(Time.now)}] Error: #{message}", :red)
297
+ say("Error Details: #{error.message}", :yellow)
298
+ end
299
+
300
+ def show_during(message = '', quiet = false, &block)
301
+ print(message) unless quiet
302
+ result = yield
303
+ print("\r#{' ' * message.length}\r") unless quiet
304
+ result
305
+ rescue
306
+ print("\n") unless quiet
307
+ raise
308
+ end
309
+
310
+ def timestamp(time = Time.now)
311
+ time.strftime(TIMEFORMAT)
312
+ end
313
+
314
+ def setup_config
315
+ if File.exist?('config.yml')
316
+ PosthavenTheme.config = YAML.load(File.read('config.yml'))
317
+ end
318
+ end
319
+
320
+ def validate_config!
321
+ if PosthavenTheme.config.empty?
322
+ say 'config.yml does not exist or is empty!', :red
323
+ exit
324
+ elsif %i{api_key theme_id}.any? { |k| PosthavenTheme.config[k].nil? }
325
+ say 'config.yml must include :api_key: and :theme_id:!', :red
326
+ exit
327
+ end
328
+ end
329
+ end
330
+ end
331
+
332
+ PosthavenTheme.configure_mime_magic
@@ -0,0 +1,3 @@
1
+ module PosthavenTheme
2
+ VERSION = '0.1.0'
3
+ end
@@ -0,0 +1,180 @@
1
+ require 'posthaven_theme/version'
2
+ require 'httparty'
3
+
4
+ module PosthavenTheme
5
+ include HTTParty
6
+ @@current_api_call_count = 0
7
+ @@total_api_calls = 40
8
+
9
+ headers "User-Agent" => "posthaven_theme #{PosthavenTheme::VERSION}"
10
+
11
+ class APIError < StandardError
12
+ attr_accessor :response
13
+
14
+ def initialize(response)
15
+ @response = response
16
+ end
17
+
18
+ def message
19
+ "#{response_message} – #{response_error_message(response)}"
20
+ end
21
+
22
+ private
23
+
24
+ def response_message
25
+ if @response.response && @response.response.message && @response.response.message.size > 0
26
+ @response.response.message
27
+ else
28
+ @response.code
29
+ end
30
+ end
31
+
32
+ def response_error_message(response)
33
+ errors = @response.parsed_response ? @response.parsed_response["errors"] : @response.body
34
+ case errors
35
+ when NilClass
36
+ ''
37
+ when String
38
+ errors.strip
39
+ when Array
40
+ errors.join(", ")
41
+ end
42
+ end
43
+ end
44
+
45
+ TIMER_RESET = 10
46
+ PERMIT_LOWER_LIMIT = 3
47
+
48
+ DEFAULT_API_ENDPOINT = 'https://api.posthaven.com/v1'
49
+
50
+ def self.test?
51
+ ENV['test']
52
+ end
53
+
54
+ def self.manage_timer(response)
55
+ return unless response.headers['x-posthaven-api-call-limit']
56
+ @@current_api_call_count, @@total_api_calls = response.headers['x-posthaven-api-call-limit']
57
+ .split('/')
58
+ @@current_timer = Time.now if @current_timer.nil?
59
+ end
60
+
61
+ def self.critical_permits?
62
+ @@total_api_calls.to_i - @@current_api_call_count.to_i < PERMIT_LOWER_LIMIT
63
+ end
64
+
65
+ def self.passed_api_refresh?
66
+ delta_seconds > TIMER_RESET
67
+ end
68
+
69
+ def self.delta_seconds
70
+ Time.now.to_i - @@current_timer.to_i
71
+ end
72
+
73
+ def self.needs_sleep?
74
+ critical_permits? && !passed_api_refresh?
75
+ end
76
+
77
+ def self.sleep
78
+ if needs_sleep?
79
+ Kernel.sleep(TIMER_RESET - delta_seconds)
80
+ @current_timer = nil
81
+ end
82
+ end
83
+
84
+ def self.api_usage
85
+ "[API Limit: #{@@current_api_call_count || "??"}/#{@@total_api_calls || "??"}]"
86
+ end
87
+
88
+ def self.theme_list
89
+ handle_response(get(themes_path))
90
+ end
91
+
92
+ def self.create_theme(data)
93
+ handle_response(post(themes_path, body: {theme: data}))
94
+ end
95
+
96
+ def self.asset_list
97
+ handle_response(get(assets_path))
98
+ end
99
+
100
+ def self.get_asset(asset)
101
+ handle_response(get(asset_path(asset)))
102
+ end
103
+
104
+ def self.send_asset(data)
105
+ handle_response(put(asset_path(data[:path]), body: {asset: data}))
106
+ end
107
+
108
+ def self.delete_asset(asset)
109
+ handle_response(delete(asset_path(asset)))
110
+ end
111
+
112
+ def self.config
113
+ @config
114
+ end
115
+
116
+ def self.config=(config)
117
+ @config = config && Hash[config.map { |k, v| [k.to_sym, v] }]
118
+ setup
119
+ end
120
+
121
+ def self.themes_path
122
+ '/themes.json'
123
+ end
124
+ def self.theme_path(theme_id = config[:theme_id])
125
+ "/themes/#{theme_id}"
126
+ end
127
+
128
+ def self.assets_path
129
+ theme_path + "/assets.json"
130
+ end
131
+ def self.asset_path(path)
132
+ theme_path + "/asset.json?path=#{path}"
133
+ end
134
+
135
+ def self.ignore_files
136
+ (config[:ignore_files] || []).compact.map { |r| Regexp.new(r) }
137
+ end
138
+
139
+ def self.whitelist_files
140
+ (config[:whitelist_files] || []).compact
141
+ end
142
+
143
+ def self.is_binary_data?(string)
144
+ if string.respond_to?(:encoding)
145
+ string.encoding == "US-ASCII"
146
+ else
147
+ unless string.empty?
148
+ (string.count("^ -~", "^\r\n").fdiv(string.size) > 0.3 || string.index("\x00"))
149
+ end
150
+ end
151
+ end
152
+
153
+ private
154
+
155
+ def self.handle_response(response)
156
+ manage_timer(response)
157
+
158
+ if response.success?
159
+ if response.parsed_response
160
+ response.parsed_response["data"]
161
+ else
162
+ response
163
+ end
164
+ else
165
+ raise APIError.new(response)
166
+ end
167
+ end
168
+
169
+ def self.setup
170
+ # Basics
171
+ headers 'Authorization' => "Token #{config[:api_key]}"
172
+ base_uri (config[:api_endpoint] || ENV['PHTHEME_ENDPOINT'] || DEFAULT_API_ENDPOINT)
173
+
174
+ # Dev
175
+ require 'resolv-replace' if base_uri.include?(':')
176
+
177
+ debug_output $stdout if config[:debug] || ENV['PHTHEME_DEBUG']
178
+ default_options.update(verify: false) if ENV['PHTHEME_IGNORE_SSL_VERIFY']
179
+ end
180
+ end
@@ -0,0 +1,32 @@
1
+ # -*- encoding: utf-8 -*-
2
+ $:.push File.expand_path("../lib", __FILE__)
3
+ require "posthaven_theme/version"
4
+
5
+ Gem::Specification.new do |s|
6
+ s.name = "posthaven_theme"
7
+ s.version = PosthavenTheme::VERSION
8
+ s.platform = Gem::Platform::RUBY
9
+ s.authors = ["Brett Gibson"]
10
+ s.email = ["help@posthaven.com"]
11
+ s.homepage = "https://github.com/posthaven/posthaven_theme"
12
+ s.summary = %q{Command line tool for developing themes}
13
+ s.description = %q{Command line tool to help with developing Posthaven themes. Provides simple commands to download, upload and delete files from a theme. Also includes the watch command to watch a directory and upload files as they change.}
14
+ s.license = 'MIT'
15
+
16
+ s.required_ruby_version = '>= 2.0'
17
+
18
+ s.rubyforge_project = "posthaven_theme"
19
+ s.add_dependency('thor', '>= 0.14.4')
20
+ s.add_dependency('httparty', '~> 0.13.0')
21
+ s.add_dependency('json', '~> 1.8.0')
22
+ s.add_dependency('mimemagic')
23
+ s.add_dependency('filewatcher')
24
+
25
+ s.add_development_dependency 'rake'
26
+ s.add_development_dependency 'minitest', '>= 5.0.0'
27
+
28
+ s.files = `git ls-files`.split("\n")
29
+ s.test_files = `git ls-files -- {test,spec,features}/*`.split("\n")
30
+ s.executables = `git ls-files -- bin/*`.split("\n").map{ |f| File.basename(f) }
31
+ s.require_paths = ['lib']
32
+ end
@@ -0,0 +1,2 @@
1
+ ENV['TEST'] = 'true'
2
+ require 'minitest/autorun'
@@ -0,0 +1,68 @@
1
+ require 'spec_helper'
2
+ require 'ostruct'
3
+ require 'posthaven_theme'
4
+ require 'posthaven_theme/cli'
5
+
6
+ module PosthavenTheme
7
+ describe "Cli" do
8
+
9
+ class CliDouble < Cli
10
+ attr_writer :local_files, :mock_config
11
+
12
+ desc "",""
13
+ def config
14
+ @mock_config || super
15
+ end
16
+ desc "",""
17
+ def binary_file?(file)
18
+ super
19
+ end
20
+
21
+ desc "", ""
22
+ def local_files
23
+ @local_files
24
+ end
25
+ end
26
+
27
+ let(:command_name) { 'help' }
28
+ let(:command) { Thor::Command.new(command_name, nil, nil, nil, {}) }
29
+ let(:cli) { CliDouble.new([], {}, {current_command: command}) }
30
+
31
+ before { PosthavenTheme.config = {} }
32
+
33
+ it "should remove assets that are not a part of the white list" do
34
+ cli.local_files = ['assets/image.png', 'config.yml', 'layouts/theme.liquid']
35
+ local_assets_list = cli.send(:local_assets_list)
36
+ assert_equal 2, local_assets_list.length
37
+ assert_equal false, local_assets_list.include?('config.yml')
38
+ end
39
+
40
+ it "should remove assets that are part of the ignore list" do
41
+ PosthavenTheme.config = {ignore_files: ['config/settings.html']}
42
+ cli.local_files = ['assets/image.png', 'layouts/theme.liquid', 'config/settings.html']
43
+ local_assets_list = cli.send(:local_assets_list)
44
+ assert_equal 2, local_assets_list.length
45
+ assert_equal false, local_assets_list.include?('config/settings.html')
46
+ end
47
+
48
+ it "should report binary files as such" do
49
+ extensions = %w(png gif jpg jpeg eot svg ttf woff otf swf ico pdf)
50
+ extensions.each do |ext|
51
+ assert cli.binary_file?("hello.#{ext}"), "#{ext.upcase}s are binary files"
52
+ end
53
+ end
54
+
55
+ it "should report unknown files as binary files" do
56
+ assert cli.binary_file?('omg.wut'), "Unknown filetypes are assumed to be binary"
57
+ end
58
+
59
+ it "should not report text based files as binary" do
60
+ refute cli.binary_file?('theme.liquid'), "liquid files are not binary"
61
+ refute cli.binary_file?('style.sass.liquid'), "sass.liquid files are not binary"
62
+ refute cli.binary_file?('style.css'), 'CSS files are not binary'
63
+ refute cli.binary_file?('application.js'), 'Javascript files are not binary'
64
+ refute cli.binary_file?('settings_data.json'), 'JSON files are not binary'
65
+ refute cli.binary_file?('applicaton.js.map'), 'Javascript Map files are not binary'
66
+ end
67
+ end
68
+ end
metadata ADDED
@@ -0,0 +1,160 @@
1
+ --- !ruby/object:Gem::Specification
2
+ name: posthaven_theme
3
+ version: !ruby/object:Gem::Version
4
+ version: 0.1.0
5
+ platform: ruby
6
+ authors:
7
+ - Brett Gibson
8
+ autorequire:
9
+ bindir: bin
10
+ cert_chain: []
11
+ date: 2016-08-08 00:00:00.000000000 Z
12
+ dependencies:
13
+ - !ruby/object:Gem::Dependency
14
+ name: thor
15
+ requirement: !ruby/object:Gem::Requirement
16
+ requirements:
17
+ - - ">="
18
+ - !ruby/object:Gem::Version
19
+ version: 0.14.4
20
+ type: :runtime
21
+ prerelease: false
22
+ version_requirements: !ruby/object:Gem::Requirement
23
+ requirements:
24
+ - - ">="
25
+ - !ruby/object:Gem::Version
26
+ version: 0.14.4
27
+ - !ruby/object:Gem::Dependency
28
+ name: httparty
29
+ requirement: !ruby/object:Gem::Requirement
30
+ requirements:
31
+ - - "~>"
32
+ - !ruby/object:Gem::Version
33
+ version: 0.13.0
34
+ type: :runtime
35
+ prerelease: false
36
+ version_requirements: !ruby/object:Gem::Requirement
37
+ requirements:
38
+ - - "~>"
39
+ - !ruby/object:Gem::Version
40
+ version: 0.13.0
41
+ - !ruby/object:Gem::Dependency
42
+ name: json
43
+ requirement: !ruby/object:Gem::Requirement
44
+ requirements:
45
+ - - "~>"
46
+ - !ruby/object:Gem::Version
47
+ version: 1.8.0
48
+ type: :runtime
49
+ prerelease: false
50
+ version_requirements: !ruby/object:Gem::Requirement
51
+ requirements:
52
+ - - "~>"
53
+ - !ruby/object:Gem::Version
54
+ version: 1.8.0
55
+ - !ruby/object:Gem::Dependency
56
+ name: mimemagic
57
+ requirement: !ruby/object:Gem::Requirement
58
+ requirements:
59
+ - - ">="
60
+ - !ruby/object:Gem::Version
61
+ version: '0'
62
+ type: :runtime
63
+ prerelease: false
64
+ version_requirements: !ruby/object:Gem::Requirement
65
+ requirements:
66
+ - - ">="
67
+ - !ruby/object:Gem::Version
68
+ version: '0'
69
+ - !ruby/object:Gem::Dependency
70
+ name: filewatcher
71
+ requirement: !ruby/object:Gem::Requirement
72
+ requirements:
73
+ - - ">="
74
+ - !ruby/object:Gem::Version
75
+ version: '0'
76
+ type: :runtime
77
+ prerelease: false
78
+ version_requirements: !ruby/object:Gem::Requirement
79
+ requirements:
80
+ - - ">="
81
+ - !ruby/object:Gem::Version
82
+ version: '0'
83
+ - !ruby/object:Gem::Dependency
84
+ name: rake
85
+ requirement: !ruby/object:Gem::Requirement
86
+ requirements:
87
+ - - ">="
88
+ - !ruby/object:Gem::Version
89
+ version: '0'
90
+ type: :development
91
+ prerelease: false
92
+ version_requirements: !ruby/object:Gem::Requirement
93
+ requirements:
94
+ - - ">="
95
+ - !ruby/object:Gem::Version
96
+ version: '0'
97
+ - !ruby/object:Gem::Dependency
98
+ name: minitest
99
+ requirement: !ruby/object:Gem::Requirement
100
+ requirements:
101
+ - - ">="
102
+ - !ruby/object:Gem::Version
103
+ version: 5.0.0
104
+ type: :development
105
+ prerelease: false
106
+ version_requirements: !ruby/object:Gem::Requirement
107
+ requirements:
108
+ - - ">="
109
+ - !ruby/object:Gem::Version
110
+ version: 5.0.0
111
+ description: Command line tool to help with developing Posthaven themes. Provides
112
+ simple commands to download, upload and delete files from a theme. Also includes
113
+ the watch command to watch a directory and upload files as they change.
114
+ email:
115
+ - help@posthaven.com
116
+ executables:
117
+ - phtheme
118
+ extensions: []
119
+ extra_rdoc_files: []
120
+ files:
121
+ - ".gitignore"
122
+ - Gemfile
123
+ - MIT-LICENSE
124
+ - README.md
125
+ - Rakefile
126
+ - bin/phtheme
127
+ - lib/posthaven_theme.rb
128
+ - lib/posthaven_theme/cli.rb
129
+ - lib/posthaven_theme/version.rb
130
+ - posthaven_theme.gemspec
131
+ - spec/spec_helper.rb
132
+ - spec/unit/cli_spec.rb
133
+ homepage: https://github.com/posthaven/posthaven_theme
134
+ licenses:
135
+ - MIT
136
+ metadata: {}
137
+ post_install_message:
138
+ rdoc_options: []
139
+ require_paths:
140
+ - lib
141
+ required_ruby_version: !ruby/object:Gem::Requirement
142
+ requirements:
143
+ - - ">="
144
+ - !ruby/object:Gem::Version
145
+ version: '2.0'
146
+ required_rubygems_version: !ruby/object:Gem::Requirement
147
+ requirements:
148
+ - - ">="
149
+ - !ruby/object:Gem::Version
150
+ version: '0'
151
+ requirements: []
152
+ rubyforge_project: posthaven_theme
153
+ rubygems_version: 2.6.4
154
+ signing_key:
155
+ specification_version: 4
156
+ summary: Command line tool for developing themes
157
+ test_files:
158
+ - spec/spec_helper.rb
159
+ - spec/unit/cli_spec.rb
160
+ has_rdoc: