nov-stylus 1.0.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/lib/stylus.rb ADDED
@@ -0,0 +1,174 @@
1
+ require 'stylus/runtime'
2
+ require 'stylus/source'
3
+ require 'stylus/version'
4
+ require 'stylus/railtie' if defined?(::Rails)
5
+ ## Stylus
6
+ #
7
+ # `stylus` is a bridge between your Ruby code and the [Stylus](https://github.com/LearnBoost/stylus)
8
+ # library that runs on Node.js. It's aims to be a replacement for the
9
+ # [stylus_rails](https://github.com/lucasmazza/stylus_rails) gem and to support the Rails 3.1 asset pipeline
10
+ # (via [Tilt](https://github.com/rtomayko/tilt)) and other scenarios,
11
+ # backed by the [ExecJS](https://github.com/sstephenson/execjs) gem.
12
+ #
13
+ ### Usage
14
+ #
15
+ # To compile a `.styl` file or an arbitrary String to .CSS using stylus, just use the `compile` method.
16
+ #
17
+ # `Stylus.compile(File.new('application.styl'))`
18
+ #
19
+ # A hash of options for the stylus API is accepted.
20
+ #
21
+ # `Stylus.compile(File.read('application.styl'), :compress => true)`
22
+ #
23
+ module Stylus
24
+ extend Runtime
25
+ class << self
26
+ @@compress = false
27
+ @@debug = false
28
+ @@paths = []
29
+ @@imports = []
30
+ @@definitions = {}
31
+ @@plugins = {}
32
+
33
+ # Stores a list of plugins to import inside `Stylus`, with an optional hash.
34
+ def use(*options)
35
+ arguments = options.last.is_a?(Hash) ? options.pop : {}
36
+ options.each do |plugin|
37
+ @@plugins[plugin] = arguments
38
+ end
39
+ end
40
+ alias :plugin :use
41
+
42
+ # Stores a list of stylesheets to import on every compile process.
43
+ def import(*paths)
44
+ if paths.any?
45
+ @@imports = @@imports.concat(paths)
46
+ end
47
+ @@imports
48
+ end
49
+ alias :imports :import
50
+
51
+
52
+ # Stores a list of defined variables to create on every compile process.
53
+ def define(variable, value, options = {})
54
+ literal = true if options[:literal]
55
+ @@definitions[variable] = { value: value, literal: literal }
56
+ end
57
+
58
+ # Retrieves all the registered plugins.
59
+ def plugins
60
+ @@plugins
61
+ end
62
+
63
+ # Retrieves all the registered variables.
64
+ def definitions
65
+ @@definitions
66
+ end
67
+
68
+ # Returns the global load path `Array` for your stylesheets.
69
+ def paths
70
+ @@paths
71
+ end
72
+
73
+ # Replaces the global load path `Array` of paths.
74
+ def paths=(val)
75
+ @@paths = Array(val)
76
+ end
77
+
78
+ # Returns the `debug` flag used to set the `linenos` and `firebug` option for Stylus.
79
+ def debug
80
+ @@debug
81
+ end
82
+ alias :debug? :debug
83
+
84
+ # Marks the `nib` plugin to be loaded and included on every stylesheet.
85
+ def nib=(flag)
86
+ if flag
87
+ use :nib
88
+ import :nib
89
+ end
90
+ end
91
+
92
+ # Sets the `debug` flag.
93
+ def debug=(val)
94
+ @@debug = val
95
+ end
96
+
97
+ # Returns the global compress flag.
98
+ def compress
99
+ @@compress
100
+ end
101
+ alias :compress? :compress
102
+
103
+ # Sets the global flag for the `compress` option.
104
+ def compress=(val)
105
+ @@compress = val
106
+ end
107
+
108
+ # Compiles a given input - a plain String, `File` or some sort of IO object that
109
+ # responds to `read`.
110
+ # It accepts a hash of options that will be merged with the global configuration.
111
+ # If the source has a `path`, it will be expanded and used as the :filename option
112
+ # So the debug options can be used.
113
+ def compile(source, options = {})
114
+ if source.respond_to?(:path) && source.path
115
+ options[:filename] ||= File.expand_path(source.path)
116
+ end
117
+ source = source.read if source.respond_to?(:read)
118
+ options = merge_options(options)
119
+ exec('compile', source, options, plugins, imports, definitions)
120
+ end
121
+
122
+ # Converts back an input of plain CSS to the `Stylus` syntax. The source object can be
123
+ # a `File`, `StringIO`, `String` or anything that responds to `read`.
124
+ def convert(source)
125
+ source = source.read if source.respond_to?(:read)
126
+ exec('convert', source)
127
+ end
128
+
129
+ # Returns a `Hash` of the given `options` merged with the default configuration.
130
+ # It also concats the global load path with a given `:paths` option.
131
+ def merge_options(options)
132
+ filename = options[:filename]
133
+
134
+ _paths = options.delete(:paths)
135
+ options = defaults.merge(options)
136
+ options[:paths] = paths.concat(Array(_paths))
137
+ if filename
138
+ options = options.merge(debug_options)
139
+ end
140
+ options
141
+ end
142
+
143
+ # Returns the default `Hash` of options:
144
+ # the compress flag and the global load path.
145
+ def defaults
146
+ { compress: self.compress?, paths: self.paths }
147
+ end
148
+
149
+ # Returns a Hash with the debug options to pass to
150
+ # Stylus.
151
+ def debug_options
152
+ { linenos: self.debug?, firebug: self.debug? }
153
+ end
154
+
155
+ # Return the gem version alongside with the current `Stylus` version of your system.
156
+ def version
157
+ "Stylus - gem #{VERSION} library #{exec('version')}"
158
+ end
159
+
160
+ protected
161
+ def bundled_path
162
+ File.dirname(Stylus::Source.bundled_path)
163
+ end
164
+ end
165
+
166
+ # Exports the `.node_modules` folder on the working directory so npm can
167
+ # require modules installed locally.
168
+ ENV['NODE_PATH'] = [
169
+ File.expand_path('node_modules'),
170
+ File.expand_path('vendor/node_modules'),
171
+ bundled_path,
172
+ ENV['NODE_PATH']
173
+ ].join(File::PATH_SEPARATOR)
174
+ end
@@ -0,0 +1,11 @@
1
+ require 'spec_helper'
2
+ require 'rails/generators/stylus/assets/assets_generator'
3
+
4
+ describe Stylus::Generators::AssetsGenerator do
5
+ include Generators::TestCase
6
+ arguments %w(posts)
7
+
8
+ it 'generates a .styl file' do
9
+ expect(file('app/assets/stylesheets/posts.css.styl')).to exist
10
+ end
11
+ end
@@ -0,0 +1,12 @@
1
+ require 'spec_helper'
2
+ require 'rails/generators/rails/controller/controller_generator'
3
+ require 'rails/generators/stylus/assets/assets_generator'
4
+
5
+ describe Rails::Generators::ControllerGenerator do
6
+ include Generators::TestCase
7
+ arguments %w(posts --stylesheet-engine=stylus)
8
+
9
+ it 'generates a .styl file' do
10
+ expect(file('app/assets/stylesheets/posts.css.styl')).to exist
11
+ end
12
+ end
@@ -0,0 +1,17 @@
1
+ require 'spec_helper'
2
+ require 'rails/generators/rails/scaffold/scaffold_generator'
3
+ require 'rails/generators/stylus/assets/assets_generator'
4
+ require 'rails/generators/stylus/scaffold/scaffold_generator'
5
+
6
+ describe Rails::Generators::ScaffoldGenerator do
7
+ include Generators::TestCase
8
+ arguments %w(posts --stylesheet-engine=stylus --orm=false)
9
+
10
+ it 'generates the default scaffold stylesheet' do
11
+ expect(file('app/assets/stylesheets/scaffold.css')).to exist
12
+ end
13
+
14
+ it 'generates a named .styl file' do
15
+ expect(file('app/assets/stylesheets/posts.css.styl')).to exist
16
+ end
17
+ end
@@ -0,0 +1,67 @@
1
+ require 'spec_helper'
2
+
3
+ describe Stylus::ImportProcessor do
4
+ let(:env) do
5
+ Sprockets::Environment.new do |assets|
6
+ assets.append_path fixture_root
7
+ Stylus.setup(assets)
8
+ end
9
+ end
10
+
11
+ it 'adds an imported stylesheet as a dependency' do
12
+ asset = env['import']
13
+ dependencies = dependencies_on(asset)
14
+
15
+ expect(dependencies).to include(fixture_path('mixins/vendor'))
16
+ end
17
+
18
+ context 'nested dependencies' do
19
+ it 'walks the dependency chain of imported files' do
20
+ asset = env['nested_import']
21
+ dependencies = dependencies_on(asset)
22
+
23
+ expect(dependencies).to include(fixture_path('mixins/nested'))
24
+ expect(dependencies).to include(fixture_path('mixins/vendor'))
25
+ end
26
+
27
+ it "adds files referenced in a directory's index file" do
28
+ asset = env['indexed_nested_import']
29
+ dependencies = dependencies_on(asset)
30
+
31
+ expect(dependencies).to include(fixture_path('indexed/index'))
32
+ expect(dependencies).to include(fixture_path('indexed/first_dep'))
33
+ expect(dependencies).to include(fixture_path('indexed/second_dep'))
34
+ end
35
+
36
+ it 'walks dependency chains through indexes' do
37
+ asset = env['indexed_recursive_import']
38
+ dependencies = dependencies_on(asset)
39
+
40
+ expect(dependencies).to include(fixture_path('indexed_nested_import'))
41
+ expect(dependencies).to include(fixture_path('indexed/index'))
42
+ expect(dependencies).to include(fixture_path('indexed/first_dep'))
43
+ expect(dependencies).to include(fixture_path('indexed/second_dep'))
44
+ end
45
+ end
46
+
47
+ it 'does not process non-stylus files' do
48
+ source = '@import "nib"'
49
+ template = Stylus::ImportProcessor.new('stylesheet.scss') { source }
50
+ context = double
51
+
52
+ expect(context).to_not receive(:depend_on)
53
+ template.render(context)
54
+ end
55
+
56
+ it 'swallows errors from files outside the Sprockets paths' do
57
+ source = '@import "nib"'
58
+ template = Stylus::ImportProcessor.new { source }
59
+ sprockets = double
60
+ expect(sprockets).to receive(:resolve).twice.and_raise(::Sprockets::FileNotFound)
61
+ expect(template).to receive(:stylus_file?).and_return(true)
62
+
63
+ expect {
64
+ template.render(sprockets)
65
+ }.to_not raise_error
66
+ end
67
+ end
@@ -0,0 +1,46 @@
1
+ require 'spec_helper'
2
+
3
+ describe 'Rails integration' do
4
+ it "copies all folders from the Sprockets load path" do
5
+ app = create_app
6
+ Stylus.paths.should include fixture_root
7
+ Stylus.paths.should == app.assets.paths
8
+ end
9
+
10
+ it 'process .styl files with the asset pipeline' do
11
+ result = fixture(:simple).last
12
+
13
+ app = create_app
14
+ app.assets['simple'].to_s.should == result
15
+ end
16
+
17
+ it 'enables @import definitions' do
18
+ result = fixture(:import).last
19
+
20
+ app = create_app
21
+ app.assets['import'].to_s.should == result
22
+ end
23
+
24
+ it 'skips debug info by default' do
25
+ app = create_app
26
+ asset = app.assets['simple']
27
+ asset.to_s.should_not match(/line 1 : #{asset.pathname}/)
28
+ end
29
+
30
+ it 'provides debug info if required' do
31
+ app = create_app(:debug => true)
32
+ asset = app.assets['simple']
33
+ asset.to_s.should match(/line 1 : #{asset.pathname}/)
34
+ end
35
+
36
+ it 'compress the output if Rails is configured to compress them too' do
37
+ result = fixture(:compressed).last
38
+
39
+ app = create_app(:compress => true)
40
+ app.assets['compressed'].to_s.should == result
41
+ end
42
+
43
+ it 'loads the app normally even when the asset pipeline is disabled' do
44
+ pending "TODO: supress the sprockets-rails railtie to test this."
45
+ end
46
+ end
@@ -0,0 +1,11 @@
1
+ require 'spec_helper'
2
+
3
+ describe Stylus::Runtime do
4
+ it 'raises an error if the runtime is not available' do
5
+ allow(Stylus).to receive(:runtime) { double('An unavailable Runtime', available?: false) }
6
+
7
+ expect {
8
+ Stylus.version
9
+ }.to raise_error RuntimeError, %r[The Node.JS runtime is not available to Stylus.]
10
+ end
11
+ end
@@ -0,0 +1,30 @@
1
+ require 'coveralls'
2
+ Coveralls.wear!
3
+
4
+ require 'rails'
5
+ require 'rails/generators/test_case'
6
+
7
+ require 'active_support/railtie'
8
+ require 'action_controller/railtie'
9
+ require 'sprockets'
10
+ require 'sprockets/rails'
11
+ require 'stylus'
12
+
13
+ require 'active_support/core_ext/class/attribute_accessors'
14
+
15
+ require 'support/helpers'
16
+ require 'support/matchers'
17
+ require 'support/generators/test_case'
18
+
19
+ RSpec.configure do |config|
20
+ config.include Helpers
21
+
22
+ config.after :each do
23
+ Stylus.compress = false
24
+ Stylus.debug = false
25
+ Stylus.paths = []
26
+ Stylus.plugins.clear
27
+ Stylus.definitions.clear
28
+ Stylus.imports.clear
29
+ end
30
+ end
@@ -0,0 +1,36 @@
1
+ require 'spec_helper'
2
+
3
+ describe 'Sprockets setup' do
4
+ let(:env) do
5
+ Sprockets::Environment.new do |assets|
6
+ assets.append_path fixture_root
7
+ assets.append_path 'javascripts'
8
+ end
9
+ end
10
+
11
+ it 'register the default Tilt template' do
12
+ expect(env).to receive(:register_engine).with('.styl', Tilt::StylusTemplate)
13
+ Stylus.setup(env)
14
+ end
15
+
16
+ it 'register a Rails specific Tilt template' do
17
+ expect(env).to receive(:register_engine).with('.styl', Stylus::Rails::StylusTemplate)
18
+ Stylus.setup(env, rails: true)
19
+ end
20
+
21
+ it 'register the import processor' do
22
+ expect(env).to receive(:register_preprocessor).with('text/css', Stylus::ImportProcessor)
23
+ Stylus.setup(env)
24
+ end
25
+
26
+ it 'copies the asset paths' do
27
+ Stylus.setup(env)
28
+ expect(Stylus.paths).to eq(env.paths)
29
+ end
30
+
31
+ it 'configure the debug and compress flags' do
32
+ Stylus.setup(env, debug: true, compress: true)
33
+ expect(Stylus.debug).to be_true
34
+ expect(Stylus.compress).to be_true
35
+ end
36
+ end
@@ -0,0 +1,118 @@
1
+ require 'spec_helper'
2
+
3
+ describe Stylus do
4
+ it 'compiles the given source' do
5
+ input, output = fixture(:simple)
6
+ expect(Stylus.compile(input)).to eq(output)
7
+ end
8
+
9
+ it 'accepts an IO object' do
10
+ input, output = fixture(:simple)
11
+ input = StringIO.new(input)
12
+ expect(Stylus.compile(input)).to eq(output)
13
+ end
14
+
15
+ it 'compress the file when the "compress" flag is given' do
16
+ input, output = fixture(:compressed)
17
+ expect(Stylus.compile(input, compress: true)).to eq(output)
18
+ end
19
+
20
+ it 'handles the compress flag globally' do
21
+ Stylus.compress = true
22
+ input, output = fixture(:compressed)
23
+ expect(Stylus.compile(input)).to eq(output)
24
+ end
25
+
26
+ it 'imports the given paths' do
27
+ path = fixture_root
28
+ input, output = fixture(:import)
29
+ expect(Stylus.compile(input, paths: path)).to eq(output)
30
+ end
31
+
32
+ it 'handles the import paths globally' do
33
+ Stylus.paths << fixture_root
34
+ input, output = fixture(:import)
35
+ expect(Stylus.compile(input)).to eq(output)
36
+ end
37
+
38
+ it 'implicit imports the given paths' do
39
+ path = File.expand_path('mixins/vendor.styl', fixture_root)
40
+ input, output = fixture(:implicit)
41
+ Stylus.import path
42
+ expect(Stylus.compile(input)).to eq(output)
43
+ end
44
+
45
+ it 'outputs both gem and library version' do
46
+ expect(Stylus.version).to match(/Stylus - gem .+ library .+/)
47
+ end
48
+
49
+ it 'converts CSS to Stylus' do
50
+ stylus, css = fixture(:stylesheet)
51
+ expect(Stylus.convert(css)).to eq(stylus)
52
+ end
53
+
54
+ it 'stores the given plugins' do
55
+ Stylus.use :one, :two, argument: true
56
+ expect(Stylus).to have(2).plugins
57
+ end
58
+
59
+ it 'includes the given plugins' do
60
+ Stylus.use :nib
61
+ input, output = fixture(:plugin)
62
+ expect(Stylus.compile(input)).to eq(output)
63
+ end
64
+
65
+ it 'stores the define calls' do
66
+ Stylus.define "mystring", "test"
67
+ expect(Stylus).to have(1).definitions
68
+ end
69
+
70
+ it 'defines a global variable string' do
71
+ Stylus.define "mystring", "test"
72
+ input, output = fixture(:definition)
73
+ expect(Stylus.compile(input)).to match(/content: 'test'/)
74
+ end
75
+
76
+ it 'defines a global variable literal' do
77
+ Stylus.define "mystring", "red", :literal => true
78
+ input, output = fixture(:definition)
79
+ expect(Stylus.compile(input)).to match(/content: red/)
80
+ end
81
+
82
+ it 'includes and imports "nib" automatically' do
83
+ Stylus.nib = true
84
+ input, output = fixture(:nib)
85
+ expect(Stylus.compile(input)).to eq(output)
86
+ end
87
+
88
+ it 'share variables between imported stylesheets' do
89
+ input, output = fixture(:variables)
90
+ path = fixture_root
91
+
92
+ expect(Stylus.compile(input, paths: path)).to eq(output)
93
+ end
94
+
95
+ describe 'The debug flag' do
96
+ let(:path) { fixture_path(:debug) }
97
+ let(:fixture) { File.read(path) }
98
+ let(:file) { File.new(path) }
99
+
100
+ before { Stylus.debug = true }
101
+
102
+ it 'turns the "linenos" option on' do
103
+ expect(Stylus.compile(file)).to match(/line 1 : #{path}/)
104
+ end
105
+
106
+ it 'skips the "linenos" option if no filename is given' do
107
+ expect(Stylus.compile(fixture)).to_not match(/line 1 : #{path}/)
108
+ end
109
+
110
+ it 'turns the "firebug" option on' do
111
+ expect(Stylus.compile(file)).to match(/@media -stylus-debug-info/)
112
+ end
113
+
114
+ it 'skips the "firebug" option if no filename is given' do
115
+ expect(Stylus.compile(fixture)).to_not match(/@media -stylus-debug-info/)
116
+ end
117
+ end
118
+ end
@@ -0,0 +1,59 @@
1
+ # Extracted from generator_spec https://github.com/stevehodgkiss/generator_spec
2
+ # But `RSpec::Rails::RailsExampleGroup` loads a truckload of things from rails and rspec-rails
3
+ # That we don't need.
4
+
5
+ module Generators
6
+ module TestCase
7
+ extend ActiveSupport::Concern
8
+ include FileUtils
9
+
10
+ included do
11
+ cattr_accessor :test_case, :test_case_instance
12
+
13
+ self.test_case = Class.new(Rails::Generators::TestCase) do
14
+ def fake_test_case; end
15
+ def add_assertion; end
16
+ end
17
+ self.test_case_instance = self.test_case.new(:fake_test_case)
18
+ self.test_case.tests described_class
19
+
20
+ before do
21
+ prepare_destination
22
+ create_routes
23
+ run_generator
24
+ end
25
+
26
+ destination File.expand_path('../tmp', __FILE__)
27
+ end
28
+
29
+ module ClassMethods
30
+ def tests(klass)
31
+ self.test_case.generator_class = klass
32
+ end
33
+
34
+ def arguments(array)
35
+ self.test_case.default_arguments = array
36
+ end
37
+
38
+ def destination(path)
39
+ self.test_case.destination_root = path
40
+ end
41
+ end
42
+
43
+ def file(relative)
44
+ File.expand_path(relative, destination_root)
45
+ end
46
+
47
+ def method_missing(method_sym, *arguments, &block)
48
+ self.test_case_instance.send(method_sym, *arguments, &block)
49
+ end
50
+
51
+ def respond_to?(method_sym, include_private = false)
52
+ if self.test_case_instance.respond_to?(method_sym)
53
+ true
54
+ else
55
+ super
56
+ end
57
+ end
58
+ end
59
+ end
@@ -0,0 +1,63 @@
1
+ module Helpers
2
+ def create_routes
3
+ destination = File.join(destination_root, 'config')
4
+
5
+ FileUtils.mkdir_p(destination)
6
+ FileUtils.touch File.join(destination, 'routes.rb')
7
+ end
8
+
9
+ def create_app(options = {})
10
+ Rails.application = nil
11
+
12
+ Class.new(Rails::Application).tap do |app|
13
+ config = app.config.assets
14
+ assets = app.assets
15
+ config.paths = []
16
+
17
+ assets.cache = ActiveSupport::Cache.lookup_store(:null_store)
18
+ config.compress = options[:compress]
19
+ config.debug = options[:debug]
20
+ config.paths << fixture_root
21
+ config.paths << File.expand_path('javascripts')
22
+ yield(app) if block_given?
23
+
24
+ app.config.eager_load = false
25
+ app.config.active_support.deprecation = :log
26
+ app.initialize!
27
+ end
28
+ end
29
+
30
+ def fixture_root
31
+ File.expand_path('../../stylesheets', __FILE__)
32
+ end
33
+
34
+ def images_root
35
+ File.expand_path('../../images', __FILE__)
36
+ end
37
+
38
+ def output_root
39
+ File.expand_path('../../cases', __FILE__)
40
+ end
41
+
42
+ def fixture(name)
43
+ source = fixture_path(name)
44
+ output = css_path(name)
45
+ [source, output].map do |path|
46
+ File.read(path) if File.file?(path)
47
+ end
48
+ end
49
+
50
+ def css_path(name)
51
+ File.join(output_root, "#{name}.css")
52
+ end
53
+
54
+ def fixture_path(name)
55
+ File.join(fixture_root, "#{name}.styl")
56
+ end
57
+
58
+ def dependencies_on(asset)
59
+ context = env.context_class.new(env, asset.logical_path, asset.pathname)
60
+ context.evaluate(asset.pathname)
61
+ context._dependency_paths
62
+ end
63
+ end
@@ -0,0 +1,5 @@
1
+ RSpec::Matchers.define :exist do
2
+ match do |file_path|
3
+ File.exists?(file_path)
4
+ end
5
+ end