Skip to content

Commit 50b9abb

Browse files
committed
Merge pull request #7 from edward/allow-optional-params
Allow optional params
2 parents de5c023 + 4b4d324 commit 50b9abb

11 files changed

+182
-60
lines changed

Diff for: Rakefile

+4-14
Original file line numberDiff line numberDiff line change
@@ -4,22 +4,12 @@ rescue LoadError
44
puts 'Cannot load bundler/gem_tasks'
55
end
66

7-
task default: :prepare
7+
require 'tasks/libsass'
88

9-
task prepare: "ext/lib/libsass.so"
9+
task default: :test
1010

11-
file "ext/lib/libsass.so" do
12-
gem_dir = File.expand_path(File.dirname(__FILE__)) + "/"
13-
cd "ext/libsass"
14-
sh 'make lib/libsass.so LDFLAGS="-Wall -O2"'
15-
cd gem_dir
16-
end
17-
18-
task test: :prepare do
11+
desc "Run all tests"
12+
task test: 'libsass:compile' do
1913
$LOAD_PATH.unshift('lib', 'test')
2014
Dir.glob('./test/**/*_test.rb') { |f| require f }
2115
end
22-
23-
task :submodule do
24-
sh "git submodule update --init"
25-
end

Diff for: ext/Rakefile

+3
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,3 @@
1+
require_relative '../lib/tasks/libsass'
2+
3+
task default: 'libsass:compile'

Diff for: lib/sassc/functions_handler.rb

+48-13
Original file line numberDiff line numberDiff line change
@@ -10,25 +10,45 @@ def setup(native_options)
1010

1111
list = Native.make_function_list(Script.custom_functions.count)
1212

13-
functs = FunctionWrapper.extend(Script::Functions)
14-
functs.options = @options
13+
functions = FunctionWrapper.extend(Script::Functions)
14+
functions.options = @options
1515

1616
Script.custom_functions.each_with_index do |custom_function, i|
17-
@callbacks[custom_function] = FFI::Function.new(:pointer, [:pointer, :pointer]) do |s_args, cookie|
18-
length = Native.list_get_length(s_args)
17+
@callbacks[custom_function] = FFI::Function.new(:pointer, [:pointer, :pointer]) do |native_argument_list, cookie|
18+
native_argument_list_length = Native.list_get_length(native_argument_list)
19+
custom_function_arguments = []
20+
error_tag = nil
1921

20-
v = Native.list_get_value(s_args, 0)
21-
v = Native.string_get_value(v).dup
22+
(0...native_argument_list_length).each do |i|
23+
native_value = Native.list_get_value(native_argument_list, i)
2224

23-
s = Script::String.new(Script::String.unquote(v), Script::String.type(v))
25+
case value_tag = Native.value_get_tag(native_value)
26+
when :sass_null
27+
# no-op
28+
when :sass_string
29+
native_string = Native.string_get_value(native_value)
30+
argument = Script::String.new(Script::String.unquote(native_string), Script::String.type(native_string))
2431

25-
value = functs.send(custom_function, s)
32+
custom_function_arguments << argument
33+
else
34+
error_tag = error("Sass argument of type #{value_tag} unsupported")
35+
break
36+
end
37+
end
38+
39+
next error_tag if error_tag
2640

27-
if value
28-
value = Script::String.new(Script::String.unquote(value.to_s), value.type)
29-
value.to_native
30-
else
31-
Script::String.new("").to_native
41+
begin
42+
value = functions.send(custom_function, *custom_function_arguments)
43+
44+
if value
45+
value = Script::String.new(Script::String.unquote(value.to_s), value.type)
46+
value.to_native
47+
else
48+
Script::String.new("").to_native
49+
end
50+
rescue StandardError => exception
51+
error(exception.message)
3252
end
3353
end
3454

@@ -48,6 +68,21 @@ def setup(native_options)
4868

4969
private
5070

71+
def error(message)
72+
value = Native::SassValue.new
73+
value[:unknown] = Native::SassUnknown.new
74+
75+
error = Native::SassError.new
76+
error[:tag] = :sass_error
77+
78+
Native.error_set_message(error, Native.native_string(message))
79+
$stderr.puts "[SassC::FunctionsHandler] #{message}"
80+
81+
value[:unknown][:tag] = :sass_error
82+
value[:error] = error
83+
value.pointer
84+
end
85+
5186
class FunctionWrapper
5287
class << self
5388
attr_accessor :options

Diff for: lib/sassc/native.rb

+2-1
Original file line numberDiff line numberDiff line change
@@ -48,7 +48,8 @@ def self.return_string_array(ptr)
4848
end
4949

