less 2.1.0 → 2.2.0

Sign up to get free protection for your applications and to get access to all the features.
data/.gitignore CHANGED
@@ -4,3 +4,4 @@
4
4
  *.rbc
5
5
  .yardoc
6
6
  Gemfile.lock
7
+ nbproject
data/.travis.yml CHANGED
@@ -3,6 +3,8 @@ rvm:
3
3
  - 1.9.2
4
4
  - 1.9.3
5
5
  - ree
6
+ - jruby-18mode
7
+ - jruby-19mode
6
8
  notifications:
7
9
  recipients:
8
10
  - cowboyd@thefrontside.net
data/Gemfile CHANGED
@@ -1,3 +1,7 @@
1
1
  source :rubygems
2
2
 
3
- gemspec
3
+ gemspec
4
+
5
+
6
+ gem "therubyracer", "~> 0.10.0", :require => nil, :platforms => :ruby
7
+ gem "therubyrhino", "~> 1.73.3", :require => nil, :platforms => :jruby
data/less.gemspec CHANGED
@@ -22,10 +22,9 @@ Gem::Specification.new do |s|
22
22
  s.executables = `git ls-files -- bin/*`.split("\n").map{ |f| File.basename(f) }
23
23
  s.require_paths = ["lib"]
24
24
 
25
- s.add_dependency "therubyracer", "~> 0.10.0"
26
- s.add_dependency "commonjs", "~> 0.2.0"
27
-
25
+ s.add_dependency "commonjs", "~> 0.2.6"
28
26
  s.add_development_dependency "rake"
29
27
  s.add_development_dependency "rspec", "~> 2.0"
28
+
30
29
  end
31
30
 
data/lib/less/defaults.rb CHANGED
@@ -1,13 +1,13 @@
1
-
2
1
  module Less
3
2
  module Defaults
4
3
 
5
4
  def defaults
6
- @defaults ||= {:paths => []}
5
+ @defaults ||= { :paths => [] }
7
6
  end
8
7
 
9
8
  def paths
10
9
  defaults[:paths]
11
10
  end
11
+
12
12
  end
13
13
  end
