epuber-stylus 1.1.0

Sign up to get free protection for your applications and to get access to all the features.
Files changed (36) hide show
  1. checksums.yaml +7 -0
  2. data/CHANGELOG.md +10 -0
  3. data/LICENSE +22 -0
  4. data/README.md +154 -0
  5. data/lib/epuber-stylus.rb +174 -0
  6. data/lib/epuber-stylus/import_processor.rb +71 -0
  7. data/lib/epuber-stylus/railtie.rb +32 -0
  8. data/lib/epuber-stylus/runtime.rb +54 -0
  9. data/lib/epuber-stylus/runtime/compiler.js +40 -0
  10. data/lib/epuber-stylus/runtime/runner.js +20 -0
  11. data/lib/epuber-stylus/sprockets.rb +58 -0
  12. data/lib/epuber-stylus/tilt.rb +2 -0
  13. data/lib/epuber-stylus/tilt/rails.rb +51 -0
  14. data/lib/epuber-stylus/tilt/stylus.rb +52 -0
  15. data/lib/epuber-stylus/version.rb +3 -0
  16. data/lib/rails/generators/epuber-stylus/assets/assets_generator.rb +13 -0
  17. data/lib/rails/generators/epuber-stylus/assets/templates/stylesheet.css.styl +3 -0
  18. data/lib/rails/generators/epuber-stylus/scaffold/scaffold_generator.rb +11 -0
  19. data/spec/generators/assets_generator_spec.rb +11 -0
  20. data/spec/generators/controller_generator_spec.rb +12 -0
  21. data/spec/generators/scaffold_generator_spec.rb +17 -0
  22. data/spec/import_processor_spec.rb +67 -0
  23. data/spec/rails_spec.rb +46 -0
  24. data/spec/runtime_spec.rb +11 -0
  25. data/spec/spec_helper.rb +30 -0
  26. data/spec/sprockets_spec.rb +36 -0
  27. data/spec/stylus_spec.rb +118 -0
  28. data/spec/support/generators/test_case.rb +59 -0
  29. data/spec/support/generators/tmp/app/controllers/posts_controller.rb +58 -0
  30. data/spec/support/generators/tmp/app/helpers/posts_helper.rb +2 -0
  31. data/spec/support/generators/tmp/config/routes.rb +0 -0
  32. data/spec/support/helpers.rb +63 -0
  33. data/spec/support/matchers.rb +5 -0
  34. data/spec/tilt/rails_spec.rb +64 -0
  35. data/spec/tilt/stylus_spec.rb +24 -0
  36. metadata +137 -0
