spar 0.2.7 → 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.
Files changed (42) hide show
  1. data/README.md +272 -1
  2. data/bin/spar +30 -1
  3. data/lib/spar.rb +128 -7
  4. data/lib/spar/assets/404.jpg +0 -0
  5. data/lib/spar/assets/500.jpg +0 -0
  6. data/lib/spar/cli.rb +75 -22
  7. data/lib/spar/compiled_asset.rb +90 -0
  8. data/lib/spar/compiler.rb +74 -0
  9. data/lib/spar/compressor.rb +18 -0
  10. data/lib/spar/deployers/cloudfront_deployer.rb +33 -0
  11. data/lib/spar/deployers/deployer.rb +26 -0
  12. data/lib/spar/deployers/local_deployer.rb +15 -0
  13. data/lib/spar/deployers/s3_deployer.rb +75 -0
  14. data/lib/spar/directive_processor.rb +36 -0
  15. data/lib/spar/exceptions.rb +330 -0
  16. data/lib/spar/generators/templates/{README → base/README} +0 -0
  17. data/lib/spar/generators/templates/base/config.yml +12 -0
  18. data/lib/spar/generators/templates/pages/haml/app/pages/index.html.haml +11 -0
  19. data/lib/spar/generators/templates/pages/html/app/pages/index.html +16 -0
  20. data/lib/spar/generators/templates/{app/assets/javascripts/javascript.js.coffee → scripts/coffee/app/javascripts/application.js.coffee} +6 -3
  21. data/lib/spar/generators/templates/scripts/js/app/javascripts/application.js +11 -0
  22. data/lib/spar/generators/templates/styles/css/app/stylesheets/application.css +18 -0
  23. data/lib/spar/generators/templates/{app/assets → styles/sass/app}/stylesheets/application.css.sass +8 -4
  24. data/lib/spar/helpers.rb +31 -102
  25. data/lib/spar/not_found.rb +64 -0
  26. data/lib/spar/rewrite.rb +29 -0
  27. data/lib/spar/static.rb +46 -0
  28. data/lib/spar/tasks.rb +8 -13
  29. data/lib/spar/version.rb +1 -1
  30. metadata +261 -66
  31. data/lib/spar/assets.rb +0 -78
  32. data/lib/spar/base.rb +0 -54
  33. data/lib/spar/css_compressor.rb +0 -17
  34. data/lib/spar/deployer.rb +0 -198
  35. data/lib/spar/generators/application.rb +0 -42
  36. data/lib/spar/generators/templates/Gemfile +0 -3
  37. data/lib/spar/generators/templates/Rakefile +0 -8
  38. data/lib/spar/generators/templates/app/views/index.haml +0 -7
  39. data/lib/spar/generators/templates/config.ru +0 -9
  40. data/lib/spar/generators/templates/config/application.rb +0 -12
  41. data/lib/spar/generators/templates/script/server +0 -1
  42. data/lib/spar/static_compiler.rb +0 -91