5050
def self.native_string(string)
51-
string += "\0"
51+
string = string.to_s
52+
string << "\0"
5253
data = Native::LibC.malloc(string.bytesize)
5354
data.write_string(string)
5455
data

Diff for: lib/sassc/native/native_functions_api.rb

+6
Original file line numberDiff line numberDiff line change
@@ -19,6 +19,7 @@ module Native
1919

2020
# ADDAPI enum Sass_Tag ADDCALL sass_value_get_tag (const union Sass_Value* v);
2121
attach_function :sass_value_get_tag, [:sass_value_ptr], SassTag
22+
attach_function :sass_value_is_null, [:sass_value_ptr], :bool
2223

2324
# ADDAPI const char* ADDCALL sass_string_get_value (const union Sass_Value* v);
2425
attach_function :sass_string_get_value, [:sass_value_ptr], :string
@@ -28,6 +29,11 @@ module Native
2829
attach_function :sass_list_get_length, [:sass_value_ptr], :size_t
2930
attach_function :sass_list_get_value, [:sass_value_ptr, :size_t], :sass_value_ptr
3031

32+
# ADDAPI char* ADDCALL sass_error_get_message (const union Sass_Value* v);
33+
# ADDAPI void ADDCALL sass_error_set_message (union Sass_Value* v, char* msg);
34+
attach_function :sass_error_get_message, [:sass_value_ptr], :string
35+
attach_function :sass_error_set_message, [:sass_value_ptr, :pointer], :void
36+
3137
# Getters for custom function descriptors
3238
# ADDAPI const char* ADDCALL sass_function_get_signature (Sass_C_Function_Callback fn);
3339
# ADDAPI Sass_C_Function ADDCALL sass_function_get_function (Sass_C_Function_Callback fn);

Diff for: lib/sassc/native/sass_value.rb

+2-2
Original file line numberDiff line numberDiff line change
@@ -71,12 +71,12 @@ class SassNull < FFI::Struct
7171

7272
class SassError < FFI::Struct
7373
layout :tag, SassTag,
74-
:messsage, :string
74+
:message, :string
7575
end
7676

7777
class SassWarning < FFI::Struct
7878
layout :tag, SassTag,
79-
:messsage, :string
79+
:message, :string
8080
end
8181

8282
class SassValue # < FFI::Union

Diff for: lib/sassc/script.rb

+2-3
Original file line numberDiff line numberDiff line change
@@ -8,10 +8,9 @@ def self.custom_functions
88

99
def self.formatted_function_name(function_name)
1010
params = Functions.instance_method(function_name).parameters
11-
params = params.select { |param| param[0] == :req }
12-
.map(&:first)
13-
.map { |p| "$#{p}" }
11+
params = params.map { |param_type, name| "$#{name}#{': null' if param_type == :opt}" }
1412
.join(", ")
13+
1514
"#{function_name}(#{params})"
1615
end
1716
end

Diff for: lib/sassc/script/string.rb

+2-2
Original file line numberDiff line numberDiff line change
@@ -66,12 +66,12 @@ def self.type(contents)
6666
def self.unquote(contents)
6767
s = contents.dup
6868

69-
case contents[0,1]
69+
case contents[0, 1]
7070
when "'", '"', '`'
7171
s[0] = ''
7272
end
7373

74-
case contents[-1,1]
74+
case contents[-1, 1]
7575
when "'", '"', '`'
7676
s[-1] = ''
7777
end

Diff for: lib/tasks/libsass.rb

+21
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,21 @@
1+
namespace :libsass do
2+
desc "Compile libsass"
3+
task compile: "ext/libsass/lib/libsass.so"
4+
5+
file "ext/libsass/.git" do
6+
sh "git submodule update --init"
7+
end
8+
9+
file "ext/libsass/lib/libsass.so" => "ext/libsass/.git" do
10+
libsass_path = ""
11+
if Dir.pwd.end_with?('/ext')
12+
libsass_path = "libsass"
13+
else
14+
libsass_path = "ext/libsass"
15+
end
16+
17+
cd libsass_path do
18+
sh 'make lib/libsass.so LDFLAGS="-Wall -O2"'
19+
end
20+
end
21+
end

Diff for: sassc.gemspec

+3-2
Original file line numberDiff line numberDiff line change
@@ -16,9 +16,10 @@ Gem::Specification.new do |spec|
1616
spec.files = `git ls-files -z`.split("\x0")
1717
spec.executables = spec.files.grep(%r{^bin/}) { |f| File.basename(f) }
1818
spec.test_files = spec.files.grep(%r{^(test|spec|features)/})
19-
spec.require_paths = ["lib", "ext"]
2019