@@ -0,0 +1,41 @@
1
+ module Less
2
+
3
+ class Error < ::StandardError
4
+
5
+ def initialize(cause, value = nil)
6
+ @value = value
7
+ message = nil
8
+ if @value # 2 args passed
9
+ message = @value['message']
10
+ else # allow passing only value as first arg cause :
11
+ if cause.respond_to?(:'[]') && message = cause['message']
12
+ @value = cause
13
+ end
14
+ end
15
+
16
+ if cause.is_a?(::Exception)
17
+ @cause = cause
18
+ super(message || cause.message)
19
+ else
20
+ super(message || cause)
21
+ end
22
+ end
23
+
24
+ def cause
25
+ @cause
26
+ end
27
+
28
+ def backtrace
29
+ @cause ? @cause.backtrace : super
30
+ end
31
+
32
+ # function LessError(e, env) { ... }
33
+ %w{ type filename index line stack column extract }.each do |key|
34
+ class_eval "def #{key}; @value && @value['#{key}']; end"
35
+ end
36
+
37
+ end
38
+
39
+ class ParseError < Error; end
40
+
41
+ end
@@ -0,0 +1,110 @@
1
+ begin
2
+ require 'rhino' unless defined?(Rhino)
3
+ rescue LoadError => e
4
+ warn "[WARNING] Please install gem 'therubyrhino' to use Less under JRuby."
5
+ raise e
6
+ end
7
+
8
+ require 'rhino/version'
9
+ if Rhino::VERSION < '1.73.3'
10
+ raise LoadError, "expected gem 'therubyrhino' '>= 1.73.3' but got '#{Rhino::VERSION}'"
11
+ end
12
+
13
+ module Less
14
+ module JavaScript
15
+ class RhinoContext
16
+
17
+ def self.instance
18
+ return new # NOTE: for Rhino a context should be kept open per thread !
19
+ end
20
+
21
+ def initialize(globals = nil)
22
+ @rhino_context = Rhino::Context.new :java => true
23
+ if @rhino_context.respond_to?(:version)
24
+ @rhino_context.version = '1.8'
25
+ apply_1_8_compatibility! if @rhino_context.version.to_s != '1.8'
26
+ else
27
+ apply_1_8_compatibility!
28
+ end
29
+ fix_memory_limit! @rhino_context
30
+ globals.each { |key, val| @rhino_context[key] = val } if globals
31
+ end
32
+
33
+ def unwrap
34
+ @rhino_context
35
+ end
36
+
37
+ def exec(&block)
38
+ @rhino_context.open(&block)
39
+ rescue Rhino::JSError => e
40
+ handle_js_error(e)
41
+ end
42
+
43
+ def eval(source, options = {})
44
+ source = source.encode('UTF-8') if source.respond_to?(:encode)
45
+
46
+ source_name = options[:source_name] || "<eval>"
47
+ line_number = options[:line_number] || 1
48
+ @rhino_context.eval("(#{source})", source_name, line_number)
49
+ rescue Rhino::JSError => e
50
+ handle_js_error(e)
51
+ end
52
+
53
+ def call(properties, *args)
54
+ options = args.last.is_a?(::Hash) ? args.pop : {} # extract_option!
55
+
56
+ source_name = options[:source_name] || "<eval>"
57
+ line_number = options[:line_number] || 1
58
+ @rhino_context.eval(properties, source_name, line_number).call(*args)
59
+ rescue Rhino::JSError => e
60
+ handle_js_error(e)
61
+ end
62
+
63
+ def method_missing(symbol, *args)
64
+ if @rhino_context.respond_to?(symbol)
65
+ @rhino_context.send(symbol, *args)
66
+ else
67
+ super
68
+ end
69
+ end
70
+
71
+ private
72
+
73
+ def handle_js_error(e)
74
+ if e.value && e.value['type'] # LessError
75
+ raise Less::ParseError.new(e, e.value)
76
+ end
77
+ if e.unwrap.to_s == "missing closing `}`"
78
+ raise Less::ParseError.new(e.unwrap.to_s)
79
+ end
80
+ if e.message && e.message[0, 12] == "Syntax Error"
81
+ raise Less::ParseError.new(e)
82
+ else
83
+ raise Less::Error.new(e)
84
+ end
85
+ end
86
+
87
+ # Disables bytecode compiling which limits you to 64K scripts
88
+ def fix_memory_limit!(context)
89
+ if context.respond_to?(:optimization_level=)
90
+ context.optimization_level = -1
91
+ else
92
+ context.instance_eval { @native.setOptimizationLevel(-1) }
93
+ end
94
+ end
95
+
96
+ def apply_1_8_compatibility!
97
+ # TODO rather load ecma-5.js ...
98
+ @rhino_context.eval("
99
+ // String
100
+ if ( ! String.prototype.trim ) {
101
+ String.prototype.trim = function () {
102
+ return this.replace(/^\s+|\s+$/g,'');
103
+ };
104
+ }
105
+ ")
106
+ end
107
+
108
+ end
109
+ end
110
+ end
@@ -0,0 +1,88 @@
1
+ begin
2
+ require 'v8' unless defined?(V8)
3
+ rescue LoadError => e
4
+ warn "[WARNING] Please install gem 'therubyracer' to use Less."
5
+ raise e
6
+ end
7
+
8
+ require 'pathname'
9
+
10
+ module Less
11
+ module JavaScript
12
+ class V8Context
13
+
14
+ def self.instance
15
+ return new
16
+ end
17
+
18
+ def initialize(globals = nil)
19
+ lock do
20
+ @v8_context = V8::Context.new
21
+ globals.each { |key, val| @v8_context[key] = val } if globals
22
+ end
23
+ end
24
+
25
+ def unwrap
26
+ @v8_context
27
+ end
28
+
29
+ def exec(&block)
30
+ lock(&block)
31
+ end
32
+
33
+ def eval(source, options = nil) # passing options not supported
34
+ source = source.encode('UTF-8') if source.respond_to?(:encode)
35
+
36
+ lock do
37
+ @v8_context.eval("(#{source})")
38
+ end
39
+ end
40
+
41
+ def call(properties, *args)
42
+ args.last.is_a?(::Hash) ? args.pop : nil # extract_options!
43
+
44
+ lock do
45
+ @v8_context.eval(properties).call(*args)
46
+ end
47
+ end
48
+
49
+ def method_missing(symbol, *args)
50
+ if @v8_context.respond_to?(symbol)
51
+ @v8_context.send(symbol, *args)
52
+ else
53
+ super
54
+ end
55
+ end
56
+
57
+ private
58
+
59
+ def lock(&block)
60
+ do_lock(&block)
61
+ rescue V8::JSError => e
62
+ if e.value["name"] == "SyntaxError" || e.in_javascript?
63
+ raise Less::ParseError.new(e)
64
+ else
65
+ raise Less::Error.new(e)
66
+ end
67
+ end
68
+
69
+ def do_lock
70
+ result, exception = nil, nil
71
+ V8::C::Locker() do
72
+ begin
73
+ result = yield
74
+ rescue Exception => e
75
+ exception = e
76
+ end
77
+ end
78
+
79
+ if exception
80
+ raise exception
81
+ else
82
+ result
83
+ end
84
+ end
85
+
86
+ end
87
+ end
88
+ end
@@ -0,0 +1,34 @@
1
+ module Less
2
+ module JavaScript
3
+
4
+ def self.default_context_wrapper
5
+ if defined?(JRUBY_VERSION)
6
+ require 'less/java_script/rhino_context'
7
+ RhinoContext
8
+ else
9
+ require 'less/java_script/v8_context'
10
+ V8Context
11
+ end
12
+ end
13
+
14
+ @@context_wrapper = nil
15
+
16
+ def self.context_wrapper
17
+ @@context_wrapper ||= default_context_wrapper
18
+ end
19
+
20
+ def self.context_wrapper=(klass)
21
+ @@context_wrapper = klass
22
+ end
23
+
24
+ # execute a block as JS
25
+ def self.exec(&block)
26
+ context_wrapper.instance.exec(&block)
27
+ end
28
+
29
+ def self.eval(source)
30
+ context_wrapper.instance.eval(source)
31
+ end
32
+
33
+ end
34
+ end
data/lib/less/loader.rb CHANGED
@@ -1,26 +1,29 @@
1
+ require 'pathname'
1
2
  require 'commonjs'
2
3
 
3
4
  module Less
4
5
  class Loader
5
- include CallJS
6
-
6
+
7
7
  attr_reader :environment
8
-
8
+
9
9
  def initialize
10
- @cxt = V8::Context.new
11
- @environment = CommonJS::Environment.new(@cxt, :path => Pathname(__FILE__).dirname.join('js','lib').to_s)
10
+ context_wrapper = Less::JavaScript.context_wrapper.instance
11
+ @context = context_wrapper.unwrap
12
+ @context['process'] = Process.new
13
+ @context['console'] = Console.new
14
+ path = Pathname(__FILE__).dirname.join('js', 'lib')
15
+ @environment = CommonJS::Environment.new(@context, :path => path.to_s)
12
16
  @environment.native('path', Path.new)
13
17
  @environment.native('util', Sys.new)
14
18
  @environment.native('fs', Fs.new)
15
-
16
- @cxt['process'] = Process.new
17
- @cxt['console'] = Console.new
18
19
  end
19
-
20
+
20
21
  def require(module_id)
21
22
  @environment.require(module_id)
22
23
  end
23
-
24
+
25
+ # stubbed JS modules required by less.js
26
+
24
27
  class Path
25
28
  def join(*components)
26
29
  File.join(*components)
@@ -34,7 +37,7 @@ module Less
34
37
  File.basename(path)
35
38
  end
36
39
  end
37
-
40
+
38
41
  class Sys
39
42
  def error(*errors)
40
43
  raise errors.join(' ')
@@ -49,7 +52,6 @@ module Less
49
52
  def readFile(path, encoding, callback)
50
53
  callback.call(nil, File.read(path))
51
54
  end
52
-
53
55
  end
54
56
 
55
57
  class Process
@@ -58,9 +60,10 @@ module Less
58
60
  end
59
61
 
60
62
  class Console
61
- def log(*msgs)
62
- puts msgs.join(',')
63
+ def self.log(*msgs)
64
+ puts msgs.join(', ')
63
65
  end
64
66
  end
67
+
65
68
  end
66
69
  end
data/lib/less/parser.rb CHANGED
@@ -1,45 +1,7 @@
1
-
2
1
  module Less
3
-
4
- # Utility for calling into the JavaScript runtime.
5
- module CallJS
6
-
7
- # @private
8
- # Wrap JavaScript invocations with uniform error handling
9
- #
10
- # @yield code to wrap
11
- def calljs
12
- lock do
13
- yield
14
- end
15
- rescue V8::JSError => e
16
- raise ParseError.new(e)
17
- end
18
-
19
- # @private
20
- # Ensure proper locking before entering the V8 API
21
- #
22
- # @yield code to wrap in lock
23
- def lock
24
- result, exception = nil, nil
25
- V8::C::Locker() do
26
- begin
27
- result = yield
28
- rescue Exception => e
29
- exception = e
30
- end
31
- end
32
- if exception
33
- raise exception
34
- else
35
- result
36
- end
37
- end
38
- end
39
-
2
+
40
3
  # Convert lesscss source into an abstract syntax Tree
41
4
  class Parser
42
- include CallJS
43
5
 
44
6
  # Construct and configure new Less::Parser
45
7
  #
@@ -51,62 +13,49 @@ module Less
51
13
  Less.defaults.merge(options).each do |k,v|
52
14
  stringy[k.to_s] = v.is_a?(Array) ? v.map(&:to_s) : v.to_s
53
15
  end
54
- lock do
55
- @parser = Less.Parser.new(stringy)
56
- end
16
+ @parser = Less::JavaScript.exec { Less['Parser'].new(stringy) }
57
17
  end
58
18
 
59
19
  # Convert `less` source into a abstract syntaxt tree
60
20
  # @param [String] less the source to parse
61
21
  # @return [Less::Tree] the parsed tree
62
22
  def parse(less)
63
- calljs do
64
- tree = nil
65
- @parser.parse(less, lambda {|*args|
66
- this, e, t = *args
67
- fail e.message unless e.nil?
68
- tree = t
23
+ error, tree = nil, nil
24
+ Less::JavaScript.exec do
25
+ @parser.parse(less, lambda { |*args| # (error, tree)
26
+ # v8 >= 0.10 passes this as first arg :
27
+ if args.size > 2
28
+ error, tree = args[-2], args[-1]
29
+ else
30
+ error, tree = *args
31
+ end
32
+ fail error.message unless error.nil?
69
33
  })
70
- Tree.new(tree) if tree
71
34
  end
72
- end
73
- end
74
-
75
- # Abstract LessCSS syntax tree Less. Mainly used to emit CSS
76
- class Tree
77
- include CallJS
78
- # Create a tree from a native javascript object.
79
- # @param [V8::Object] tree the native less.js tree
80
- def initialize(tree)
81
- @tree = tree
82
- end
83
-
84
- # Serialize this tree into CSS.
85
- # By default this will be in pretty-printed form.
86
- # @param [Hash] opts modifications to the output
87
- # @option opts [Boolean] :compress minify output instead of pretty-printing
88
- def to_css(options = {})
89
- calljs do
90
- @tree.toCSS(options)
35
+ Tree.new(tree) if tree
36
+ end
37
+
38
+ private
39
+
40
+ # Abstract LessCSS syntax tree Less. Mainly used to emit CSS
41
+ class Tree
42
+
43
+ # Create a tree from a native javascript object.
44
+ # @param [V8::Object] tree the native less.js tree
45
+ def initialize(tree)
46
+ @tree = tree
91
47
  end
92
- end
93
- end
94
-
95
- # Thrown whenever an error occurs parsing
96
- # and/or serializing less source. It is intended
97
- # to wrap a native V8::JSError
98
- class ParseError < StandardError
99
48
 
100
- # Copies over `error`'s message and backtrace
101
- # @param [V8::JSError] error native error
102
- def initialize(error)
103
- super(error.message)
104
- @backtrace = error.backtrace
105
- end
106
-
107
- # @return [Array] the backtrace frames
108
- def backtrace
109
- @backtrace
49
+ # Serialize this tree into CSS.
50
+ # By default this will be in pretty-printed form.
51
+ # @param [Hash] opts modifications to the output
52
+ # @option opts [Boolean] :compress minify output instead of pretty-printing
53
+ def to_css(options = {})
54
+ Less::JavaScript.exec { @tree.toCSS(options) }
55
+ end
56
+
110
57
  end
58
+
111
59
  end
60
+
112
61
  end
data/lib/less/version.rb CHANGED
@@ -1,3 +1,3 @@
1
1
  module Less
2
- VERSION = '2.1.0'
2
+ VERSION = '2.2.0'
3
3
  end
data/lib/less.rb CHANGED
@@ -1,18 +1,25 @@
1
- require 'v8'
2
- require 'pathname'
3
- require 'less/parser'
1
+
2
+ require 'less/defaults'
3
+ require 'less/errors'
4
4
  require 'less/loader'
5
+ require 'less/parser'
5
6
  require 'less/version'
6
- require 'less/defaults'
7
+ require 'less/java_script'
7
8
 
8
9
  module Less
9
10
  extend Less::Defaults
10
-
11
+
12
+ # NOTE: keep the @loader as less-rails depends on
13
+ # it as it overrides some less/tree.js functions!
11
14
  @loader = Less::Loader.new
12
15
  @less = @loader.require('less/index')
13
16
 
17
+ def self.[](name)
18
+ @less[name]
19
+ end
20
+
14
21
  def self.Parser
15
- @less['Parser']
22
+ self['Parser']
16
23
  end
17
24
 
18
25
  end
@@ -3,7 +3,11 @@ require 'spec_helper'
3
3
  describe Less::Parser do
4
4
 
5
5
  cwd = Pathname(__FILE__).dirname
6
-
6
+
7
+ it "instantiates" do
8
+ expect { Less::Parser.new }.should_not raise_error
9
+ end
10
+
7
11
  describe "simple usage" do
8
12
  it "parse less into a tree" do
9
13
  root = subject.parse(".class {width: 1+1}")
@@ -15,16 +19,12 @@ describe Less::Parser do
15
19
  end
16
20
  end
17
21
 
18
- it "throws a ParseError if the lesscss is bogus" do
19
- expect {subject.parse('{^)')}.should raise_error(Less::ParseError)
20
- end
21
-
22
22
  it "passes exceptions from the less compiler" do
23
- expect {subject.parse('body { color: @a; }').to_css}.should raise_error(Less::ParseError, /variable @a is undefined/)
23
+ expect { subject.parse('body { color: @a; }').to_css }.should raise_error(Less::ParseError, /variable @a is undefined/)
24
24
  end
25
25
 
26
26
  describe "when configured with multiple load paths" do
27
- subject {Less::Parser.new :paths => [cwd.join('one'), cwd.join('two'), cwd.join('faulty')]}
27
+ subject { Less::Parser.new :paths => [ cwd.join('one'), cwd.join('two'), cwd.join('faulty') ] }
28
28
 
29
29
  it "will load files from both paths" do
30
30
  subject.parse('@import "one.less";').to_css.gsub(/\n/,'').strip.should eql ".one { width: 1;}"
@@ -32,7 +32,7 @@ describe Less::Parser do
32
32
  end
33
33
 
34
34
  it "passes exceptions from less imported less files" do
35
- expect {subject.parse('@import "faulty.less";').to_css}.should raise_error(Less::ParseError, /variable @a is undefined/)
35
+ expect { subject.parse('@import "faulty.less";').to_css }.should raise_error(Less::ParseError, /variable @a is undefined/)
36
36
  end
37
37
 
38
38
  end
data/spec/spec_helper.rb CHANGED
@@ -1,3 +1,5 @@
1
- $:.unshift Pathname(__FILE__).dirname.join('..','lib')
1
+ require 'pathname'
2
2
 
3
- require 'less'
3
+ $:.unshift Pathname(__FILE__).dirname.join('..', 'lib').to_s
4
+
5
+ require 'less'
metadata CHANGED
@@ -1,7 +1,7 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: less
3
3
  version: !ruby/object:Gem::Version
4
- version: 2.1.0
4
+ version: 2.2.0
5
5
  prerelease:
6
6
  platform: ruby
7
7
  authors:
@@ -9,33 +9,22 @@ authors:
9
9
  autorequire:
10
10
  bindir: bin
11
11
  cert_chain: []
12
- date: 2012-03-29 00:00:00.000000000 Z
12
+ date: 2012-04-25 00:00:00.000000000 Z
13
13
  dependencies:
14
- - !ruby/object:Gem::Dependency
15
- name: therubyracer
16
- requirement: &2158173480 !ruby/object:Gem::Requirement
17
- none: false
18
- requirements:
19
- - - ~>
20
- - !ruby/object:Gem::Version
21
- version: 0.10.0
22
- type: :runtime
23
- prerelease: false
24
- version_requirements: *2158173480
25
14
  - !ruby/object:Gem::Dependency
26
15
  name: commonjs
27
- requirement: &2158172980 !ruby/object:Gem::Requirement
16
+ requirement: &2151889720 !ruby/object:Gem::Requirement
28
17
  none: false
29
18
  requirements:
30
19
  - - ~>
31
20
  - !ruby/object:Gem::Version
32
- version: 0.2.0
21
+ version: 0.2.6
33
22
  type: :runtime
34
23
  prerelease: false
35
- version_requirements: *2158172980
24
+ version_requirements: *2151889720
36
25
  - !ruby/object:Gem::Dependency
37
26
  name: rake
38
- requirement: &2158172600 !ruby/object:Gem::Requirement
27
+ requirement: &2151888660 !ruby/object:Gem::Requirement
39
28
  none: false
40
29
  requirements:
41
30
  - - ! '>='
@@ -43,10 +32,10 @@ dependencies:
43
32
  version: '0'
44
33
  type: :development
45
34
  prerelease: false
46
- version_requirements: *2158172600
35
+ version_requirements: *2151888660
47
36
  - !ruby/object:Gem::Dependency
48
37
  name: rspec
49
- requirement: &2158172000 !ruby/object:Gem::Requirement
38
+ requirement: &2151887500 !ruby/object:Gem::Requirement
50
39
  none: false
51
40
  requirements:
52
41
  - - ~>
@@ -54,7 +43,7 @@ dependencies:
54
43
  version: '2.0'
55
44
  type: :development
56
45
  prerelease: false
57
- version_requirements: *2158172000
46
+ version_requirements: *2151887500
58
47
  description: Invoke the Less CSS compiler from Ruby
59
48
  email:
60
49
  - cowboyd@thefrontside.net
@@ -73,6 +62,10 @@ files:
73
62
  - less.gemspec
74
63
  - lib/less.rb
75
64
  - lib/less/defaults.rb
65
+ - lib/less/errors.rb
66
+ - lib/less/java_script.rb
67
+ - lib/less/java_script/rhino_context.rb
68
+ - lib/less/java_script/v8_context.rb
76
69
  - lib/less/loader.rb
77
70
  - lib/less/parser.rb
78
71
  - lib/less/version.rb
@@ -356,7 +349,7 @@ required_ruby_version: !ruby/object:Gem::Requirement
356
349
  version: '0'
357
350
  segments:
358
351
  - 0
359
- hash: -105846769214785865
352
+ hash: 2145882749298948818
360
353
  required_rubygems_version: !ruby/object:Gem::Requirement
361
354
  none: false
362
355
  requirements:
@@ -365,7 +358,7 @@ required_rubygems_version: !ruby/object:Gem::Requirement
365
358
  version: '0'
366
359
  segments:
367
360
  - 0
368
- hash: -105846769214785865
361
+ hash: 2145882749298948818
369
362
  requirements: []
370
363
  rubyforge_project: less
371
364
  rubygems_version: 1.8.17