checksums.yaml ADDED
@@ -0,0 +1,7 @@
1
+ ---
2
+ SHA1:
3
+ metadata.gz: e4fe8b935a89a5c33ed76565781f7ee79447753a
4
+ data.tar.gz: 61bdd3e96d0aa491d0bb07dd0333e89fcfa991f1
5
+ SHA512:
6
+ metadata.gz: dd5c1ab079de3c7ab1ce14806520ef2c82cb91352cd20b5b6972631e10f73e4fcf9a0dd205f6a4b6b48fbfc17e8293939521b92422094e0a0d570d3c37c888fe
7
+ data.tar.gz: 13ada496cfb38974a8cca74302fa8d093ca25796a581c893ca6ceb4296f98dcec531d62555cedf2dafc98c46a0215fad4f452cdcbfd1ac94bdc5f7cb7e380df6
data/CHANGELOG.md ADDED
@@ -0,0 +1,10 @@
1
+ ### 1.0.1
2
+
3
+ * Prevent exceptions from files without extensions.
4
+
5
+ ### 1.0.0
6
+
7
+ * Rails 4+ and Ruby 1.9.x/2.0.0 support.
8
+ * Support for the `asset-path` and `asset-url` mixins, thanks to [@spilin](https://github.com/spilin).
9
+
10
+ Please check [0-7-stable](https://github.com/lucasmazza/ruby-stylus/blob/0-7-stable/CHANGELOG.md) for previous changes.
data/LICENSE ADDED
@@ -0,0 +1,22 @@
1
+ (The MIT License)
2
+
3
+ Copyright (c) 2012-2015 Lucas Mazza; 2015 Forge Software, LLC.
4
+
5
+ Permission is hereby granted, free of charge, to any person obtaining
6
+ a copy of this software and associated documentation files (the
7
+ 'Software'), to deal in the Software without restriction, including
8
+ without limitation the rights to use, copy, modify, merge, publish,
9
+ distribute, sublicense, and/or sell copies of the Software, and to
10
+ permit persons to whom the Software is furnished to do so, subject to
11
+ the following conditions:
12
+
13
+ The above copyright notice and this permission notice shall be
14
+ included in all copies or substantial portions of the Software.
15
+
16
+ THE SOFTWARE IS PROVIDED 'AS IS', WITHOUT WARRANTY OF ANY KIND,
17
+ EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
18
+ MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
19
+ IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY
20
+ CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT,
21
+ TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE
22
+ SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
data/README.md ADDED
@@ -0,0 +1,154 @@
1
+ # Ruby Stylus
2
+
3
+ [![Gem Version](https://badge.fury.io/rb/stylus.svg)](http://badge.fury.io/rb/stylus)
4
+ [![Build Status](https://travis-ci.org/forgecrafted/ruby-stylus.svg?branch=master)](https://travis-ci.org/forgecrafted/ruby-stylus)
5
+ [![Coverage Status](https://coveralls.io/repos/forgecrafted/ruby-stylus/badge.svg)](https://coveralls.io/r/forgecrafted/ruby-stylus)
6
+
7
+ `stylus` is a bridge between Ruby and the [Stylus](https://github.com/stylus/stylus) library that runs on [node.js](http://nodejs.org). It has support for Rails 4 applications. (if you are working with Rails 3, check the [0-7-stable](https://github.com/forgecrafted/ruby-stylus/tree/0-7-stable) branch.)
8
+
9
+ ## Installation
10
+
11
+ If you have a `Gemfile`:
12
+
13
+ ```
14
+ gem 'stylus'
15
+ ```
16
+
17
+ or install it on your system:
18
+
19
+ ```
20
+ gem install stylus
21
+ ```
22
+
23
+ The [ruby-stylus-source](https://github.com/forgecrafted/ruby-stylus-source) packages the Stylus source into a gem, and is installed as a dependency of this gem. Versions of `ruby-stylus-source` follow Stylus releases and their versions.
24
+
25
+ You can manually replace the Stylus code by placing another version of Stylus on `./node_modules/stylus`, and it will be used instead of the version bundled inside the gem.
26
+
27
+ **REMEMBER**, you still need the `node` command available on your runtime for this gem to work. This gem is also compatible with the Heroku Cedar stack, enabling asset compilation during the deployment of your apps. You can check the [Node.js wiki](https://github.com/joyent/node/wiki/Quick-and-easy-installation) for more info.
28
+
29
+ ## Usage
30
+
31
+ The interaction is done by the `Stylus` module. You can compile Stylus syntax to CSS, convert it back, enable plugins and tweak some other options:
32
+
33
+ ```ruby
34
+ require 'stylus'
35
+
36
+ # Accepts a raw string or an IO object (File, StringIO or anything that responds to 'read').
37
+ Stylus.compile(File.new('application.styl')) # returns the compiled stylesheet.
38
+
39
+ # Use the :compress option, removing most newlines from the code.
40
+ Stylus.compile(File.read('application.styl'), compress: true)
41
+
42
+ # Or use the global compress flag
43
+ Stylus.compress = true
44
+ Stylus.compile(File.read('application.styl'))
45
+
46
+ # Convert old and boring CSS to awesome Stylus.
47
+ Stylus.convert(File.new('file.css'))
48
+
49
+ # Import plugins directly from Node.js, like nib.
50
+ Stylus.use :nib
51
+
52
+ # Enable debug info, which sends the 'linenos' and 'firebug' options to Stylus.
53
+ # If you provide a raw content String to the `Stylus.compile` method, remember to send
54
+ # a `:filename` option so Stylus can locate your stylesheet for proper inspection.
55
+ Stylus.debug = true
56
+ ```
57
+
58
+ ### With Rails and the Asset Pipeline.
59
+
60
+ Adding `stylus` to your Gemfile should let you work with `.styl` files with the Rails 3.1 Pipeline. Any asset generated with `rails generate` will be created with a `.css.styl` extension.
61
+
62
+ Any `@import` directive will add the stylesheet as a sprockets dependency, so you can update external libraries and it will reflect on your assets fingerprints. Also, the Sprockets load path (usually `app/assets`, `lib/assets`, `vendor/assets` and the `assets` folder inside any other gem) will be available to your stylesheets.
63
+
64
+ If the `config.assets.debug` is turned on, Stylus will emit extra comments on your stylesheets to help debugging and inspection using the `linenos` and `firebug` options. Check the [FireStylus extension for Firebug](https://github.com/stylus/stylus/blob/master/docs/firebug.md) for more info.
65
+
66
+ ### `@import` and file extensions.
67
+
68
+ Stylus and Sprockets file lookups differ on the subject of handling file extensions, and that may hurt a bit.
69
+
70
+ If you use Stylus `@import` to expose variables, mixins or just to concatenate code, you should use only the `.styl` extension on your imported files. If you use the `.css.styl` form (a convention from Sprockets), Stylus will treat it as a plain CSS file since it has `.css` on its name.
71
+
72
+ ```sass
73
+ // imports mixins.styl
74
+ @import 'mixins'
75
+ ```
76
+
77
+ ### `@import` dependency resolution
78
+
79
+ Because of how sprockets handles dependency resolution for computing file changes and expiring caches, it is necessary to specify the full import path in your import statements.
80
+
81
+ That is, given:
82
+
83
+ ```
84
+ app/assets/stylesheets/
85
+ app/assets/stylesheets/file.styl
86
+ app/assets/stylesheets/some_directory/other_file.styl
87
+ app/assets/stylesheets/some_directory/another_file.styl
88
+ ```
89
+
90
+ Imports should be specified with the full path relative to `app/assets/stylesheets` regardless of where the file calling the import is. In this example we use the `app/assets` directory, but this also applies to `vendor/assets` and `lib/assets`.
91
+
92
+ ```ruby
93
+ # app/assets/stylesheets/file.styl
94
+ @import "some_directory/other_file.styl"
95
+
96
+ # app/assets/stylesheets/some_directory/other_file.styl
97
+ @import "some_directory/another_file.styl"
98
+ ```
99
+
100
+ This will ensure that all changes get reflected when any of the imported
101
+ files change. If you don't do this, sprockets will not accurately be
102
+ able to keep track of your dependencies.
103
+
104
+ ### Standalone Sprockets usage
105
+
106
+ If you're using Sprockets outside Rails, on Sinatra or on a plain Rack app, you can wire up Stylus inside a instance of `Sprockets::Environment` with the `Stylus.setup` method.
107
+
108
+ An example of serving stylesheets from `./stylesheets` using just Sprockets and Rack.
109
+
110
+ ```ruby
111
+ require 'sprockets'
112
+ require 'stylus/sprockets'
113
+
114
+ # Serve your stylesheets living on ./stylesheets
115
+ assets = Sprockets::Environment.new
116
+ assets.append_path('stylesheets')
117
+
118
+ Stylus.setup(assets)
119
+
120
+ # Run the Sprockets with Rack
121
+ map('/assets') { run assets.index }
122
+ ```
123
+
124
+ ## Plugins
125
+
126
+ [Stylus](https://github.com/stylus/stylus) exposes a nice API to create plugins written on [node.js](http://nodejs.org), like [nib](https://github.com/visionmedia/nib). The installation process should be the same as described above for [Stylus](https://github.com/stylus/stylus) (since they're all npm packages after all). You can hook them up on your Ruby code with `Stylus.use`:
127
+
128
+ ```ruby
129
+ Stylus.use :fingerprint, literal: 'caa8c262e23268d2a7062c6217202343b84f472b'
130
+ ```
131
+
132
+ Will run something like this in JavaScript:
133
+
134
+ ```javascript
135
+ stylus(file).use(fingerprint({literal:'caa8c262e23268d2a7062c6217202343b84f472b'}));
136
+ ```
137
+
138
+ ## Questions, Bugs or Support
139
+
140
+ [Drop us a line in the issues section](https://github.com/forgecrafted/ruby-stylus/issues).
141
+
142
+ **Be sure to include sample code that reproduces the problem.**
143
+
144
+ For more info about Stylus syntax and its features, you can check the [project repository](https://github.com/stylus/stylus), and the docs on the [GitHub page](http://stylus.github.io/stylus/).
145
+
146
+ ## Changelog
147
+
148
+ [Available here.](https://github.com/forgecrafted/ruby-stylus/blob/master/CHANGELOG.md)
149
+
150
+ ## License
151
+
152
+ Copyright (c) 2012-2015 Lucas Mazza; 2015 Forge Software, LLC.
153
+
154
+ This is free software, and may be redistributed under the terms specified in the LICENSE file.
@@ -0,0 +1,174 @@
1
+ require 'epuber-stylus/runtime'
2
+ require 'stylus/source'
3
+ require 'epuber-stylus/version'
4
+ require 'epuber-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,71 @@
1
+ # Based on the ImportProcessor from the less-rails gem, by @metaskills
2
+ module Stylus
3
+ # Internal: A Tilt template that tracks down '@import' declarations
4
+ # and marks them as a dependency on the current asset.
5
+ #
6
+ # Example
7
+ #
8
+ # @import 'dashboard'
9
+ # # => A '//= depend_on dashboard' directive can be ommited from the stylesheet.
10
+ class ImportProcessor < Tilt::Template
11
+
12
+ IMPORT_SCANNER = /@import\s*['"]([^'"]+)['"]\s*/.freeze
13
+
14
+ # Internal: Tilt default interface requirement.
15
+ #
16
+ # Returns nothing.
17
+ def prepare
18
+ end
19
+
20
+ # Public: Scans the current stylesheet to track down Stylus
21
+ # '@import' calls as dependencies.
22
+ #
23
+ # Returns the stylesheet content, unmodified.
24
+ def evaluate(context, locals, &block)
25
+ return data unless stylus_file?(context)
26
+
27
+ depend_on(context, data)
28
+ data
29
+ end
30
+
31
+ private
32
+
33
+ # Internal: Returns true if the file being processed counts as a
34
+ # Stylus file (That is, it has a .styl extension).
35
+ def stylus_file?(context)
36
+ File.fnmatch('*.styl', file) || File.fnmatch('*.styl.*', file)
37
+ end
38
+
39
+ # Internal: Scan the stylesheet body, looking for '@import' calls to
40
+ # declare them as a dependency on the context using 'depend_on' method.
41
+ # This method will recursively scans all dependency to ensure that
42
+ # the current stylesheet tracks down nested dependencies.
43
+ def depend_on(context, data)
44
+ dependencies = data.scan(IMPORT_SCANNER).flatten.compact.uniq
45
+
46
+ dependencies.each do |path|
47
+ asset = resolve(context, path)
48
+
49
+ if asset
50
+ context.depend_on(asset)
51
+ depend_on(context, File.read(asset))
52
+ end
53
+ end
54
+ end
55
+
56
+ # Internal: Resolves the given 'path' with the Sprockets context, but
57
+ # avoids 'Sprockets::FileNotFound' exceptions since we might be importing
58
+ # files outside the Sprockets load path - like "nib".
59
+ #
60
+ # Returns the resolved 'Asset' or nil if it can't be found.
61
+ def resolve(context, path)
62
+ context.resolve(path, content_type: 'text/css')
63
+ rescue ::Sprockets::FileNotFound
64
+ begin
65
+ context.resolve(path + '/index', content_type: 'text/css')
66
+ rescue
67
+ nil
68
+ end
69
+ end
70
+ end
71
+ end
@@ -0,0 +1,32 @@
1
+ require 'epuber-stylus'
2
+ require 'epuber-stylus/sprockets'
3
+
4
+ module Stylus
5
+ # Internal: The Railtie responsible for integrate the Stylus library with
6
+ # a Rails application.
7
+ #
8
+ # Some of the customizations of using Stylus alongside Rails:
9
+ #
10
+ # * The application will use the Stylus stylesheet engine;
11
+ # * The Sprockets instance (present at Rails.application.assets) will be configured
12
+ # with the Stylus templates and the configurations defined at 'config.assets' will
13
+ # affect how Stylus compile your stylesheets;
14
+ # * The assets load path - the folders living on app/assets/stylesheets,
15
+ # lib/assets/stylesheets and vendor/assets/stylesheets on your app - will be
16
+ # added to the Stylus path registry.
17
+ class Railtie < ::Rails::Railtie
18
+
19
+ # Internal: Set the Stylesheet engine to 'stylus', so the generator constants
20
+ # will be loaded from the `Stylus::Generators` namespace.
21
+ config.app_generators.stylesheet_engine :stylus
22
+
23
+ # Internal: Initializer block that uses the Stylus.setup utility to configure
24
+ # both Stylus and the Sprockets instance that lives on 'app.assets'. The entire
25
+ # configuration object will be passed along.
26
+ #
27
+ # Returns nothing.
28
+ config.assets.configure do |env|
29
+ Stylus.setup(env, config.assets.merge(rails: true))
30
+ end
31
+ end
32
+ end