21-
spec.extensions = ["Rakefile"]
20+
spec.require_paths = ["lib"]
21+
22+
spec.extensions = ["ext/Rakefile"]
2223

2324
spec.add_development_dependency "rake"
2425
spec.add_development_dependency "minitest", "~> 5.5.1"

Diff for: test/functions_test.rb

+89-23
Original file line numberDiff line numberDiff line change
@@ -1,34 +1,16 @@
11
require_relative "test_helper"
2+
require "stringio"
23

34
module SassC
45
class FunctionsTest < MiniTest::Test
56
include FixtureHelper
67

7-
SassString = Struct.new(:value, :type) do
8-
def to_s
9-
value
10-
end
8+
def setup
9+
@real_stderr, $stderr = $stderr, StringIO.new
1110
end
1211

13-
module Script::Functions
14-
def javascript_path(path)
15-
Script::String.new("/js/#{path.value}", :string)
16-
end
17-
18-
def no_return_path(path)
19-
nil
20-
end
21-
22-
def sass_return_path(path)
23-
return SassString.new("'#{path.value}'", :string)
24-
end
25-
26-
module Compass
27-
def stylesheet_path(path)
28-
Script::String.new("/css/#{path.value}", :identifier)
29-
end
30-
end
31-
include Compass
12+
def teardown
13+
$stderr = @real_stderr
3214
end
3315

3416
def test_functions_may_return_sass_string_type
@@ -70,5 +52,89 @@ def test_function_with_no_return_value
7052
url: url(); }
7153
EOS
7254
end
55+
56+
def test_function_with_optional_arguments
57+
engine = Engine.new("div {url: optional_arguments('first'); url: optional_arguments('second', 'qux')}")
58+
assert_equal <<-EOS, engine.render
59+
div {
60+
url: "first/bar";
61+
url: "second/qux"; }
62+
EOS
63+
end
64+
65+
def test_function_with_unsupported_tag
66+
engine = Engine.new("div {url: function_with_unsupported_tag(red);}")
67+
68+
exception = assert_raises(SassC::SyntaxError) do
69+
engine.render
70+
end
71+
72+
assert_equal "Error: error in C function function_with_unsupported_tag: Sass argument of type sass_color unsupported\n\n Backtrace:\n \tstdin:1, in function `function_with_unsupported_tag`\n \tstdin:1\n on line 1 of stdin\n>> div {url: function_with_unsupported_tag(red);}\n ----------^\n", exception.message
73+
74+
assert_equal "[SassC::FunctionsHandler] Sass argument of type sass_color unsupported", stderr_output
75+
end
76+
77+
def test_function_with_error
78+
engine = Engine.new("div {url: function_that_raises_errors();}")
79+
exception = assert_raises(SassC::SyntaxError) do
80+
engine.render
81+
end
82+
83+
assert_equal "Error: error in C function function_that_raises_errors: Intentional wrong thing happened somewhere inside the custom function
84+
85+
Backtrace:
86+
\tstdin:1, in function `function_that_raises_errors`
87+
\tstdin:1
88+
on line 1 of stdin
89+
>> div {url: function_that_raises_errors();}
90+
----------^
91+
", exception.message
92+
93+
assert_equal "[SassC::FunctionsHandler] Intentional wrong thing happened somewhere inside the custom function", stderr_output
94+
end
95+
96+
private
97+
98+
SassString = Struct.new(:value, :type) do
99+
def to_s
100+
value
101+
end
102+
end
103+
104+
module Script::Functions
105+
def javascript_path(path)
106+
Script::String.new("/js/#{path.value}", :string)
107+
end
108+
109+
def no_return_path(path)
110+
nil
111+
end
112+
113+
def sass_return_path(path)
114+
return SassString.new("'#{path.value}'", :string)
115+
end
116+
117+
def optional_arguments(path, optional = "bar")
118+
return SassString.new("#{path}/#{optional}", :string)
119+
end
120+
121+
def function_that_raises_errors()
122+
raise StandardError, "Intentional wrong thing happened somewhere inside the custom function"
123+
end
124+
125+
def function_with_unsupported_tag(color)
126+
end
127+
128+
module Compass
129+
def stylesheet_path(path)
130+
Script::String.new("/css/#{path.value}", :identifier)
131+
end
132+
end
133+
include Compass
134+
end
135+
136+
def stderr_output
137+
$stderr.string.gsub("\u0000\n", '')
138+
end
73139
end
74140
end

0 commit comments

Comments
 (0)