assette 0.0.0 → 0.0.1

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.
@@ -1,24 +1,158 @@
1
- require 'yaml'
2
-
3
1
  module Assette
4
- attr_accessor :config
2
+ def config(optional_path=nil)
3
+ return @config if @config && !optional_path
4
+
5
+ f = %w{assets.rb config/assets.rb}
6
+ f.unshift(optional_path.to_s) if optional_path
7
+
8
+ p = f.find { |path| File.exist?(path) }
9
+
10
+ @config = Assette::Config.load(p)
11
+ end
5
12
 
6
13
  class Config
7
- OPTIONS = [
8
- :file_paths, :asset_path, :templates_path, :template_format
9
- ]
14
+ MULTIPLES = %w{file_path asset_host}.freeze
15
+ SINGLES = %w{asset_dir templates_path template_format cache_method
16
+ template_preloader template_partial}.freeze
17
+ BLOCKS = %w{after_compile}.freeze
18
+
19
+ OPTIONS = begin
20
+ arr = MULTIPLES.collect do |m|
21
+ "#{m}s"
22
+ end
23
+ arr += SINGLES
24
+ arr += BLOCKS
25
+ end.freeze
26
+
10
27
  attr_reader *OPTIONS
28
+ attr_accessor :compiling, :sha
29
+
30
+ DEFAULTS = {
31
+ :asset_dir => 'assets',
32
+ :asset_hosts => [],
33
+ :file_paths => %w{public},
34
+ :templates_path => 'app/templates',
35
+ :template_format => 'AT.t[{*path*}] = {*template*};'
36
+ }.freeze
11
37
 
12
38
  def initialize args = {}
39
+ args = DEFAULTS.merge(args||{})
40
+
13
41
  args.each do |k,v|
14
42
  instance_variable_set "@#{k}", v.dup.freeze
15
43
  end
16
44
  end
17
45
 
