stylist 0.1.0
Sign up to get free protection for your applications and to get access to all the features.
- data/.document +5 -0
- data/.gitignore +12 -0
- data/LICENSE +20 -0
- data/README.md +236 -0
- data/Rakefile +82 -0
- data/init.rb +1 -0
- data/lib/stylist/configuration.rb +56 -0
- data/lib/stylist/controller_mixin.rb +19 -0
- data/lib/stylist/processor.rb +35 -0
- data/lib/stylist/processors/less_processor.rb +42 -0
- data/lib/stylist/processors/sass_processor.rb +41 -0
- data/lib/stylist/processors/yui_compressor_processor.rb +62 -0
- data/lib/stylist/railtie.rb +17 -0
- data/lib/stylist/stylesheet_collection.rb +92 -0
- data/lib/stylist/version.rb +9 -0
- data/lib/stylist/view_helpers.rb +14 -0
- data/lib/stylist.rb +33 -0
- data/rails/init.rb +4 -0
- data/spec/configuration_spec.rb +16 -0
- data/spec/controllers/controller_mixin_spec.rb +22 -0
- data/spec/controllers/view_helpers_spec.rb +73 -0
- data/spec/fixtures/public/stylesheets/base.css +0 -0
- data/spec/fixtures/public/stylesheets/home.css +0 -0
- data/spec/fixtures/public/stylesheets/layout.css +0 -0
- data/spec/fixtures/public/stylesheets/typography.css +0 -0
- data/spec/spec.opts +1 -0
- data/spec/spec_helper.rb +37 -0
- data/spec/stylesheet_collection_spec.rb +123 -0
- data/spec/support/application_controller.rb +3 -0
- data/spec/support/boot_rails2.rb +8 -0
- data/spec/support/boot_rails3.rb +7 -0
- data/stylist.gemspec +85 -0
- metadata +127 -0
data/.document
ADDED
data/.gitignore
ADDED
data/LICENSE
ADDED
@@ -0,0 +1,20 @@
|
|
1
|
+
Copyright (c) 2010 Tisho Georgiev
|
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,236 @@
|
|
1
|
+
Stylist
|
2
|
+
=======
|
3
|
+
|
4
|
+
|
5
|
+
____ __ ___ __
|
6
|
+
/\ _`\ /\ \__ /\_ \ __ /\ \
|
7
|
+
\ \,\L\_\ \ ,_\ __ __\//\ \ /\_\ ____\ \ \___
|
8
|
+
\/_\__ \\ \ \/ /\ \/\ \ \ \ \ \/\ \ /',__\\ \ _ `\
|
9
|
+
/\ \L\ \ \ \_\ \ \_\ \ \_\ \_\ \ \/\__, `\\ \ \ \ \
|
10
|
+
\ `\____\ \__\\/`____ \/\____\\ \_\/\____/ \ \_\ \_\
|
11
|
+
\/_____/\/__/ `/___/> \/____/ \/_/\/___/ \/_/\/_/
|
12
|
+
/\___/
|
13
|
+
\/__/
|
14
|
+
|
15
|
+
|
16
|
+
_Stylist_ provides powerful stylesheet management for your Rails app. You can organize your .css files by media, add, remove or prepend stylesheets in the stylesheets stack from your controllers and views, and process them using [Less](http://lesscss.org) or [Sass](http://sass-lang.com/). And as if that wasn't awesome enough, you can even minify them using [YUI Compressor](http://developer.yahoo.com/yui/compressor) and bundle them into completely incomprehensible, but bandwidth-friendly mega-stylesheets.
|
17
|
+
|
18
|
+
Install
|
19
|
+
=======
|
20
|
+
|
21
|
+
Rails 2.x
|
22
|
+
-------
|
23
|
+
|
24
|
+
Put this in your `environment.rb` file:
|
25
|
+
|
26
|
+
config.gem 'stylist'
|
27
|
+
|
28
|
+
That's it. `rake gems:install` and you're done.
|
29
|
+
|
30
|
+
Rails 3
|
31
|
+
-------
|
32
|
+
|
33
|
+
Add this to your `Gemfile`:
|
34
|
+
|
35
|
+
gem 'stylist'
|
36
|
+
|
37
|
+
That's it. `bundle install` and you're good to go.
|
38
|
+
|
39
|
+
How It Works
|
40
|
+
=======
|
41
|
+
|
42
|
+
The inner-workings of Stylist are actually pretty simple. Stylist keeps track of stylesheets you intend to use on a given page by storing them in a `Hash` with media types as keys. When you call `render_stylesheets` in your views, it processes all stylesheets in the hash and passes the resulting file paths to Rails's `stylesheet_link_tag`. It takes advantage of Rails's asset caching features by bundling the stylesheets in files with names like `all-[unique hash of collection].css`.
|
43
|
+
|
44
|
+
Usage
|
45
|
+
=======
|
46
|
+
|
47
|
+
In order to use Stylist, you have to add this to the part of your layout file(s) responsible for `<link>` tag generation (where your `stylesheet_link_tag` used to be):
|
48
|
+
|
49
|
+
views/layouts/application.html.haml
|
50
|
+
-----------------------------------
|
51
|
+
|
52
|
+
%html
|
53
|
+
%head
|
54
|
+
%title My Awesome App
|
55
|
+
- css :base
|
56
|
+
= render_stylesheets
|
57
|
+
%body
|
58
|
+
= yield
|
59
|
+
|
60
|
+
This will make sure that the stylesheets stack always has the `:base` stylesheet expansion group loaded before everything else and will then render the necessary `<link>` tags. The above example is using [Haml](http://haml-lang.com), but fear not if you're using regular ERB templates. Just write `<%= render_stylesheets %>`, instead of `= render_stylesheets` and `<% css :base %>`, instead of `- css :base`.
|
61
|
+
|
62
|
+
After you've done that, you can start manipulating the stylesheet stack from whatever view and/or controller you like.
|
63
|
+
|
64
|
+
The `css` Helper
|
65
|
+
-------
|
66
|
+
|
67
|
+
The `css` helper is used to manipulate the current stylesheet stack. You can use it in both controllers and views.
|
68
|
+
|
69
|
+
Examples
|
70
|
+
------
|
71
|
+
|
72
|
+
These can be written either in your views inside `<% %>` tags, or directly in your controller actions or filters. Every method accepts a `:media => :type` argument to specify what stylesheet stack should be manipulated. When omitted, the default media stack is used.
|
73
|
+
|
74
|
+
* `css 'list_view', 'foo_details'`
|
75
|
+
Adds `list_view.css` and `foo_details.css` to the stack.
|
76
|
+
|
77
|
+
* `css.remove('grid').prepend('grid_iphone').append('mobile')`
|
78
|
+
Removes `grid.css`, prepends `grid_iphone.css` to the front of the stack and then appends `mobile.css` to the end.
|
79
|
+
|
80
|
+
* `css '-grid', 'grid_iphone+', 'mobile'`
|
81
|
+
The short, ninja version of the above example.
|
82
|
+
|
83
|
+
* `css :print, :media => :print`
|
84
|
+
Adds the stylesheets, registered in the `:print` stylesheet expansion group, to the print media stack.
|
85
|
+
|
86
|
+
Working with Less/Sass
|
87
|
+
-------
|
88
|
+
|
89
|
+
If you've used the special route/controller approach up til now, you can safely get rid of it. Stylist will automatically find any files in the stack that have `.less` or `.sass` sources, "compile" and cache them. If you decide to update the source files, just reload the page. Stylist will detect if you've made any changes to the original files and re-compile them.
|
90
|
+
|
91
|
+
Working with the YUI Compressor
|
92
|
+
-------
|
93
|
+
|
94
|
+
It's important to note that `:yui_compressor` should be _the last processor_ that your files should go through. That's because for every file it minifies, YUI Compressor generates a corresponding `filename-min.css` and then replaces the path in the original collection with the minified one. This is done to make sure you never overwrite your original files with minified ones. It also means that you probably don't want to have anything `-min.css` in your public stylesheets dir.
|
95
|
+
|
96
|
+
Configuration
|
97
|
+
=======
|
98
|
+
|
99
|
+
You can provide additional configuration options to _Stylist_ by using its `Stylist.configure` method. Say you want to process your stylesheets with _Less_. Put this in `config/initializers/stylist.rb`:
|
100
|
+
|
101
|
+
Stylist.configure do |config|
|
102
|
+
config.process_with :less
|
103
|
+
end
|
104
|
+
|
105
|
+
Global Configuration Options
|
106
|
+
------
|
107
|
+
|
108
|
+
`process_with`
|
109
|
+
-----
|
110
|
+
|
111
|
+
Accepts an array of processors that each file will pass through before being linked on the page. Currently, _Stylist_ has support for _Less_, _Sass_ and _YUI Compressor_ built in. In order to use them, pass either `:less`, `:sass`, `:yui_compressor` or a combination of them to `process_with`. For example, if you want to have your stylesheets processes with _Sass_, and then minified with _YUI Compressor_, you'd have the following config block:
|
112
|
+
|
113
|
+
Stylist.configure do |config|
|
114
|
+
config.process_with :sass, :yui_compressor
|
115
|
+
end
|
116
|
+
|
117
|
+
Additionally, you can pass your own processor classes to `process_with`. Just make sure they respond to a `process!` method. More on this later.
|
118
|
+
|
119
|
+
`default_media`
|
120
|
+
-----
|
121
|
+
|
122
|
+
This is where your stylesheets will end up when you don't specify which media type they should respond to. Defaults to `:all`
|
123
|
+
|
124
|
+
`public_stylesheets_path`
|
125
|
+
-----
|
126
|
+
|
127
|
+
This one is pretty self-explanatory. It's a `Pathname` instance that defaults to `your_app/public/stylesheets`.
|
128
|
+
|
129
|
+
Less-Specific Configuration Options
|
130
|
+
------
|
131
|
+
|
132
|
+
`less.source_path`
|
133
|
+
-----
|
134
|
+
|
135
|
+
This is where all your `.lss` or `.less` source files reside. Defaults to `your_app/app/stylesheets`.
|
136
|
+
|
137
|
+
`less.compress`
|
138
|
+
-----
|
139
|
+
|
140
|
+
When this options is set to `true`, the _Less_ processor will automatically strip all newlines in the resulting CSS file. On by default.
|
141
|
+
|
142
|
+
Sass-Specific Configuration Options
|
143
|
+
------
|
144
|
+
|
145
|
+
`sass.source_path`
|
146
|
+
-----
|
147
|
+
|
148
|
+
This is where all your `.sass` source files reside. Defaults to `your_app/app/stylesheets`.
|
149
|
+
|
150
|
+
`sass.compress`
|
151
|
+
-----
|
152
|
+
|
153
|
+
When this options is set to `true`, the _Sass_ processor will automatically strip all newlines in the resulting CSS file. On by default.
|
154
|
+
|
155
|
+
YUICompressor-Specific Configuration Options
|
156
|
+
------
|
157
|
+
|
158
|
+
`yui_compressor.path_to_jar`
|
159
|
+
-----
|
160
|
+
|
161
|
+
The path to your `yuicompressor-x.x.x.jar` file. Doesn't default to anything, so make sure you set it if you want to use the compressor.
|
162
|
+
|
163
|
+
`yui_compressor.path_to_java`
|
164
|
+
-----
|
165
|
+
|
166
|
+
The path to your `java` executable. Defaults to just `java` and expects to pick it up from your environment, but it's not anywhere in your `$PATH`, make sure you set this option.
|
167
|
+
|
168
|
+
`yui_compressor.charset`
|
169
|
+
-----
|
170
|
+
|
171
|
+
The character set used to read the stylesheet files. Defaults to `utf-8`.
|
172
|
+
|
173
|
+
`yui_compressor.line_break`
|
174
|
+
-----
|
175
|
+
|
176
|
+
The number of characters after which YUI Compressor will try to put a line break. Equivalent to the `--line-break` option when running YUI Compressor from the command line. `nil` by default (so no line breaks).
|
177
|
+
|
178
|
+
`yui_compressor.production_only`
|
179
|
+
-----
|
180
|
+
|
181
|
+
When set to `true` the _YUI Compressor_ processor will only attempt to minify a collection of CSS files when your app is running in a production environment. On by default.
|
182
|
+
|
183
|
+
Rolling Your Own Processor
|
184
|
+
=======
|
185
|
+
|
186
|
+
The basics (`lib/stylist/rainbows_and_unicorns_processor.rb`):
|
187
|
+
|
188
|
+
module Stylist
|
189
|
+
module Processors
|
190
|
+
|
191
|
+
class RainbowsAndUnicornsProcessor < Processor
|
192
|
+
class << self
|
193
|
+
|
194
|
+
# This method will be called automatically when you add your processor
|
195
|
+
# to the config.process_with array. It has access to the Stylist configuration
|
196
|
+
# object called 'configuration'.
|
197
|
+
|
198
|
+
def configure
|
199
|
+
configuration.add_option_group :rainbows_and_unicorns, { :unicorns => true, :rainbows => true }
|
200
|
+
end
|
201
|
+
|
202
|
+
# Called from 'render_stylesheets'. Passes the collection hash as an argument.
|
203
|
+
|
204
|
+
def process!(collection)
|
205
|
+
# TODO: Add unicorns and rainbows in every stylesheet
|
206
|
+
# ...
|
207
|
+
end
|
208
|
+
|
209
|
+
end
|
210
|
+
end
|
211
|
+
|
212
|
+
end
|
213
|
+
end
|
214
|
+
|
215
|
+
Then in your `initializers/stylist.rb`:
|
216
|
+
|
217
|
+
Stylist.configure do |config|
|
218
|
+
config.process_with :rainbows_and_unicorns
|
219
|
+
config.rainbows_and_unicorns.rainbows = false
|
220
|
+
end
|
221
|
+
|
222
|
+
If you choose to not make `RainbowsAndUnicornsProcessor` part of `Stylist::Processors`, make sure you pass it to `config.process_with` as a class, instead of a symbol. Stylist will automatically try to expand symbols and strings to `Stylist::Processors::SomeStringProcessor`.
|
223
|
+
|
224
|
+
The base `Stylist::Processors::Processor` class also lets you use an `expand_stylesheet_sources(*sources)` method that you can use to explode stylesheet expansions into a series of filepaths.
|
225
|
+
|
226
|
+
If you choose to create your own processor, I strongly advise you to look at one of the built-in ones for reference.
|
227
|
+
|
228
|
+
Credits
|
229
|
+
=======
|
230
|
+
|
231
|
+
Stylist was extracted with little modification from the loving embrace of the [TransFS](http://transfs.com) codebase, which to this day helps businesses quickly and easily compare the best credit card processors. For more open-source love from [TFS](htp://transfs.com), please take a look at [AppConfig](http://github.com/tisho/app_config) and [FunnelCake](http://github.com/jkrall/funnel_cake).
|
232
|
+
|
233
|
+
License
|
234
|
+
=======
|
235
|
+
|
236
|
+
Copyright (c) 2010 Tisho Georgiev, released under the MIT license
|
data/Rakefile
ADDED
@@ -0,0 +1,82 @@
|
|
1
|
+
require 'rubygems'
|
2
|
+
require 'rake'
|
3
|
+
require File.dirname(__FILE__) + "/lib/stylist/version.rb"
|
4
|
+
|
5
|
+
begin
|
6
|
+
require 'jeweler'
|
7
|
+
Jeweler::Tasks.new do |gem|
|
8
|
+
gem.name = "stylist"
|
9
|
+
gem.version = Stylist::VERSION::STRING
|
10
|
+
gem.summary = %Q{Powerful stylesheet management for your Rails app.}
|
11
|
+
gem.description = %Q{Stylist provides powerful stylesheet management for your Rails app. You can organize your CSS files by media, add, remove or prepend stylesheets in the stylesheets stack from your controllers and views, and process them using Less or Sass. And as if that wasn't awesome enough, you can even minify them using YUI Compressor and bundle them into completely incomprehensible, but bandwidth-friendly mega-stylesheets.}
|
12
|
+
gem.email = "tihomir.georgiev@gmail.com"
|
13
|
+
gem.homepage = "http://github.com/tisho/stylist"
|
14
|
+
gem.authors = ["Tisho Georgiev"]
|
15
|
+
gem.add_development_dependency "rspec", ">= 1.2.9"
|
16
|
+
gem.add_development_dependency "rails", ">= 2.3"
|
17
|
+
# gem is a Gem::Specification... see http://www.rubygems.org/read/chapter/20 for additional settings
|
18
|
+
end
|
19
|
+
Jeweler::GemcutterTasks.new
|
20
|
+
rescue LoadError
|
21
|
+
puts "Jeweler (or a dependency) not available. Install it with: gem install jeweler"
|
22
|
+
end
|
23
|
+
|
24
|
+
namespace :spec do
|
25
|
+
desc 'Run RSpec code examples within a Rails 2 environment.'
|
26
|
+
task :rails2 do
|
27
|
+
RakeFileUtils.verbose(verbose) do
|
28
|
+
spec_files = FileList['spec/**/*_spec.rb'].to_a
|
29
|
+
lib_path = ['lib', 'spec']
|
30
|
+
spec_script = Gem.bin_path('rspec', 'spec', '~> 1.2')
|
31
|
+
|
32
|
+
unless spec_files.empty?
|
33
|
+
cmd_parts = ['RAILS_VERSION=2', RUBY]
|
34
|
+
cmd_parts << '-Ilib'
|
35
|
+
cmd_parts << '-Ispec'
|
36
|
+
cmd_parts << %["#{spec_script}"]
|
37
|
+
cmd_parts += spec_files.collect { |fn| %["#{fn}"] }
|
38
|
+
cmd = cmd_parts.join(" ")
|
39
|
+
puts cmd if verbose
|
40
|
+
unless system(cmd)
|
41
|
+
raise("Command #{cmd} failed")
|
42
|
+
end
|
43
|
+
end
|
44
|
+
end
|
45
|
+
end
|
46
|
+
|
47
|
+
desc 'Run RSpec code examples within a Rails 3 environment.'
|
48
|
+
task :rails3 do
|
49
|
+
RAILS_VERSION = 3
|
50
|
+
RakeFileUtils.verbose(verbose) do
|
51
|
+
spec_files = FileList['spec/**/*_spec.rb'].to_a
|
52
|
+
lib_path = ['lib', 'spec']
|
53
|
+
|
54
|
+
unless spec_files.empty?
|
55
|
+
cmd_parts = ['RAILS_VERSION=3', RUBY]
|
56
|
+
cmd_parts << '-Ilib'
|
57
|
+
cmd_parts << '-Ispec'
|
58
|
+
cmd_parts += spec_files.collect { |fn| %["#{fn}"] }
|
59
|
+
cmd = cmd_parts.join(" ")
|
60
|
+
puts cmd if verbose
|
61
|
+
unless system(cmd)
|
62
|
+
raise("Command #{cmd} failed")
|
63
|
+
end
|
64
|
+
end
|
65
|
+
end
|
66
|
+
end
|
67
|
+
end
|
68
|
+
|
69
|
+
desc "Run RSpec code examples first in Rails 2, then in a Rails 3 environment."
|
70
|
+
task :spec => [:check_dependencies, 'spec:rails2', 'spec:rails3']
|
71
|
+
|
72
|
+
task :default => :spec
|
73
|
+
|
74
|
+
require 'rake/rdoctask'
|
75
|
+
Rake::RDocTask.new do |rdoc|
|
76
|
+
version = File.exist?('VERSION') ? File.read('VERSION') : ""
|
77
|
+
|
78
|
+
rdoc.rdoc_dir = 'rdoc'
|
79
|
+
rdoc.title = "stylist #{version}"
|
80
|
+
rdoc.rdoc_files.include('README*')
|
81
|
+
rdoc.rdoc_files.include('lib/**/*.rb')
|
82
|
+
end
|
data/init.rb
ADDED
@@ -0,0 +1 @@
|
|
1
|
+
require 'stylist'
|
@@ -0,0 +1,56 @@
|
|
1
|
+
module Stylist
|
2
|
+
class << self
|
3
|
+
attr_accessor :configuration
|
4
|
+
end
|
5
|
+
|
6
|
+
def self.configure
|
7
|
+
self.configuration ||= Configuration.new
|
8
|
+
yield(configuration)
|
9
|
+
end
|
10
|
+
|
11
|
+
class Configuration
|
12
|
+
attr_reader :processors
|
13
|
+
attr_accessor :public_stylesheets_path, :default_media
|
14
|
+
|
15
|
+
def initialize
|
16
|
+
@default_media = :all
|
17
|
+
@processors = []
|
18
|
+
@public_stylesheets_path = "#{defined?(Rails.public_path) ? Rails.public_path : 'public'}/stylesheets"
|
19
|
+
end
|
20
|
+
|
21
|
+
def metaclass
|
22
|
+
class << self; self; end
|
23
|
+
end
|
24
|
+
|
25
|
+
def add_option_group(group_name, options={})
|
26
|
+
group = instance_variable_set "@#{group_name}", Configuration.new
|
27
|
+
metaclass.class_eval <<-EOC
|
28
|
+
attr_reader :#{group_name}
|
29
|
+
EOC
|
30
|
+
|
31
|
+
options.each_pair { |key, value| group.add_option(key, value) }
|
32
|
+
end
|
33
|
+
|
34
|
+
def add_option(key, default_value=nil)
|
35
|
+
metaclass.class_eval <<-EOC
|
36
|
+
attr_accessor :#{key}
|
37
|
+
EOC
|
38
|
+
|
39
|
+
instance_variable_set "@#{key}", default_value
|
40
|
+
end
|
41
|
+
|
42
|
+
def process_with(*processors)
|
43
|
+
@processors = processors.collect do |processor|
|
44
|
+
if processor.is_a? Class
|
45
|
+
processor_class = processor
|
46
|
+
else
|
47
|
+
class_name = processor.to_s.strip.camelize.sub(/(Processor)?$/, 'Processor')
|
48
|
+
processor_class = class_name.constantize rescue "::Stylist::Processors::#{class_name}".constantize
|
49
|
+
end
|
50
|
+
end.compact
|
51
|
+
|
52
|
+
@processors.each { |processor| processor.configure if processor.respond_to?(:configure) }
|
53
|
+
end
|
54
|
+
|
55
|
+
end
|
56
|
+
end
|
@@ -0,0 +1,19 @@
|
|
1
|
+
module Stylist
|
2
|
+
module ControllerMixin
|
3
|
+
|
4
|
+
def self.included(base)
|
5
|
+
base.class_eval do
|
6
|
+
include InstanceMethods
|
7
|
+
before_filter { Stylist.stylesheets.reset! }
|
8
|
+
helper_method :css
|
9
|
+
end
|
10
|
+
end
|
11
|
+
|
12
|
+
module InstanceMethods
|
13
|
+
def css(*args)
|
14
|
+
Stylist.stylesheets.manipulate *args
|
15
|
+
end
|
16
|
+
end
|
17
|
+
|
18
|
+
end
|
19
|
+
end
|
@@ -0,0 +1,35 @@
|
|
1
|
+
module Stylist
|
2
|
+
module Processors
|
3
|
+
class Processor
|
4
|
+
|
5
|
+
class << self
|
6
|
+
def configuration; Stylist.configuration; end
|
7
|
+
|
8
|
+
def process!(collection)
|
9
|
+
# Provide your own implementation
|
10
|
+
end
|
11
|
+
|
12
|
+
def expand_stylesheet_sources(*sources)
|
13
|
+
expansions = defined?(ActionView::Helpers::AssetTagHelper) ? ActionView::Helpers::AssetTagHelper.send(:class_variable_get, :@@stylesheet_expansions) : {}
|
14
|
+
|
15
|
+
sources.flatten.collect do |source|
|
16
|
+
case source
|
17
|
+
when Symbol
|
18
|
+
expansions[source] || raise(ArgumentError, "No expansion found for #{source.inspect}")
|
19
|
+
else
|
20
|
+
source
|
21
|
+
end
|
22
|
+
end.flatten.collect do |source|
|
23
|
+
source_ext = File.extname(source)[1..-1]
|
24
|
+
if (source_ext.blank? || ('css' != source_ext))
|
25
|
+
source += ".css"
|
26
|
+
end
|
27
|
+
|
28
|
+
Rails.root.join(configuration.public_stylesheets_path, source)
|
29
|
+
end
|
30
|
+
end
|
31
|
+
end
|
32
|
+
|
33
|
+
end
|
34
|
+
end
|
35
|
+
end
|
@@ -0,0 +1,42 @@
|
|
1
|
+
require 'less' unless defined?(::Less)
|
2
|
+
|
3
|
+
module Stylist
|
4
|
+
module Processors
|
5
|
+
class LessProcessor < Processor
|
6
|
+
class << self
|
7
|
+
def configure
|
8
|
+
configuration.add_option_group :less, { :source_path => ::Rails.root.join('app/stylesheets'), :compress => true }
|
9
|
+
end
|
10
|
+
|
11
|
+
def process!(collection)
|
12
|
+
stylesheets_to_process(collection).each do |stylesheet|
|
13
|
+
source_path = source_path_for(stylesheet)
|
14
|
+
|
15
|
+
if source_path && source_path.exist?
|
16
|
+
engine = File.open(source_path) {|f| ::Less::Engine.new(f) }
|
17
|
+
css = engine.to_css
|
18
|
+
css.delete!("\n") if configuration.less.compress
|
19
|
+
File.open(stylesheet, "w") { |f| f.puts css }
|
20
|
+
end
|
21
|
+
end
|
22
|
+
end
|
23
|
+
|
24
|
+
def source_path_for(file)
|
25
|
+
Pathname.glob(File.join(configuration.less.source_path, File.basename(file, '.css')+'.{css,less,lss}'))[0]
|
26
|
+
end
|
27
|
+
|
28
|
+
def stylesheets_to_process(collection)
|
29
|
+
expand_stylesheet_sources(collection.values.flatten).select do |file|
|
30
|
+
(File.exists?(file) and source_has_been_changed?(file)) or not File.exists?(file)
|
31
|
+
end
|
32
|
+
end
|
33
|
+
|
34
|
+
def source_has_been_changed?(file)
|
35
|
+
source_path = source_path_for(file)
|
36
|
+
source_path && (File.new(source_path).mtime >= File.new(file).mtime)
|
37
|
+
end
|
38
|
+
end
|
39
|
+
|
40
|
+
end
|
41
|
+
end
|
42
|
+
end
|
@@ -0,0 +1,41 @@
|
|
1
|
+
module Stylist
|
2
|
+
module Processors
|
3
|
+
class SassProcessor < Processor
|
4
|
+
|
5
|
+
class << self
|
6
|
+
def configure
|
7
|
+
configuration.add_option_group :sass, { :source_path => ::Rails.root.join('app/stylesheets'), :compress => true }
|
8
|
+
end
|
9
|
+
|
10
|
+
def process!(collection)
|
11
|
+
stylesheets_to_process(collection).each do |stylesheet|
|
12
|
+
source_path = source_path_for(stylesheet)
|
13
|
+
if source_path && source_path.exist?
|
14
|
+
puts "Processing #{source_path}..."
|
15
|
+
engine = File.open(source_path) {|f| ::Sass::Engine.new(f.read) }
|
16
|
+
css = engine.to_css
|
17
|
+
css.delete!("\n") if configuration.sass.compress
|
18
|
+
File.open(stylesheet, "w") { |f| f.puts css }
|
19
|
+
end
|
20
|
+
end
|
21
|
+
end
|
22
|
+
|
23
|
+
def source_path_for(file)
|
24
|
+
Pathname.glob(File.join(configuration.sass.source_path, File.basename(file, '.css')+'.sass'))[0]
|
25
|
+
end
|
26
|
+
|
27
|
+
def stylesheets_to_process(collection)
|
28
|
+
expand_stylesheet_sources(collection.values.flatten).select do |file|
|
29
|
+
(File.exists?(file) and source_has_been_changed?(file)) or not File.exists?(file)
|
30
|
+
end
|
31
|
+
end
|
32
|
+
|
33
|
+
def source_has_been_changed?(file)
|
34
|
+
source_path = source_path_for(file)
|
35
|
+
source_path && (File.new(source_path).mtime >= File.new(file).mtime)
|
36
|
+
end
|
37
|
+
end
|
38
|
+
|
39
|
+
end
|
40
|
+
end
|
41
|
+
end
|
@@ -0,0 +1,62 @@
|
|
1
|
+
module Stylist
|
2
|
+
module Processors
|
3
|
+
class YuiCompressorProcessor < Processor
|
4
|
+
class MinificationCommandFailed < StandardError; end
|
5
|
+
|
6
|
+
class << self
|
7
|
+
def configure
|
8
|
+
configuration.add_option_group :yui_compressor, { :path_to_jar => nil, :path_to_java => 'java', :charset => 'utf-8', :line_break => nil, :production_only => true }
|
9
|
+
end
|
10
|
+
|
11
|
+
def process!(collection)
|
12
|
+
return if configuration.yui_compressor.production_only and not Rails.env.production?
|
13
|
+
|
14
|
+
stylesheets_to_process(collection).each do |stylesheet|
|
15
|
+
minify(stylesheet)
|
16
|
+
end
|
17
|
+
replace_files_in_collection_with_minified_files(collection)
|
18
|
+
end
|
19
|
+
|
20
|
+
def replace_files_in_collection_with_minified_files(collection)
|
21
|
+
collection.each_pair do |media, stylesheets|
|
22
|
+
collection[media] = expand_stylesheet_sources(stylesheets).map { |stylesheet| minified_stylesheet_name_for(stylesheet).basename.to_s }
|
23
|
+
end
|
24
|
+
end
|
25
|
+
|
26
|
+
def minify(stylesheet)
|
27
|
+
config = configuration.yui_compressor
|
28
|
+
command = [config.path_to_java, '-jar', config.path_to_jar, '-o', minified_stylesheet_name_for(stylesheet), *command_options] << stylesheet
|
29
|
+
unless system(*command)
|
30
|
+
raise MinificationCommandFailed, "Oops! Minification command failed with error code: #{$?}. The command executed was: #{command.join ' '}"
|
31
|
+
end
|
32
|
+
end
|
33
|
+
|
34
|
+
def command_options
|
35
|
+
config = configuration.yui_compressor
|
36
|
+
{ :type => 'css', :charset => config.charset, :line_break => nil }.inject([]) do |options, (name, argument)|
|
37
|
+
options.concat(["--#{name.to_s.gsub '_', '-'}", argument.to_s]) if argument
|
38
|
+
options
|
39
|
+
end
|
40
|
+
end
|
41
|
+
|
42
|
+
def minified_stylesheet_name_for(stylesheet)
|
43
|
+
dir, filename = stylesheet.split
|
44
|
+
dir.join([filename.basename(filename.extname), '.min', filename.extname].join)
|
45
|
+
end
|
46
|
+
|
47
|
+
def stylesheets_to_process(collection)
|
48
|
+
expand_stylesheet_sources(collection.values.flatten).select do |source_file|
|
49
|
+
minified_file = minified_stylesheet_name_for(source_file)
|
50
|
+
(minified_file.exist? and source_has_been_changed?(source_file)) or (source_file.exist? and not minified_file.exist?)
|
51
|
+
end
|
52
|
+
end
|
53
|
+
|
54
|
+
def source_has_been_changed?(file)
|
55
|
+
minified_path = minified_stylesheet_name_for(file)
|
56
|
+
minified_path.exist? && (File.new(file).mtime >= File.new(minified_path).mtime)
|
57
|
+
end
|
58
|
+
end
|
59
|
+
|
60
|
+
end
|
61
|
+
end
|
62
|
+
end
|
@@ -0,0 +1,17 @@
|
|
1
|
+
module Stylist
|
2
|
+
class Railtie < Rails::Railtie
|
3
|
+
railtie_name :stylist
|
4
|
+
|
5
|
+
initializer :configure_stylist_defaults, :before => :load_application_initializers do |app|
|
6
|
+
if defined? ::Haml
|
7
|
+
require 'stylist/processors/sass_processor'
|
8
|
+
::Stylist.configure { |config| config.process_with :sass }
|
9
|
+
end
|
10
|
+
|
11
|
+
if defined? ::Less
|
12
|
+
require 'stylist/processors/less_processor'
|
13
|
+
::Stylist.configure { |config| config.process_with :less }
|
14
|
+
end
|
15
|
+
end
|
16
|
+
end
|
17
|
+
end
|