data/lib/spar/assets.rb DELETED
@@ -1,78 +0,0 @@
1
- require 'sprockets'
2
- require 'sprockets-sass'
3
- require 'coffee_script'
4
- require 'uglifier'
5
-
6
- module Spar
7
- module Assets
8
-
9
- DEFAULT_DIRS = ['stylesheets', 'javascripts', 'images', 'fonts']
10
- DEFAULT_PRECOMPILE = [ /\w+\.(?!js|css).+/, /application.(css|js)$/ ]
11
- DEFAULT_PREFIX = 'assets'
12
- DEFAULT_DIGEST_FREE_ASSETS = []
13
-
14
- def self.registered(app)
15
- app.set :asset_env, Sprockets::Environment.new(app.root)
16
- app.set :asset_precompile, app.respond_to?(:asset_precompile) ? app.asset_precompile : DEFAULT_PRECOMPILE
17
- app.set :asset_dirs, app.respond_to?(:asset_dirs) ? app.asset_dirs : DEFAULT_DIRS
18
- app.set :asset_prefix, app.respond_to?(:asset_prefix) ? app.asset_prefix : DEFAULT_PREFIX
19
- app.set :digest_free_assets, app.respond_to?(:digest_free_assets) ? app.digest_free_assets : DEFAULT_DIGEST_FREE_ASSETS
20
- app.set :asset_path, File.join(app.root, app.asset_prefix)
21
- app.set :manifest_path, File.join(app.public_path, app.asset_prefix, "manifest.yml")
22
-
23
- app.configure :production, :staging do
24
- app.set :request_gzip, true unless app.respond_to?(:request_gzip)
25
- app.set :assets_debug, false unless app.respond_to?(:assets_debug)
26
- app.set :asset_digest, true unless app.respond_to?(:asset_digest)
27
- app.set :asset_host, nil unless app.respond_to?(:asset_host)
28
- app.set :asset_compile, false unless app.respond_to?(:asset_compile)
29
- app.asset_env.js_compressor = Uglifier.new(:mangle => false)
30
- app.asset_env.css_compressor = CssCompressor.new
31
- end
32
-
33
- app.configure :development, :test do
34
- app.set :request_gzip, false unless app.respond_to?(:request_gzip)
35
- app.set :assets_debug, true unless app.respond_to?(:assets_debug)
36
- app.set :asset_digest, false unless app.respond_to?(:asset_digest)
37
- app.set :asset_host, nil unless app.respond_to?(:asset_host)
38
- app.set :asset_compile, true unless app.respond_to?(:asset_compile)
39
- end
40
-
41
- app.asset_dirs.each do |asset_type|
42
- app.asset_env.append_path(File.join(app.asset_path, asset_type))
43
- app.asset_env.append_path(File.join(Spar.root, 'lib', DEFAULT_PREFIX, asset_type))
44
- app.asset_env.append_path(File.join(Spar.root, 'vendor', DEFAULT_PREFIX, asset_type))
45
- Gem.loaded_specs.each do |name, gem|
46
- app.asset_env.append_path(File.join(gem.full_gem_path, 'vendor', DEFAULT_PREFIX, asset_type))
47
- app.asset_env.append_path(File.join(gem.full_gem_path, 'app', 'assets', DEFAULT_PREFIX, asset_type))
48
- end
49
- end
50
-
51
- if File.exist?(app.manifest_path)
52
- app.set :asset_digests, YAML.load_file(app.manifest_path)
53
- else
54
- app.set :asset_digests, {}
55
- end
56
-
57
- Spar::Helpers.configure do |config|
58
- config.asset_environment = app.asset_env
59
- config.asset_prefix = app.asset_prefix
60
- config.compile_assets = app.asset_compile
61
- config.debug_assets = app.assets_debug
62
- config.digest_assets = app.asset_digest
63
- config.asset_digests = app.asset_digests
64
- config.asset_host = app.asset_host
65
- end
66
-
67
- app.asset_env.context_class.instance_eval do
68
- include ::Spar::Helpers
69
- end
70
-
71
- app.helpers do
72
- include Spar::Helpers
73
- end
74
-
75
- end
76
-
77
- end
78
- end
data/lib/spar/base.rb DELETED
@@ -1,54 +0,0 @@
1
- require 'rbconfig'
2
- require 'sinatra/base'
3
- require 'haml'
4
- require 'sass'
5
-
6
- module Spar
7
- class Base < Sinatra::Base
8
-
9
- class << self
10
-
11
- attr_accessor :setup_block
12
-
13
- def inherited(base)
14
- super
15
-
16
- find_root
17
-
18
- set :environment, (ENV['RACK_ENV'] || :development).to_sym
19
- set :public_path, File.join(Spar.root, 'public')
20
- set :library_path, File.join(Spar.root, 'lib')
21
- set :root, File.join(Spar.root, 'app')
22
-
23
- config_path = "#{Spar.root}/config/environments/#{environment}.rb"
24
- eval File.read(config_path), binding, config_path
25
-
26
- Dir[File.join(library_path, '*.rb')].each {|file| autoload file }
27
-
28
- puts "Started Spar Server [#{environment.to_s}]"
29
- end
30
-
31
- protected
32
-
33
- def find_root
34
- Spar.root = begin
35
- # Remove the line number from backtraces making sure we don't leave anything behind
36
- call_stack = caller.map { |p| p.sub(/:\d+.*/, '') }
37
- root_path = File.dirname(call_stack.detect { |p| p !~ %r[[\w.-]*/lib/spar|rack[\w.-]*/lib/rack] })
38
-
39
- while root_path && File.directory?(root_path) && !File.exist?("#{root_path}/config.ru")
40
- parent = File.dirname(root_path)
41
- root_path = parent != root_path && parent
42
- end
43
-
44
- root = File.exist?("#{root_path}/config.ru") ? root_path : Dir.pwd
45
- raise "Could not find root path for #{self}" unless root
46
-
47
- RbConfig::CONFIG['host_os'] =~ /mswin|mingw/ ? Pathname.new(root).expand_path : Pathname.new(root).realpath
48
- end
49
- end
50
-
51
- end
52
-
53
- end
54
- end
@@ -1,17 +0,0 @@
1
- module Spar
2
- module Assets
3
- class CssCompressor
4
- def compress(css)
5
- if css.count("\n") > 2
6
- Sass::Engine.new(css,
7
- :syntax => :scss,
8
- :cache => false,
9
- :read_cache => false,
10
- :style => :compressed).render
11
- else
12
- css
13
- end
14
- end
15
- end
16
- end
17
- end
data/lib/spar/deployer.rb DELETED
@@ -1,198 +0,0 @@
1
- require 'logger'
2
- require 'find'
3
- require 'mime/types'
4
- require 'aws-sdk'
5
- require 'cloudfront-invalidator'
6
-
7
- module Spar
8
- class Deployer
9
-
10
- def initialize(app, options = {})
11
- @app = app
12
- unless @app.respond_to? :aws_access_key_id and @app.respond_to? :aws_secret_access_key
13
- raise ArgumentError.new(":aws_acces_key_id and :aws_secret_access_key are required")
14
- end
15
- AWS.config(
16
- :access_key_id => @app.aws_access_key_id,
17
- :secret_access_key => @app.aws_secret_access_key,
18
- :logger => Logger.new($stderr),
19
- :log_formatter => AWS::Core::LogFormatter.colored
20
- )
21
- AWS.config.logger.level = Logger::WARN
22
- @s3 = AWS::S3.new
23
- @bucket = @s3.buckets[App.s3_bucket]
24
- @age_out = options.delete(:age_out) || 60 * 60 * 24 * 3 # 3 days
25
- @cache_control = options.delete(:cache_control) || "public, max-age=#{60 * 60 * 24 * 7}"
26
- @zip_files = options.delete(:zip_files) || /\.(?:css|html|js|svg|txt|xml)$/
27
- @view_paths = app.precompile_view_paths || []
28
- @to_invalidate = []
29
- end
30
-
31
- def upload_assets
32
- Dir.chdir(@app.public_path) do
33
- local = Find.find( 'assets' ).reject{|f| %w[assets/index.html assets/manifest.yml].index f}.reject! { |f| File.directory? f }
34
- remote = @bucket.objects.with_prefix( 'assets/' ).map{|o|o.key}.reject{|o| o =~ /\/$/ }
35
- to_delete = remote - local
36
- to_upload = local - remote
37
- to_check = remote & local
38
-
39
- to_check.each do |file|
40
- if @bucket.objects[file].etag.gsub(/\"/,'') != Digest::MD5.hexdigest(File.read(file))
41
- logger "Etag mismatch for: #{file}"
42
- headers = {
43
- :content_type => MIME::Types.of(file.gsub(/\.?gz$/, '')).first,
44
- :cache_control => 'public, max-age=0',
45
- :acl => :public_read
46
- }
47
- headers[:content_encoding] = :gzip if %w[svgz gz].index file.split('.').last
48
-
49
- logger "Uploading #{file}", headers
50
- @bucket.objects[file].write(headers.merge :data => File.read(file) )
51
- @to_invalidate << file
52
- end
53
- end
54
-
55
- to_upload.each do |file|
56
-
57
- headers = {
58
- :content_type => MIME::Types.of(file.gsub(/\.?gz$/, '')).first,
59
- :cache_control => @cache_control,
60
- :acl => :public_read,
61
- :expires => (Time.now+60*60*24*365).httpdate
62
- }
63
- headers[:content_encoding] = :gzip if %w[svgz gz].index file.split('.').last
64
-
65
- logger "Uploading #{file}", headers
66
- @bucket.objects[file].write(headers.merge :data => File.read(file) )
67
- end
68
-
69
- age_out to_delete
70
- end
71
- end
72
-
73
- # TODO I really want this to call StaticCompiler#write_manifest directly. Another day.
74
- def upload_views
75
- Dir.chdir(@app.public_path) do
76
- views = Find.find( '.' ).to_a
77
- .select { |f| File.basename(f) == 'index.html' }
78
- .sort_by { |f| f.length }
79
- .map { |f| f.gsub('./','') }
80
- .flatten
81
- views.each do |file|
82
- headers = {
83
- :content_type => 'text/html; charset=utf-8',
84
- :cache_control => 'public, max-age=0',
85
- :acl => :public_read
86
- }
87
- logger "Uploading #{file}", headers
88
- @bucket.objects[file].write(headers.merge :data => File.read(file) )
89
- end
90
- @to_invalidate << views.map { |f| [ f, f.gsub('/index.html', ''), f.gsub('index.html', '') ] }.flatten
91
- end
92
- end
93
-
94
- def upload_downloads
95
- # TODO This should be doing checksum comparisons so that downloads can be replaced.
96
- Dir.chdir(File.join(@app.root,'assets')) do
97
- return unless Dir.exists? 'downloads'
98
- local = Find.find( 'downloads' ).to_a.reject! { |f| File.directory? f }
99
- remote = @bucket.objects.with_prefix( 'downloads/' ).map{|o|o.key}.reject{|o| o =~ /\/$/ }
100
- to_delete = remote - local
101
- to_upload = local - remote
102
- @to_invalidate << to_upload
103
-
104
- to_upload.each do |file|
105
-
106
- headers = {
107
- :content_type => MIME::Types.of(file).first,
108
- :content_disposition => "attachment; filename=#{File.basename(file)}",
109
- :cache_control => 'public, max-age=86400',
110
- :acl => :public_read,
111
- :expires => (Time.now+60*60*24*365).httpdate
112
- }
113
-
114
- logger "Uploading #{file}", headers
115
- @bucket.objects[file].write(headers.merge :data => File.read(file) )
116
- end
117
- age_out to_delete
118
- end
119
- end
120
-
121
- # Copy `assets/favicon-hash.ico` (if changed in this deployment) to /favicon.ico.
122
- def upload_favicon
123
- Dir.chdir(@app.public_path) do
124
- to_upload.select{ |path| path =~ /favicon/}.each do |hashed| # Should only be one.
125
- logger "Copying favicon.ico into place from #{hashed}"
126
- @bucket.objects[hashed].copy_to('favicon.ico', { :acl => :public_read })
127
- @to_invalidate << 'favicon.ico'
128
- end
129
- end
130
- end
131
-
132
- # Remove obsolete objects once they are sufficiently old
133
- def age_out(list)
134
- list.flatten.each do |file|
135
- if Time.now - @bucket.objects[file].last_modified > @age_out
136
- logger "Deleting #{file}"
137
- @bucket.objects[file].delete
138
- end
139
- end
140
- end
141
-
142
- # Add a file indicating time of most recent deploy.
143
- def timestamp!
144
- @bucket.objects['TIMESTAMP.txt'].write(
145
- :data => Time.now.to_s+"\n",
146
- :content_type => "text/plain",
147
- :acl => :public_read
148
- )
149
- @to_invalidate << 'TIMESTAMP.txt'
150
- end
151
-
152
- def deploy
153
- upload_assets
154
- upload_views
155
- upload_downloads
156
- timestamp!
157
- invalidate_cloudfront
158
- end
159
-
160
- def invalidate_cloudfront
161
- return unless @app.respond_to? :cloudfront_distribution
162
- @to_invalidate.flatten!
163
- @to_invalidate.uniq!
164
- logger "Issuing CloudFront invalidation request for #{@to_invalidate.count} objects."
165
- CloudfrontInvalidator.new(
166
- @app.aws_access_key_id,
167
- @app.aws_secret_access_key,
168
- @app.cloudfront_distribution,
169
- ).invalidate(@to_invalidate) do |status,time|
170
- puts "Invalidation #{status} after %.2f seconds" % time.to_f
171
- end
172
- end
173
-
174
- def logger(*args)
175
- STDERR.puts args.map{|x|x.to_s}.join(' ')
176
- end
177
-
178
- end
179
- end
180
-
181
- # MIME::Types has incomplete knowledge of how web fonts get served, and
182
- # complains when we try to fix it.
183
- module Kernel
184
- def suppress_warnings
185
- original_verbosity = $VERBOSE
186
- $VERBOSE = nil
187
- result = yield
188
- $VERBOSE = original_verbosity
189
- return result
190
- end
191
- end
192
-
193
- Kernel.suppress_warnings do
194
- MIME::Types.add(
195
- MIME::Type.new('image/svg+xml'){|t| t.extensions = %w[svgz]},
196
- MIME::Type.new('application/vnd.ms-fontobject'){|t| t.extensions = %w[eot]}
197
- )
198
- end
@@ -1,42 +0,0 @@
1
- require 'thor'
2
-
3
- module Spar
4
- module Generators
5
- class Error < Thor::Error
6
- end
7
-
8
- class Application < Thor
9
- include Thor::Actions
10
-
11
- source_root File.expand_path("../", __FILE__)
12
-
13
- desc "new APP_NAME", "create a new Spar app"
14
- def generate(name)
15
- directory 'templates', name
16
- inside name do
17
- run('chmod +x script/*')
18
- empty_directory_with_gitkeep "public"
19
- empty_directory "lib"
20
- empty_directory_with_gitkeep "lib/tasks"
21
- empty_directory_with_gitkeep "lib/assets"
22
- empty_directory_with_gitkeep "app/assets/images"
23
- empty_directory_with_gitkeep "vendor/assets/javascripts"
24
- empty_directory_with_gitkeep "vendor/assets/stylesheets"
25
- end
26
- puts "A new Spar app has been created in #{name} - Have Fun!"
27
- end
28
-
29
- protected
30
-
31
- def empty_directory_with_gitkeep(destination)
32
- empty_directory(destination)
33
- git_keep(destination)
34
- end
35
-
36
- def git_keep(destination)
37
- create_file("#{destination}/.gitkeep")
38
- end
39
-
40
- end
41
- end
42
- end
@@ -1,3 +0,0 @@
1
- source "http://rubygems.org"
2
-
3
- gem "spar"
@@ -1,8 +0,0 @@
1
- require 'bundler'
2
- Bundler.require
3
-
4
- task :environment do
5
- require './config/application'
6
- end
7
-
8
- require 'spar/tasks'
@@ -1,7 +0,0 @@
1
- !!! Strict
2
- %html
3
- %head
4
- %title Spar App
5
- %link{:rel => "stylesheet", :href => stylesheet_path('application')}
6
- %script{:src => javascript_path('application')}
7
- %body
@@ -1,9 +0,0 @@
1
- require './config/application'
2
-
3
- map "/assets" do
4
- run App.asset_env
5
- end
6
-
7
- map "/" do
8
- run App
9
- end
@@ -1,12 +0,0 @@
1
- require 'bundler'
2
- Bundler.require
3
-
4
- class App < Spar::Base
5
-
6
- register Spar::Assets
7
-
8
- get "/" do
9
- haml :index
10
- end
11
-
12
- end
@@ -1 +0,0 @@
1
- rackup