sprockets-less 0.5.2

Sign up to get free protection for your applications and to get access to all the features.
data/.gitignore ADDED
@@ -0,0 +1,3 @@
1
+ *.gem
2
+ Gemfile.lock
3
+ .rbenv-version
data/Gemfile ADDED
@@ -0,0 +1,5 @@
1
+ source :rubygems
2
+ gemspec
3
+
4
+ gem "therubyracer", ">= 0.10.1", :require => nil, :platforms => :ruby
5
+ gem "therubyrhino", ">= 1.73.2", :require => nil, :platforms => :jruby
data/LICENSE ADDED
@@ -0,0 +1,20 @@
1
+ Copyright (c) 2012 Loic Nageleisen
2
+
3
+ Permission is hereby granted, free of charge, to any person obtaining
4
+ a copy of this software and associated documentation files (the
5
+ "Software"), to deal in the Software without restriction, including
6
+ without limitation the rights to use, copy, modify, merge, publish,
7
+ distribute, sublicense, and/or sell copies of the Software, and to
8
+ permit persons to whom the Software is furnished to do so, subject to
9
+ the following conditions:
10
+
11
+ The above copyright notice and this permission notice shall be
12
+ included in all copies or substantial portions of the Software.
13
+
14
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
15
+ EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
16
+ MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
17
+ NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
18
+ LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
19
+ OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
20
+ WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
data/README.md ADDED
@@ -0,0 +1,102 @@
1
+ # sprockets-less
2
+
3
+ **Better Less integration with [Sprockets 2.x](http://github.com/sstephenson/sprockets)**
4
+
5
+ When using Sprockets 2.x with Less you will eventually run into a pretty big issue. `//= require` directives will not allow Less mixins, variables, etc. to be shared between files. So you'll try to use `@import`, and that'll also blow up in your face. `sprockets-less` aims to fix this.
6
+
7
+ _Note: If you use Rails 3.1, you may want to use the [less-rails gem](http://github.com/rails/less-rails). But if you want to use Sprockets and Less anywhere else, like Sinatra, use `sprockets-less`._
8
+
9
+ ## Installing
10
+
11
+ Once you have set up Sprockets to behave on Rails 2.3 (see RAILS_UP for how to uprate your app), just bundle up less-rails in your Gemfile. This will pull in less as a runtime dependency too. This gem shall not be published at rubygems under this name, so be sure to set up both git and the branch correctly.
12
+
13
+ gem 'less-rails', :git => https://github.com/lloeki/less-rails.git, :branch => 'rails-2.3-backport'
14
+
15
+ ## Usage
16
+
17
+ In your Rack application, setup Sprockets as you normally would, and require "sprockets-less":
18
+
19
+ ``` ruby
20
+ require "sprockets"
21
+ require "sprockets-less"
22
+ require "less"
23
+
24
+ map "/assets" do
25
+ environment = Sprockets::Environment.new
26
+ environment.append_path "assets/stylesheets"
27
+ run environment
28
+ end
29
+
30
+ map "/" do
31
+ run YourRackApp
32
+ end
33
+ ```
34
+
35
+ ## Configuration
36
+
37
+ If you would like to configure any of the Less options, you can do so like this:
38
+
39
+ ```ruby
40
+ Sprockets::Less.options[:compress] = true
41
+ ```
42
+
43
+ ## Import Hooks
44
+
45
+ Any `@import` to a `.less` file will automatically declare that file as a sprockets dependency to the file importing it. This means that you can edit imported framework files and see changes reflected in the parent durning development. So this:
46
+
47
+ ```css
48
+ @import "frameworks/bootstrap/mixins";
49
+
50
+ #leftnav { .border-radius(5px); }
51
+ ```
52
+
53
+ Will end up acting as if you had done this below:
54
+
55
+ ```css
56
+ /*
57
+ *= depend_on "frameworks/bootstrap/mixins.less"
58
+ */
59
+
60
+ @import "frameworks/bootstrap/mixins";
61
+
62
+ #leftnav { .border-radius(5px); }
63
+ ```
64
+
65
+ ## Helpers
66
+
67
+ *Warning: this is currently non-functional*
68
+
69
+ When referencing assets use the following helpers in LESS.
70
+
71
+ ```css
72
+ asset-path(@relative-asset-path) /* Returns a string to the asset. */
73
+ asset-path("rails.png") /* Becomes: "/assets/rails.png" */
74
+
75
+ asset-url(@relative-asset-path) /* Returns url reference to the asset. */
76
+ asset-url("rails.png") /* Becomes: url(/assets/rails.png) */
77
+ ```
78
+
79
+ As a convenience, for each of the following asset classes there are corresponding `-path` and `-url` helpers image, font, video, audio, javascript and stylesheet. The following examples only show the `-url` variants since you get the idea of the `-path` ones above.
80
+
81
+ ```css
82
+ image-url("rails.png") /* Becomes: url(/assets/rails.png) */
83
+ font-url("rails.ttf") /* Becomes: url(/assets/rails.ttf) */
84
+ video-url("rails.mp4") /* Becomes: url(/videos/rails.mp4) */
85
+ audio-url("rails.mp3") /* Becomes: url(/audios/rails.mp3) */
86
+ javascript-url("rails.js") /* Becomes: url(/assets/rails.js) */
87
+ stylesheet-url("rails.css") /* Becomes: url(/assets/rails.css) */
88
+ ```
89
+
90
+ Lastly, we provide a data url method for base64 encoding assets.
91
+
92
+ ```css
93
+ asset-data-uri("rails.png") /* Becomes: url(data:image/png;base64,iVBORw0K...) */
94
+ ```
95
+
96
+ Please note that these helpers are only available server-side, and something like ERB templates should be used if client-side rendering is desired.
97
+
98
+
99
+ ## License
100
+
101
+ Sprocket::Less is Copyright (c) 2012 Loic Nageleisen, <loic.nageleisen@gmail.com>
102
+
data/Rakefile ADDED
@@ -0,0 +1,5 @@
1
+ require 'bundler/gem_tasks'
2
+ require 'rspec/core/rake_task'
3
+
4
+ RSpec::Core::RakeTask.new
5
+ task :default => :spec
@@ -0,0 +1,153 @@
1
+ require 'less'
2
+
3
+ module Sprockets
4
+ module Less
5
+
6
+ # Sprockets-aware Less functions
7
+ module Functions
8
+ def asset_data_url(path)
9
+ "url(#{sprockets_context.asset_data_uri(path)})"
10
+ end
11
+
12
+ def asset_path(asset)
13
+ public_path(asset).inspect
14
+ end
15
+
16
+ def asset_url(asset)
17
+ "url(#{public_path(asset)})"
18
+ end
19
+
20
+ def image_path(img)
21
+ sprockets_context.image_path(img).inspect
22
+ end
23
+
24
+ def asset_data_uri(source)
25
+ "url(#{sprockets_context.asset_data_uri(source.value)})"
26
+ end
27
+
28
+ def image_url(img)
29
+ "url(#{sprockets_context.image_path(img)})"
30
+ end
31
+
32
+ def video_path(video)
33
+ sprockets_context.video_path(video).inspect
34
+ end
35
+
36
+ def video_url(video)
37
+ "url(#{sprockets_context.video_path(video)})"
38
+ end
39
+
40
+ def audio_path(audio)
41
+ sprockets_context.audio_path(audio).inspect
42
+ end
43
+
44
+ def audio_url(audio)
45
+ "url(#{context.audio_path(audio)})"
46
+ end
47
+
48
+ def javascript_path(javascript)
49
+ context.javascript_path(javascript).inspect
50
+ end
51
+
52
+ def javascript_url(javascript)
53
+ "url(#{context.javascript_path(javascript)})"
54
+ end
55
+
56
+ def stylesheet_path(stylesheet)
57
+ sprockets_context.stylesheet_path(stylesheet).inspect
58
+ end
59
+
60
+ def stylesheet_url(stylesheet)
61
+ "url(#{sprockets_context.stylesheet_path(stylesheet)})"
62
+ end
63
+
64
+ protected
65
+
66
+ def public_path(asset)
67
+ sprockets_context.asset_paths.compute_public_path asset, '/assets'
68
+ end
69
+
70
+ def context_asset_data_uri(path)
71
+
72
+ end
73
+ end
74
+ end
75
+ end
76
+
77
+ module Less
78
+
79
+ # Wrapper for the `tree` JavaScript module.
80
+ #
81
+ # This is not to be confused with Less::Parser::Tree, as Less uses that
82
+ # to wrap the AST (which is actually a tree.RuleSet) resulting from parsing
83
+ class Tree
84
+ attr_reader :sprockets_context
85
+
86
+ def initialize(options)
87
+ @tree = Less.instance_eval { @loader.require('less/tree') }
88
+ @sprockets_context = options[:importer].context
89
+ extend_js Sprockets::Less::Functions
90
+ end
91
+
92
+ private
93
+
94
+ # Transforms a css function name string to a (method) symbol
95
+ def css_to_sym(str)
96
+ str.gsub('-','_').to_sym
97
+ end
98
+
99
+ # Transforms a method symbol to a css function name string
100
+ def sym_to_css(sym)
101
+ sym.to_sym.to_s.gsub('_', '-')
102
+ end
103
+
104
+ # Removes quotes
105
+ def unquote(str)
106
+ s = str.to_s.strip
107
+ s =~ /^['"](.*?)['"]$/ ? $1 : s
108
+ end
109
+
110
+ # Creates a JavaScript anonymous function from a Ruby block
111
+ def anonymous_function(block)
112
+ lambda do |*args|
113
+ # args: (this, node) v8 >= 0.10, otherwise (node)
114
+ raise ArgumentError, "missing node" if args.empty?
115
+ @tree[:Anonymous].new block.call(@tree, args.last)
116
+ end
117
+ end
118
+
119
+ # Access to the Less JavaScript function object
120
+ def functions
121
+ @tree['functions']
122
+ end
123
+
124
+ # Injects a Ruby method into the JavaScript Less
125
+ def add_function(name, &block)
126
+ functions[name] = anonymous_function(block)
127
+ end
128
+
129
+ # Adds all of a module's public instance methods as Less functions
130
+ def extend_js(mod)
131
+ extend mod
132
+ mod.public_instance_methods.each do |method_name|
133
+ add_function(sym_to_css(method_name)) { |tree, cxt|
134
+ send method_name.to_sym, unquote(cxt.toCSS())
135
+ }
136
+ end
137
+ end
138
+
139
+ end
140
+
141
+ class Parser
142
+
143
+ attr_reader :tree
144
+
145
+ # Override the parser's initialization to improve Less `tree`
146
+ # with sprockets awareness
147
+ alias_method :initialize_without_tree, :initialize
148
+ def initialize(options={})
149
+ initialize_without_tree(options)
150
+ @tree = Less::Tree.new(options)
151
+ end
152
+ end
153
+ end
@@ -0,0 +1,95 @@
1
+ module Sprockets
2
+ module Less
3
+ class Importer
4
+ # TODO: override `tree.Import` to inject our own import resolution
5
+ # logic and .push() into its `imports` argument.
6
+ # See less/tree/import.js
7
+
8
+ # Reference to the Sprockets context
9
+ attr_reader :context
10
+
11
+ IMPORT_SCANNER = /^\s*@import\s*['"]([^'"]+)['"]\s*;/.freeze
12
+
13
+ def initialize(context)
14
+ @context = context
15
+ end
16
+
17
+ # Finds an asset from the given path. This is where
18
+ # we make Sprockets behave like Less, and import partial
19
+ # style paths.
20
+ def resolve(path, base_path)
21
+ possible_files(path, base_path).each do |file|
22
+ context.resolve(file) { |found| return found if context.asset_requirable?(found) }
23
+ end
24
+
25
+ nil
26
+ end
27
+
28
+ # Returns all of the possible paths (including partial variations)
29
+ # to attempt to resolve with the given path.
30
+ def possible_files(path, base_path)
31
+ path = Pathname.new(path)
32
+ base_path = Pathname.new(base_path).dirname
33
+ root_path = Pathname.new(context.root_path)
34
+ paths = [ path, partialize_path(path) ]
35
+
36
+ # Add the relative path from the root, if necessary
37
+ if path.relative? && base_path != root_path && path.to_s !~ /\A\.\//
38
+ relative_path = base_path.relative_path_from(root_path).join path
39
+
40
+ paths.unshift(relative_path, partialize_path(relative_path))
41
+ end
42
+
43
+ paths.compact
44
+ end
45
+
46
+ # Returns the partialized version of the given path.
47
+ # Returns nil if the path is already to a partial.
48
+ def partialize_path(path)
49
+ if path.basename.to_s !~ /\A_/
50
+ Pathname.new path.to_s.sub(/([^\/]+)\Z/, '_\1')
51
+ end
52
+ end
53
+
54
+ # Returns the syntax of the given path.
55
+ def syntax(path)
56
+ path.to_s.include?('.css') ? :css : :less
57
+ end
58
+
59
+ # Returns the string to be passed to the Less engine. We use
60
+ # Sprockets to process the file, but we remove any Less processors
61
+ # because we need to let the Sass::Engine handle that.
62
+ def evaluate(path)
63
+ processors = context.environment.attributes_for(path).processors.dup
64
+ processors.delete_if { |processor| processor < Tilt::LessTemplate }
65
+ context.evaluate(path, :processors => processors)
66
+ end
67
+
68
+ # Tests if a path will make the import directive be passed as is.
69
+ def passthrough?(pathname)
70
+ pathname.to_s.end_with?('.css')
71
+ end
72
+
73
+ # Assemble dependencies for the context
74
+ def process_dependencies(data)
75
+ import_paths = data.scan(IMPORT_SCANNER).flatten.compact.uniq
76
+ import_paths.each do |path|
77
+ pathname = begin
78
+ #TODO: use resolve to partialize paths
79
+ context.resolve(path)
80
+ rescue Sprockets::FileNotFound
81
+ nil
82
+ end
83
+
84
+ unless pathname.nil? || passthrough?(pathname)
85
+ # mark dependency in Sprockets context
86
+ context.depend_on(path)
87
+ # recurse for more dependencies
88
+ process_dependencies File.read(pathname)
89
+ end
90
+ end
91
+ end
92
+
93
+ end
94
+ end
95
+ end
@@ -0,0 +1,103 @@
1
+ require 'tilt'
2
+
3
+ module Sprockets
4
+ module Less
5
+ class LessTemplate < Tilt::LessTemplate
6
+ self.default_mime_type = 'text/css'
7
+
8
+ # A reference to the current Sprockets context
9
+ attr_reader :context
10
+
11
+ # Templates are initialized once the functions are added.
12
+ def self.engine_initialized?
13
+ super && (!Less.add_less_functions || defined?(Functions))
14
+ end
15
+
16
+ # Add the Less functions if they haven't already been added.
17
+ def initialize_engine
18
+ super unless self.class.superclass.engine_initialized?
19
+
20
+ if Less.add_less_functions
21
+ require 'sprockets/less/functions'
22
+ end
23
+ end
24
+
25
+ # Define the expected syntax for the template
26
+ def syntax
27
+ :less
28
+ end
29
+
30
+ # See `Tilt::Template#prepare`.
31
+ def prepare
32
+ @context = nil
33
+ @output = nil
34
+ end
35
+
36
+ # See `Tilt::Template#evaluate`.
37
+ def evaluate(context, locals, &block)
38
+ @output ||= begin
39
+ @context = context
40
+ process_dependencies less_options
41
+ parser = ::Less::Parser.new less_options
42
+ tree = parser.parse(data)
43
+ tree.to_css css_options
44
+ end
45
+ end
46
+
47
+ protected
48
+
49
+ def process_dependencies options
50
+ options[:importer].process_dependencies data
51
+ end
52
+
53
+ # Returns a Sprockets-aware cache store.
54
+ def cache_store
55
+ return nil if context.environment.cache.nil?
56
+
57
+ CacheStore.new context.environment
58
+ end
59
+
60
+ # A reference to the custom Less importer, `Sprockets::Less::Importer`.
61
+ def importer
62
+ Importer.new context
63
+ end
64
+
65
+ # Assemble the options for the Less parser
66
+ def less_options
67
+ new_options = merge_less_options(global_less_options, options)
68
+ merge_less_options(new_options, default_less_options)
69
+ end
70
+
71
+ # Extract options for CSS output
72
+ def css_options
73
+ css_keys = [:compress, :optimization, :silent, :color]
74
+ Hash[less_options.to_enum.to_a.select{|k, _| css_keys.include? k}]
75
+ end
76
+
77
+ # Get global Less options
78
+ def global_less_options
79
+ Sprockets::Less.options.dup
80
+ end
81
+
82
+ def default_less_options
83
+ {
84
+ :filename => eval_file,
85
+ :line => line,
86
+ :paths => context.environment.paths,
87
+ #:syntax => syntax,
88
+ #:cache_store => cache_store,
89
+ :importer => importer
90
+ }
91
+ end
92
+
93
+ # Merges two sets of Less parser options, prepending
94
+ # the `:paths` instead of clobbering them.
95
+ def merge_less_options(options, other_options)
96
+ if (load_paths = options[:paths]) && (other_paths = other_options[:paths])
97
+ other_options[:paths] = other_paths + load_paths
98
+ end
99
+ options.merge other_options
100
+ end
101
+ end
102
+ end
103
+ end
@@ -0,0 +1,5 @@
1
+ module Sprockets
2
+ module Less
3
+ VERSION = '0.5.2'
4
+ end
5
+ end
@@ -0,0 +1,24 @@
1
+ require 'sprockets/less/version'
2
+ require 'sprockets/less/template'
3
+ require 'sprockets/engines'
4
+
5
+ module Sprockets
6
+ module Less
7
+ autoload :Importer, 'sprockets/less/importer'
8
+
9
+ class << self
10
+ # Global configuration for `Less::Parser` instances
11
+ attr_accessor :options
12
+
13
+ # When false, the asset path helper provided by
14
+ # `sprockets-helpers` will not be added as Less functions.
15
+ # `true` by default.
16
+ attr_accessor :add_less_functions
17
+ end
18
+
19
+ @options = {}
20
+ @add_less_functions = true
21
+ end
22
+
23
+ register_engine '.less', Less::LessTemplate
24
+ end
@@ -0,0 +1 @@
1
+ require 'sprockets/less'
Binary file
@@ -0,0 +1,12 @@
1
+ require 'sprockets'
2
+ require 'sprockets-less'
3
+ require 'sprockets-helpers'
4
+ require 'construct'
5
+
6
+ # Requires supporting files with custom matchers and macros, etc,
7
+ # in ./support/ and its subdirectories.
8
+ Dir["#{File.dirname(__FILE__)}/support/**/*.rb"].each { |f| require f }
9
+
10
+ RSpec.configure do |config|
11
+ config.include Construct::Helpers
12
+ end
@@ -0,0 +1,260 @@
1
+ require 'spec_helper'
2
+
3
+ RSpec.configure do |c|
4
+ c.filter_run_excluding :broken => true
5
+ c.filter_run_excluding :todo => true
6
+ end
7
+
8
+ describe Sprockets::Less do
9
+ before :each do
10
+ @root = create_construct
11
+ @assets = @root.directory 'assets'
12
+ @env = Sprockets::Environment.new @root.to_s
13
+ @env.append_path @assets.to_s
14
+ end
15
+
16
+ after :each do
17
+ @root.destroy!
18
+ end
19
+
20
+ it 'processes less files normally' do
21
+ @assets.file 'main.css.less', '//= require dep'
22
+ @assets.file 'dep.less', %(body { color: blue; })
23
+ asset = @env['main.css']
24
+ asset.to_s.should == "body {\n color: blue;\n}\n"
25
+ end
26
+
27
+ it 'imports standard files' do
28
+ @assets.file 'main.css.less', %(@import "dep";\nbody { color: @color; })
29
+ @assets.file 'dep.less', '@color: blue;'
30
+ asset = @env['main.css']
31
+ asset.to_s.should == "body {\n color: #0000ff;\n}\n"
32
+ end
33
+
34
+ it 'imports partials' do
35
+ @assets.file 'main.css.less', %(@import "_dep";\nbody { color: @color; })
36
+ @assets.file '_dep.less', '@color: blue;'
37
+ asset = @env['main.css']
38
+ asset.to_s.should == "body {\n color: #0000ff;\n}\n"
39
+ end
40
+
41
+ it 'imports files with the correct content type', :broken => true do
42
+ @assets.file 'main.css.less', %(@import "dep";\nbody { color: @color; })
43
+ @assets.file 'dep.js', 'var app = {};'
44
+ @assets.file '_dep.less', '@color: blue;'
45
+ asset = @env['main.css']
46
+ asset.to_s.should == "body {\n color: #0000ff;\n}\n"
47
+ end
48
+
49
+ it 'imports files with directives', :todo => true do
50
+ @assets.file 'main.css.less', %(@import "dep";)
51
+ @assets.file 'dep.css', "/*\n *= require subdep\n */"
52
+ @assets.file 'subdep.css.less', "@color: blue;\nbody { color: @color; }"
53
+ asset = @env['main.css']
54
+ asset.to_s.should include("body {\n color: #0000ff; }\n")
55
+ end
56
+
57
+ it 'imports files with additional processors', :todo => true do
58
+ @assets.file 'main.css.less', %(@import "dep";\nbody { color: @color; })
59
+ @assets.file 'dep.css.less.erb', "@color: <%= 'blue' %>;"
60
+ asset = @env['main.css']
61
+ asset.to_s.should == "body {\n color: blue; }\n"
62
+ end
63
+
64
+ it 'imports relative files', :todo => true do
65
+ @assets.file 'folder/main.css.less', %(@import "./dep-1";\n@import "./subfolder/dep-2";\nbody { background-color: @background-color; color: @color; })
66
+ @assets.file 'folder/dep-1.less', '@background-color: red;'
67
+ @assets.file 'folder/subfolder/dep-2.less', '@color: blue;'
68
+ asset = @env['folder/main.css']
69
+ asset.to_s.should == "body {\n background-color: #ff0000;\n color: #0000ff; }\n"
70
+ end
71
+
72
+ it 'imports relative partials', :todo => true do
73
+ @assets.file 'folder/main.css.less', %(@import "./dep-1";\n@import "./subfolder/dep-2";\nbody { background-color: @background-color; color: @color; })
74
+ @assets.file 'folder/_dep-1.less', '@background-color: red;'
75
+ @assets.file 'folder/subfolder/_dep-2.less', '@color: blue;'
76
+ asset = @env['folder/main.css']
77
+ asset.to_s.should == "body {\n background-color: #ff0000;\n color: blue; }\n"
78
+ end
79
+
80
+ it 'imports relative files without preceding ./', :todo => true do
81
+ @assets.file 'folder/main.css.less', %(@import "dep-1";\n@import "subfolder/dep-2";\nbody { background-color: @background-color; color: @color; })
82
+ @assets.file 'folder/dep-1.less', '@background-color: red;'
83
+ @assets.file 'folder/subfolder/dep-2.less', '@color: blue;'
84
+ asset = @env['folder/main.css']
85
+ asset.to_s.should == "body {\n background-color: red;\n color: blue; }\n"
86
+ end
87
+
88
+ it 'imports relative partials without preceding ./', :todo => true do
89
+ @assets.file 'folder/main.css.less', %(@import "dep-1";\n@import "subfolder/dep-2";\nbody { background-color: @background-color; color: @color; })
90
+ @assets.file 'folder/_dep-1.less', '@background-color: red;'
91
+ @assets.file 'folder/subfolder/_dep-2.less', '@color: blue;'
92
+ asset = @env['folder/main.css']
93
+ asset.to_s.should == "body {\n background-color: red;\n color: blue; }\n"
94
+ end
95
+
96
+ it 'imports files relative to root' do
97
+ @assets.file 'folder/main.css.less', %(@import "dep";\nbody { color: @color; })
98
+ @assets.file 'dep.less', '@color: blue;'
99
+ asset = @env['folder/main.css']
100
+ asset.to_s.should == "body {\n color: #0000ff;\n}\n"
101
+ end
102
+
103
+ it 'imports partials relative to root', :broken => true do
104
+ @assets.file 'folder/main.css.less', %(@import "dep";\nbody { color: @color; })
105
+ @assets.file '_dep.less', '@color: blue;'
106
+ asset = @env['folder/main.css']
107
+ asset.to_s.should == "body {\n color: #0000ff;\n}\n"
108
+ end
109
+
110
+ it 'shares Less environment with other imports' do
111
+ @assets.file 'main.css.less', %(@import "dep-1";\n@import "dep-2";)
112
+ @assets.file 'dep-1.less', '@color: blue;'
113
+ @assets.file 'dep-2.less', 'body { color: @color; }'
114
+ asset = @env['main.css']
115
+ asset.to_s.should == "body {\n color: #0000ff;\n}\n"
116
+ end
117
+
118
+ it 'imports files from the assets load path' do
119
+ vendor = @root.directory 'vendor'
120
+ @env.append_path vendor.to_s
121
+
122
+ @assets.file 'main.css.less', %(@import "dep";\nbody { color: @color; })
123
+ vendor.file 'dep.less', '@color: blue;'
124
+ asset = @env['main.css']
125
+ asset.to_s.should == "body {\n color: #0000ff;\n}\n"
126
+ end
127
+
128
+ it 'allows global Less configuration' do
129
+ Sprockets::Less.options[:compress] = true
130
+ @assets.file 'main.css.less', "body {\n color: blue;\n}"
131
+
132
+ asset = @env['main.css']
133
+ asset.to_s.should == "body{color:blue;}\n"
134
+ Sprockets::Less.options.delete(:compress)
135
+ end
136
+
137
+ it 'imports files from the Less load path' do
138
+ vendor = @root.directory 'vendor'
139
+ Sprockets::Less.options[:paths] = [ vendor.to_s ]
140
+
141
+ @assets.file 'main.css.less', %(@import "dep";\nbody { color: @color; })
142
+ vendor.file 'dep.less', '@color: blue;'
143
+ asset = @env['main.css']
144
+ asset.to_s.should == "body {\n color: #0000ff;\n}\n"
145
+ Sprockets::Less.options.delete(:paths)
146
+ end
147
+
148
+ it 'adds dependencies when imported' do
149
+ @assets.file 'main.css.less', %(@import "dep";\nbody { color: @color; })
150
+ dep = @assets.file 'dep.less', '@color: blue;'
151
+
152
+ asset = @env['main.css']
153
+ asset.should be_fresh(@env)
154
+
155
+ mtime = Time.now + 1
156
+ dep.open('w') { |f| f.write '@color: red;' }
157
+ dep.utime mtime, mtime
158
+
159
+ asset.should_not be_fresh(@env)
160
+ end
161
+
162
+ it 'adds dependencies from assets when imported' do
163
+ @assets.file 'main.css.less', %(@import "dep-1";\nbody { color: @color; })
164
+ @assets.file 'dep-1.less', %(@import "dep-2";\n)
165
+ dep = @assets.file 'dep-2.less', '@color: blue;'
166
+
167
+ asset = @env['main.css']
168
+ asset.should be_fresh(@env)
169
+
170
+ mtime = Time.now + 1
171
+ dep.open('w') { |f| f.write '@color: red;' }
172
+ dep.utime mtime, mtime
173
+
174
+ asset.should_not be_fresh(@env)
175
+ end
176
+
177
+ it "uses the environment's cache", :todo => true do
178
+ cache = {}
179
+ @env.cache = cache
180
+
181
+ @assets.file 'main.css.less', %(@color: blue;\nbody { color: @color; })
182
+
183
+ @env['main.css'].to_s
184
+ less_cache = cache.keys.detect { |key| key =~ /main\.css\.less/ }
185
+ less_cache.should_not be_nil
186
+ end
187
+
188
+ it 'adds the #asset_path helper', :broken => true do
189
+ @assets.file 'asset_path.css.less', %(body { background: url(asset-path("image.jpg")); })
190
+ @assets.file 'asset_url.css.less', %(body { background: asset-url("image.jpg"); })
191
+ @assets.file 'asset_path_options.css.less', %(body { background: url(asset-path("image.jpg", $digest: true, $prefix: "/themes")); })
192
+ @assets.file 'asset_url_options.css.less', %(body { background: asset-url("image.jpg", $digest: true, $prefix: "/themes"); })
193
+ @assets.file 'image.jpg'
194
+
195
+ @env['asset_path.css'].to_s.should == %(body {\n background: url("/assets/image.jpg"); }\n)
196
+ @env['asset_url.css'].to_s.should == %(body {\n background: url("/assets/image.jpg"); }\n)
197
+ @env['asset_path_options.css'].to_s.should =~ %r(body \{\n background: url\("/themes/image-[0-9a-f]+.jpg"\); \}\n)
198
+ @env['asset_url_options.css'].to_s.should =~ %r(body \{\n background: url\("/themes/image-[0-9a-f]+.jpg"\); \}\n)
199
+ end
200
+
201
+ it 'adds the #image_path helper', :broken => true do
202
+ @assets.file 'image_path.css.less', %(body { background: url(image-path("image.jpg")); })
203
+ @assets.file 'image_url.css.less', %(body { background: image-url("image.jpg"); })
204
+ @assets.file 'image_path_options.css.less', %(body { background: url(image-path("image.jpg", $digest: true, $prefix: "/themes")); })
205
+ @assets.file 'image_url_options.css.less', %(body { background: image-url("image.jpg", $digest: true, $prefix: "/themes"); })
206
+ @assets.file 'image.jpg'
207
+
208
+ @env['image_path.css'].to_s.should == %(body {\n background: url("/assets/image.jpg"); }\n)
209
+ @env['image_url.css'].to_s.should == %(body {\n background: url("/assets/image.jpg"); }\n)
210
+ @env['image_path_options.css'].to_s.should =~ %r(body \{\n background: url\("/themes/image-[0-9a-f]+.jpg"\); \}\n)
211
+ @env['image_url_options.css'].to_s.should =~ %r(body \{\n background: url\("/themes/image-[0-9a-f]+.jpg"\); \}\n)
212
+ end
213
+
214
+ it 'adds the #font_path helper', :broken => true do
215
+ @assets.file 'font_path.css.less', %(@font-face { src: url(font-path("font.ttf")); })
216
+ @assets.file 'font_url.css.less', %(@font-face { src: font-url("font.ttf"); })
217
+ @assets.file 'font_path_options.css.less', %(@font-face { src: url(font-path("font.ttf", $digest: true, $prefix: "/themes")); })
218
+ @assets.file 'font_url_options.css.less', %(@font-face { src: font-url("font.ttf", $digest: true, $prefix: "/themes"); })
219
+ @assets.file 'font.ttf'
220
+
221
+ @env['font_path.css'].to_s.should == %(@font-face {\n src: url("/assets/font.ttf"); }\n)
222
+ @env['font_url.css'].to_s.should == %(@font-face {\n src: url("/assets/font.ttf"); }\n)
223
+ @env['font_path_options.css'].to_s.should =~ %r(@font-face \{\n src: url\("/themes/font-[0-9a-f]+.ttf"\); \}\n)
224
+ @env['font_url_options.css'].to_s.should =~ %r(@font-face \{\n src: url\("/themes/font-[0-9a-f]+.ttf"\); \}\n)
225
+ end
226
+
227
+ it 'adds the #asset_data_uri helper', :broken => true do
228
+ @assets.file 'asset_data_uri.css.less', %(body { background: asset-data-uri("image.jpg"); })
229
+ @assets.file 'image.jpg', File.read('spec/fixtures/image.jpg')
230
+
231
+ @env['asset_data_uri.css'].to_s.should == %(body {\n background: url(data:image/jpeg;base64,%2F9j%2F4AAQSkZJRgABAgAAZABkAAD%2F7AARRHVja3kAAQAEAAAAPAAA%2F%2B4ADkFkb2JlAGTAAAAAAf%2FbAIQABgQEBAUEBgUFBgkGBQYJCwgGBggLDAoKCwoKDBAMDAwMDAwQDA4PEA8ODBMTFBQTExwbGxscHx8fHx8fHx8fHwEHBwcNDA0YEBAYGhURFRofHx8fHx8fHx8fHx8fHx8fHx8fHx8fHx8fHx8fHx8fHx8fHx8fHx8fHx8fHx8fHx8f%2F8AAEQgAAQABAwERAAIRAQMRAf%2FEAEoAAQAAAAAAAAAAAAAAAAAAAAgBAQAAAAAAAAAAAAAAAAAAAAAQAQAAAAAAAAAAAAAAAAAAAAARAQAAAAAAAAAAAAAAAAAAAAD%2F2gAMAwEAAhEDEQA%2FACoD%2F9k%3D); }\n)
232
+ end
233
+
234
+ it "mirrors Less::Rails's #asset_path helpers", :broken => true do
235
+ @assets.file 'asset_path.css.less', %(body { background: url(asset-path("image.jpg", image)); })
236
+ @assets.file 'asset_url.css.less', %(body { background: asset-url("icon.jpg", image); })
237
+ @assets.file 'image.jpg'
238
+
239
+ @env['asset_path.css'].to_s.should == %(body {\n background: url("/assets/image.jpg"); }\n)
240
+ @env['asset_url.css'].to_s.should == %(body {\n background: url("/images/icon.jpg"); }\n)
241
+ end
242
+
243
+ describe Sprockets::Less::LessTemplate do
244
+ describe 'initialize_engine' do
245
+ it 'initializes super if super is uninitialized' do
246
+ Tilt::LessTemplate.stub(:engine_initialized?).and_return false
247
+ template = Sprockets::Less::LessTemplate.new {}
248
+ template.should_receive(:require_template_library) # called from Tilt::LessTemplate.initialize
249
+ template.initialize_engine
250
+ end
251
+
252
+ it "does not initializes super if super is initialized to silence warnings" do
253
+ Tilt::LessTemplate.stub(:engine_initialized?).and_return true
254
+ template = Sprockets::Less::LessTemplate.new {}
255
+ template.should_not_receive(:require_template_library) # called from Tilt::LessTemplate.initialize
256
+ template.initialize_engine
257
+ end
258
+ end
259
+ end
260
+ end
@@ -0,0 +1,17 @@
1
+ RSpec::Matchers.define :be_fresh do |env|
2
+ match do |actual|
3
+ if actual.method(:fresh?).arity == 1
4
+ actual.fresh?(env)
5
+ else
6
+ actual.fresh?
7
+ end
8
+ end
9
+
10
+ failure_message_for_should do |env|
11
+ 'expected asset to be fresh'
12
+ end
13
+
14
+ failure_message_for_should_not do |env|
15
+ 'expected asset to be stale'
16
+ end
17
+ end
@@ -0,0 +1,23 @@
1
+ # -*- encoding: utf-8 -*-
2
+ $:.push File.expand_path("../lib", __FILE__)
3
+ require "sprockets/less/version"
4
+
5
+ Gem::Specification.new do |s|
6
+ s.name = "sprockets-less"
7
+ s.version = Sprockets::Less::VERSION
8
+ s.authors = ["Loic Nageleisen"]
9
+ s.email = ["loic.nageleisen@gmail.com"]
10
+ s.homepage = "http://github.com/lloeki/sprockets-less"
11
+ s.summary = %q{The dynamic stylesheet language for the Sprockets asset pipeline.}
12
+ s.description = %q{The dynamic stylesheet language for the Sprockets asset pipeline.}
13
+ s.files = `git ls-files`.split("\n")
14
+ s.test_files = `git ls-files -- {test,spec,features}/*`.split("\n")
15
+ s.executables = `git ls-files -- bin/*`.split("\n").map{ |f| File.basename(f) }
16
+ s.require_paths = ["lib"]
17
+ s.add_dependency 'less', '~> 2.2.0'
18
+ s.add_dependency 'tilt', '~> 1.1'
19
+ s.add_development_dependency 'sprockets-helpers', '~> 0.6'
20
+ s.add_development_dependency 'rspec', '~> 2.6'
21
+ s.add_development_dependency 'test-construct', '~> 1.2'
22
+ s.add_development_dependency 'rake'
23
+ end
metadata ADDED
@@ -0,0 +1,175 @@
1
+ --- !ruby/object:Gem::Specification
2
+ name: sprockets-less
3
+ version: !ruby/object:Gem::Version
4
+ hash: 15
5
+ prerelease:
6
+ segments:
7
+ - 0
8
+ - 5
9
+ - 2
10
+ version: 0.5.2
11
+ platform: ruby
12
+ authors:
13
+ - Loic Nageleisen
14
+ autorequire:
15
+ bindir: bin
16
+ cert_chain: []
17
+
18
+ date: 2012-08-06 00:00:00 +02:00
19
+ default_executable:
20
+ dependencies:
21
+ - !ruby/object:Gem::Dependency
22
+ requirement: &id001 !ruby/object:Gem::Requirement
23
+ none: false
24
+ requirements:
25
+ - - ~>
26
+ - !ruby/object:Gem::Version
27
+ hash: 7
28
+ segments:
29
+ - 2
30
+ - 2
31
+ - 0
32
+ version: 2.2.0
33
+ version_requirements: *id001
34
+ name: less
35
+ prerelease: false
36
+ type: :runtime
37
+ - !ruby/object:Gem::Dependency
38
+ requirement: &id002 !ruby/object:Gem::Requirement
39
+ none: false
40
+ requirements:
41
+ - - ~>
42
+ - !ruby/object:Gem::Version
43
+ hash: 13
44
+ segments:
45
+ - 1
46
+ - 1
47
+ version: "1.1"
48
+ version_requirements: *id002
49
+ name: tilt
50
+ prerelease: false
51
+ type: :runtime
52
+ - !ruby/object:Gem::Dependency
53
+ requirement: &id003 !ruby/object:Gem::Requirement
54
+ none: false
55
+ requirements:
56
+ - - ~>
57
+ - !ruby/object:Gem::Version
58
+ hash: 7
59
+ segments:
60
+ - 0
61
+ - 6
62
+ version: "0.6"
63
+ version_requirements: *id003
64
+ name: sprockets-helpers
65
+ prerelease: false
66
+ type: :development
67
+ - !ruby/object:Gem::Dependency
68
+ requirement: &id004 !ruby/object:Gem::Requirement
69
+ none: false
70
+ requirements:
71
+ - - ~>
72
+ - !ruby/object:Gem::Version
73
+ hash: 15
74
+ segments:
75
+ - 2
76
+ - 6
77
+ version: "2.6"
78
+ version_requirements: *id004
79
+ name: rspec
80
+ prerelease: false
81
+ type: :development
82
+ - !ruby/object:Gem::Dependency
83
+ requirement: &id005 !ruby/object:Gem::Requirement
84
+ none: false
85
+ requirements:
86
+ - - ~>
87
+ - !ruby/object:Gem::Version
88
+ hash: 11
89
+ segments:
90
+ - 1
91
+ - 2
92
+ version: "1.2"
93
+ version_requirements: *id005
94
+ name: test-construct
95
+ prerelease: false
96
+ type: :development
97
+ - !ruby/object:Gem::Dependency
98
+ requirement: &id006 !ruby/object:Gem::Requirement
99
+ none: false
100
+ requirements:
101
+ - - ">="
102
+ - !ruby/object:Gem::Version
103
+ hash: 3
104
+ segments:
105
+ - 0
106
+ version: "0"
107
+ version_requirements: *id006
108
+ name: rake
109
+ prerelease: false
110
+ type: :development
111
+ description: The dynamic stylesheet language for the Sprockets asset pipeline.
112
+ email:
113
+ - loic.nageleisen@gmail.com
114
+ executables: []
115
+
116
+ extensions: []
117
+
118
+ extra_rdoc_files: []
119
+
120
+ files:
121
+ - .gitignore
122
+ - Gemfile
123
+ - LICENSE
124
+ - README.md
125
+ - Rakefile
126
+ - lib/sprockets-less.rb
127
+ - lib/sprockets/less.rb
128
+ - lib/sprockets/less/functions.rb
129
+ - lib/sprockets/less/importer.rb
130
+ - lib/sprockets/less/template.rb
131
+ - lib/sprockets/less/version.rb
132
+ - spec/fixtures/image.jpg
133
+ - spec/spec_helper.rb
134
+ - spec/sprockets-less_spec.rb
135
+ - spec/support/be_fresh_matcher.rb
136
+ - sprockets-less.gemspec
137
+ has_rdoc: true
138
+ homepage: http://github.com/lloeki/sprockets-less
139
+ licenses: []
140
+
141
+ post_install_message:
142
+ rdoc_options: []
143
+
144
+ require_paths:
145
+ - lib
146
+ required_ruby_version: !ruby/object:Gem::Requirement
147
+ none: false
148
+ requirements:
149
+ - - ">="
150
+ - !ruby/object:Gem::Version
151
+ hash: 3
152
+ segments:
153
+ - 0
154
+ version: "0"
155
+ required_rubygems_version: !ruby/object:Gem::Requirement
156
+ none: false
157
+ requirements:
158
+ - - ">="
159
+ - !ruby/object:Gem::Version
160
+ hash: 3
161
+ segments:
162
+ - 0
163
+ version: "0"
164
+ requirements: []
165
+
166
+ rubyforge_project:
167
+ rubygems_version: 1.6.2
168
+ signing_key:
169
+ specification_version: 3
170
+ summary: The dynamic stylesheet language for the Sprockets asset pipeline.
171
+ test_files:
172
+ - spec/fixtures/image.jpg
173
+ - spec/spec_helper.rb
174
+ - spec/sprockets-less_spec.rb
175
+ - spec/support/be_fresh_matcher.rb