46
+ def find_file_from_relative_path path
47
+ a = Assette.config.file_paths.find do |pp|
48
+ File.exist?(File.join(pp,path))
49
+ end
50
+ File.join(a,path)
51
+ end
52
+
53
+ def find_target_from_relative_path path
54
+ Assette.config.file_paths.find do |p|
55
+ Assette::Reader.possible_targets(File.join(p,path)).find do |pp|
56
+ File.exist?(pp)
57
+ end
58
+ end
59
+ end
60
+
61
+ def build_target
62
+ @asset_dir
63
+ end
64
+
65
+ def asset_hosts?
66
+ !@asset_hosts.empty?
67
+ end
68
+
69
+ def compiling?
70
+ !!compiling
71
+ end
72
+
73
+ def asset_host i
74
+ return '' unless asset_hosts?
75
+ @asset_hosts[i % @asset_hosts.size]
76
+ end
77
+
78
+ def compiled_path str
79
+ str = str.dup
80
+ if sha
81
+ case cache_method.to_s
82
+ when 'path'
83
+ str = File.join(sha,str)
84
+ when 'param'
85
+ str << (str.include?('?') ? '&' : '?')
86
+
87
+ str << 'v=' << sha
88
+ else
89
+ warn('No cache compile method set (param or path)')
90
+ end
91
+ end
92
+
93
+ str
94
+ end
95
+
96
+ class Builder
97
+
98
+ attr_reader :__hsh__
99
+ def initialize(file)
100
+ @__str = File.open(file).read
101
+
102
+ @__hsh__ = {}
103
+ end
104
+
105
+ def __run__
106
+ instance_eval @__str
107
+ end
108
+
109
+ MULTIPLES.each do |m|
110
+ eval <<-RB
111
+ def #{m} *args
112
+ @__hsh__[:#{m}s] ||= []
113
+ @__hsh__[:#{m}s] |= args.flatten
114
+ end
115
+
116
+ def #{m}s *args
117
+ @__hsh__[:#{m}s] = args.flatten
118
+ end
119
+ RB
120
+ end
121
+
122
+ SINGLES.each do |s|
123
+ eval <<-RB
124
+ def #{s} v
125
+ @__hsh__[:#{s}] = v
126
+ end
127
+ RB
128
+ end
129
+
130
+ BLOCKS.each do |b|
131
+ eval <<-RB
132
+ def #{b} val=nil, &blk
133
+ if val
134
+ @__hsh__[:#{b}] ||= {}
135
+ @__hsh__[:#{b}][val] = blk
136
+ else
137
+ @__hsh__[:#{b}] = blk
138
+ end
139
+ end
140
+ RB
141
+ end
142
+
143
+ def to_hash
144
+ @__hsh__
145
+ end
146
+
147
+ end
148
+
18
149
  class << self
19
150
 
20
151
  def load p
21
- new YAML.load_file(p)
152
+ b = Builder.new(p)
153
+ b.__run__
154
+
155
+ new b.to_hash
22
156
  end
23
157
 
24
158
  end
@@ -26,10 +160,3 @@ module Assette
26
160
  end
27
161
 
28
162
  end
29
-
30
- f = %w{assets.yml config/assets.yml}
31
- f << File.join( File.dirname(__FILE__), '..', '..', 'examples', 'defaults.yml' )
32
-
33
- p = f.find { |path| File.exist?(path) }
34
-
35
- Assette.config = Assette::Config.load(p)
data/lib/assette/file.rb CHANGED
@@ -6,7 +6,7 @@ class Assette::File < ::File
6
6
  end
7
7
 
8
8
  def extension
9
- m = path.match(/\.(\w+)$/)
9
+ m = filename.match(/\.(\w+)$/)
10
10
  m[1] if m
11
11
  end
12
12
 
@@ -67,8 +67,12 @@ class Assette::File < ::File
67
67
  File.dirname(path)
68
68
  end
69
69
 
70
+ def target_path
71
+ File.join(dirname,filename.gsub(reader_class.extension,target_class.extension))
72
+ end
73
+
70
74
  def filename
71
- path.gsub(dirname,'').gsub(/^\//,'')
75
+ File.basename(path)
72
76
  end
73
77
 
74
78
  def puts *args
@@ -0,0 +1,65 @@
1
+ module Assette
2
+ module PostProcessor
3
+ extend self
4
+ POST_PROCESSORS = Hash.new {|h,k| h[k] = []}
5
+
6
+ def s
7
+ POST_PROCESSORS
8
+ end
9
+
10
+ class Base
11
+
12
+ def initialize(str, args={})
13
+ @str = str; @args = args
14
+ end
15
+
16
+ def should_process?
17
+ true
18
+ end
19
+
20
+ def processor
21
+ raise Exception, "You must implement the processor method for #{self.class.inspect} (you can use the @str)"
22
+ end
23
+
24
+ def process
25
+ return @str unless should_process?
26
+
27
+ processor
28
+ end
29
+
30
+ class << self
31
+ def inherited subclass
32
+ return if subclass == Assette::PostProcessor::Base || subclass.inspect =~ /#<Class/
33
+
34
+ if outputs
35
+ Assette::PostProcessor::POST_PROCESSORS[outputs] |= [subclass]
36
+ end
37
+ end
38
+
39
+ def outputs
40
+
41
+ end
42
+
43
+ def set_outputs val
44
+ raise ArgumentError, 'must set outputs to a symbol' unless val.is_a?(Symbol)
45
+
46
+ instance_eval <<-RUBY
47
+ def outputs
48
+ #{val.inspect}
49
+ end
50
+ RUBY
51
+ val
52
+ end
53
+ end
54
+ end
55
+
56
+ end
57
+
58
+
59
+
60
+ def self.PostProcessor(type)
61
+ c = Class.new(PostProcessor::Base)
62
+ c.set_outputs(type)
63
+ c
64
+ end
65
+ end
@@ -0,0 +1,18 @@
1
+ class Assette::PostProcessor::CacheBuster < Assette::PostProcessor(:css)
2
+ URL_MATCHER = /url\((?:["'])?(?!http)(?!\/\/)([\w\/\.\-\s\?=]+)(?:["'])?\)/i
3
+
4
+ def should_process?
5
+ Assette.config.compiling?
6
+ end
7
+
8
+ def processor
9
+ @@i ||= -1
10
+
11
+ @str.gsub(URL_MATCHER) do |s|
12
+ url = File.join(Assette.config.asset_host(@@i+=1),Assette.config.compiled_path($1))
13
+
14
+ %Q{url("#{url}")}
15
+ end
16
+ end
17
+
18
+ end
@@ -0,0 +1,4 @@
1
+ fname = File.join(File.dirname(__FILE__),'post_processors','*.rb')
2
+ Dir[fname].each do |f|
3
+ require f
4
+ end
@@ -1,4 +1,4 @@
1
- require 'open3'
1
+ require 'coffee-script'
2
2
 
3
3
  class Assette::Reader::Coffee < Assette::Reader(:js)
4
4
 
@@ -6,26 +6,26 @@ class Assette::Reader::Coffee < Assette::Reader(:js)
6
6
  self.class.compile_str text
7
7
  end
8
8
 
9
- class << self
10
-
11
- def compile_str text
12
- out = nil
13
-
14
- Open3.popen3('coffee -sc') do |stdin, stdout, stderr|
15
- stdin.puts(text)
16
- stdin.close_write
17
- out = stdout.read
18
- end
19
-
20
- out
21
- end
22
-
23
- def comment_str
24
- '#'
25
- end
26
-
27
- def comment_str_end
28
- ''
29
- end
9
+ def self.comment_str
10
+ '# %s'
30
11
  end
31
- end
12
+
13
+ def self.compile_str text
14
+ CoffeeScript.compile text
15
+ end
16
+
17
+ end
18
+
19
+ # require 'open3'
20
+ # Compile coffeescript using coffee binary.
21
+ # def compile_str_external text
22
+ # out = nil
23
+ #
24
+ # Open3.popen3('coffee -sc') do |stdin, stdout, stderr|
25
+ # stdin.puts(text)
26
+ # stdin.close_write
27
+ # out = stdout.read
28
+ # end
29
+ #
30
+ # out
31
+ # end
@@ -7,7 +7,7 @@ class Assette::Reader::Css < Assette::Reader(:css)
7
7
  class << self
8
8
 
9
9
  def comment_str
10
- @comment_str ||= '/* %s */'
10
+ '/* %s */'
11
11
  end
12
12
 
13
13
  end
@@ -7,7 +7,7 @@ class Assette::Reader::Js < Assette::Reader(:js)
7
7
  class << self
8
8
 
9
9
  def comment_str
10
- @comment_str ||= "// %s"
10
+ '// %s'
11
11
  end
12
12
  end
13
13
  end
@@ -3,8 +3,7 @@ require 'sass'
3
3
  class Assette::Reader::Sass < Assette::Reader(:css)
4
4
 
5
5
  def compile
6
- engine = ::Sass::Engine.new(text,options)
7
- engine.to_css
6
+ ::Sass::Engine.new(text,options).to_css
8
7
  end
9
8
 
10
9
  private
@@ -0,0 +1,2 @@
1
+
2
+ run Assette::Server
@@ -2,9 +2,11 @@ module Assette
2
2
  # Methods for finding files are ugly, should fix someday
3
3
  class Server
4
4
  attr_reader :path
5
- def initialize(path)
6
- @path = path
7
- Assette.config.asset_path
5
+ def initialize(env)
6
+ @env = env
7
+ @path = env["PATH_INFO"]
8
+
9
+ # Assette.config.asset_path
8
10
  end
9
11
 
10
12
  def find_compiled_file
@@ -44,8 +46,8 @@ module Assette
44
46
  Assette.config.file_paths.each do |p|
45
47
  new_path = File.join(p,path)
46
48
  if File.exist?(new_path)
47
- f = File.open(new_path)
48
- # add in grabbing index.html files if no extension provided
49
+ new_path = File.join(Dir.pwd,new_path)
50
+ f = Rack::File.new(p).call(@env)
49
51
  end
50
52
 
51
53
  break if f
@@ -55,25 +57,25 @@ module Assette
55
57
  end
56
58
 
57
59
  def rack_resp
58
-
59
60
  if has_registered_reader? && (f = find_compiled_file)
60
61
  [200,{"Content-Type" => f.content_type},f]
61
- elsif (f = find_file) && path_mime_type
62
- puts content_type
63
- [200,{"Content-Type" => content_type},f]
62
+ elsif f = find_file
63
+ f
64
64
  else
65
- [404,{"Content-Type" => "text/plain"},["File Not Found"]]
65
+ possible_paths = Assette.config.file_paths.collect do |p|
66
+ File.join(Dir.pwd,p,path)
67
+ end
68
+
69
+ [404,{"Content-Type" => "text/plain"},["File Not Found\n#{possible_paths.join("\n")}"]]
66
70
  end
67
71
  end
68
72
 
69
73
  class << self
70
74
 
71
75
  def call(env)
72
- s = new(env["PATH_INFO"])
76
+ s = new(env)
73
77
 
74
78
  s.rack_resp
75
- # rescue => e
76
- # [500,{"Content-Type" => "text/plain"},[e.inspect]]
77
79
  end
78
80
 
79
81
  end
@@ -2,7 +2,11 @@ module Assette
2
2
 
3
3
  class Template < Assette::File
4
4
  def compile
5
- format = Assette.config.template_format.dup
5
+ if Assette.config.template_partial && filename =~ /^_/
6
+ format = Assette.config.template_partial.dup
7
+ else
8
+ format = Assette.config.template_format.dup
9
+ end
6
10
 
7
11
  format.gsub!('{*path*}',local_path.to_json)
8
12
  format.gsub!('{*template*}',stringify_body)
@@ -14,7 +18,9 @@ module Assette
14
18
 
15
19
  def local_path
16
20
  lp = path.gsub(Assette.config.templates_path,'')
17
- lp.gsub!(/((\.html)?\.\w+)$/,'')
21
+ lp.gsub!(/((\.html)?\.\w+)$/,'').gsub!(/^\//,'')
22
+ lp.gsub!(/\/_/,'/')
23
+ lp
18
24
  end
19
25
  end
20
26
 
@@ -36,6 +36,13 @@ module Assette
36
36
  end
37
37
  end
38
38
 
39
+ def insert_preloader
40
+ p = Assette.config.template_preloader
41
+ if p && pp = Assette.config.find_file_from_relative_path(p)
42
+ Assette::File.open(pp).all_code
43
+ end
44
+ end
45
+
39
46
  def compile
40
47
  coffee = Array.new
41
48
 
@@ -49,7 +56,7 @@ module Assette
49
56
  str = "window[#{var.to_json}] ||= {}"
50
57
  else
51
58
  str = used.join('.')
52
- str << " = (#{str} || {})"
59
+ str << " ||= {}"
53
60
  end
54
61
 
55
62
  coffee << str
@@ -59,7 +66,9 @@ module Assette
59
66
  coffee << template.compile
60
67
  end
61
68
 
62
- Assette::Reader::Coffee.compile_str(coffee.join("\n"))
69
+ t = Assette::Reader::Coffee.compile_str(coffee.join("\n"))
70
+ pre = insert_preloader
71
+ pre ? [pre,t].join("\n") : t
63
72
  end
64
73
 
65
74
  def storage_variable
data/lib/assette.rb CHANGED
@@ -17,8 +17,10 @@ MIME::Types.add coffee_type, sass_type
17
17
  module Assette
18
18
  extend self
19
19
  CONFIG_WRAPPER = 'ASSETTE CONFIG'
20
+ VERSION = File.open(File.expand_path(File.dirname(__FILE__)+'/../VERSION')).read
20
21
  end
21
22
 
22
- %w{config reader readers compiled_file file template_set template}.each do |f|
23
+ %w{config reader readers post_processor post_processors
24
+ compiled_file file template_set template}.each do |f|
23
25
  require File.dirname(__FILE__)+'/assette/'+f
24
26
  end
@@ -0,0 +1,52 @@
1
+ require File.expand_path(File.dirname(__FILE__) + '/spec_helper')
2
+
3
+ describe Assette::Config do
4
+
5
+ before do
6
+ Assette.instance_variable_set :@config, nil
7
+ end
8
+
9
+ it "should recieve correct file" do
10
+ Assette::Config.should_receive(:load).once.with('config/assets.rb')
11
+
12
+ Dir.chdir(File.dirname(__FILE__) + '/../examples') { Assette.config }
13
+ end
14
+
15
+ describe "default file" do
16
+ before(:all) do
17
+ @dir_before = Dir.pwd
18
+ Dir.chdir(File.dirname(__FILE__) + '/../examples')
19
+ end
20
+
21
+ after(:all) do
22
+ Dir.chdir(@dir_before)
23
+ end
24
+
25
+ subject {Assette.config}
26
+
27
+ it "should set file paths" do
28
+ subject.file_paths.should == %w{public foo}
29
+ end
30
+
31
+ it "should set asset hosts" do
32
+ subject.asset_hosts.should == %w{http://cdn1.gilt.com http://cdn2.gilt.com}
33
+ end
34
+
35
+ it "should set assets dir" do
36
+ subject.asset_dir.should == 'myassets'
37
+ end
38
+
39
+ it "should set templates path" do
40
+ subject.templates_path.should == 'myapp/templates'
41
+ end
42
+
43
+ it "should set templates format" do
44
+ subject.template_format.should == 'GC.foo.t[{*path*}] = Handlebars.compile({*template*});'
45
+ end
46
+
47
+ it "should set after compile" do
48
+ subject.after_compile.call.should == 3
49
+ end
50
+ end
51
+
52
+ end
@@ -0,0 +1,63 @@
1
+ require File.expand_path(File.dirname(__FILE__) + '/spec_helper')
2
+
3
+ describe Assette::PostProcessor do
4
+
5
+ before(:all) do
6
+ @dir_before = Dir.pwd
7
+ Dir.chdir(File.dirname(__FILE__) + '/../examples')
8
+ end
9
+
10
+ after(:all) do
11
+ Dir.chdir(@dir_before)
12
+ end
13
+
14
+ describe Assette::PostProcessor::CacheBuster do
15
+
16
+ before(:all) do
17
+ Assette.config.compiling = true
18
+ Assette.config.sha = "1234567"
19
+ end
20
+
21
+ after(:all) do
22
+ Assette.config.compiling = nil
23
+ end
24
+
25
+ subject do
26
+ Assette::File.open('public/stylesheets/two2.scss')
27
+ end
28
+
29
+ describe "with asset cdn paths" do
30
+ before(:all) do
31
+ Assette.config.instance_variable_set :@asset_hosts, %w{http://cdn1.gilt.com http://cdn2.gilt.com}
32
+ end
33
+
34
+ describe "path changing" do
35
+ before(:all) do
36
+ Assette.config.instance_variable_set :@cache_method, 'path'
37
+ end
38
+
39
+ it "does something" do
40
+ subject.all_code.should include('gilt.com/1234567/images/mystuff/goat.se?test=1"')
41
+ subject.all_code.should include('gilt.com/1234567/images/mystuff/goa-t.se"')
42
+ subject.all_code.should include('gilt.com/1234567/images/mystuff/go_at.se"')
43
+ end
44
+ end
45
+
46
+
47
+ describe "param changing" do
48
+ before(:all) do
49
+ Assette.config.instance_variable_set :@cache_method, 'param'
50
+ end
51
+
52
+ it "does something" do
53
+ subject.all_code.should include('gilt.com/images/mystuff/goat.se?test=1&v=1234567"')
54
+ subject.all_code.should include('gilt.com/images/mystuff/goa-t.se?v=1234567"')
55
+ subject.all_code.should include('gilt.com/images/mystuff/go_at.se?v=1234567"')
56
+ end
57
+ end
58
+
59
+ end
60
+
61
+ end
62
+
63
+ end
@@ -0,0 +1,15 @@
1
+ require File.expand_path(File.dirname(__FILE__) + '/spec_helper')
2
+ require 'assette/server'
3
+
4
+ describe Assette::Server do
5
+ before(:all) do
6
+ @dir_before = Dir.pwd
7
+ Dir.chdir(File.dirname(__FILE__) + '/../examples')
8
+ end
9
+
10
+ after(:all) do
11
+ Dir.chdir(@dir_before)
12
+ end
13
+
14
+ it "should be implemented"
15
+ end
@@ -14,18 +14,22 @@ describe "Templates" do
14
14
  describe Assette::TemplateSet do
15
15
 
16
16
  it "should get correct list of files" do
17
- f = Assette::Template.open("app/templates/foo/index.html.mustache")
17
+ f = Assette::Template.open("myapp/templates/foo/index.html.mustache")
18
18
 
19
19
  a = Assette::TemplateSet.new(:all)
20
20
  a.templates.should include f
21
-
21
+
22
+ p = Assette::Template.open("myapp/templates/foo/_partial.html.mustache")
22
23
  a = Assette::TemplateSet.new(:foo)
23
- a.templates.should == [f]
24
+ a.templates.should == [p,f]
24
25
  end
25
26
 
26
27
  it "should compile" do
27
28
  a = Assette::TemplateSet.new(:all)
28
- a.compile.should include('{{foo}}')
29
+ c = a.compile
30
+ c.should include('{{foo}}')
31
+ c.should include('Handlebars.registerPartial("foo/partial",')
32
+ c.should include('Handlebars.compile("<div>')
29
33
 
30
34
  a = Assette::TemplateSet.new(:foo)
31
35
  a.compile.should include('{{foo}}')
@@ -36,7 +40,7 @@ describe "Templates" do
36
40
  describe Assette::Template do
37
41
 
38
42
  subject do
39
- Assette::Template.open("app/templates/foo/index.html.mustache")
43
+ Assette::Template.open("myapp/templates/foo/index.html.mustache")
40
44
  end
41
45
 
42
46
  end