sprockets-less 0.5.2

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.
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