guard-less 0.0.4 → 0.1.0

Sign up to get free protection for your applications and to get access to all the features.
data/README.md CHANGED
@@ -21,16 +21,59 @@ Please read [Guard usage doc](https://github.com/guard/guard#readme).
21
21
 
22
22
  ## Guardfile
23
23
 
24
- guard 'less', :all_on_start => true, :all_after_change => true do
25
- watch(/.*\.less$/)
26
- end
24
+ ```ruby
25
+ guard 'less', :all_on_start => true, :all_after_change => true do
26
+ watch(%r{^.*\.less$})
27
+ end
28
+ ```
27
29
 
28
30
  Please read [Guard doc](https://github.com/guard/guard#readme) for more info about Guardfile DSL.
29
31
 
30
32
  ## Options
31
33
 
32
- :all_after_change => [true|false] # run on all files after any changed files, default: true
33
- :all_on_start => [true|false] # run on all the files at startup, default: true
34
+ ```ruby
35
+ :all_after_change => [true|false] # run on all files after any changed files
36
+ # default: true
37
+
38
+ :all_on_start => [true|false] # run on all the files at startup
39
+ # default: true
40
+
41
+ :output => 'relative/path' # base directory for output CSS files; if unset,
42
+ # .css files are generated in the same directories
43
+ # as their corresponding .less file
44
+ # default: nil
45
+
46
+ :import_paths => ['lib/styles'] # an array of additional load paths to pass to the
47
+ # LESS parser, used when resolving `@import`
48
+ # statements
49
+ # default: [] (see below)
50
+ ```
51
+
52
+ ### Output option
53
+
54
+ By default, `.css` files will be generated in the same directories as their
55
+ corresponding `.less` files (partials beginning with `_` are always excluded).
56
+ To customize the output location, pass the `:output` option as described above,
57
+ and be sure to use a match group in the regular expression in your watch to
58
+ capture nested structure that will be preserved, i.e.
59
+
60
+ ```ruby
61
+ guard 'less', :output => 'public/stylesheets' do
62
+ watch(%r{^app/stylesheets/(.+\.less)$})
63
+ end
64
+ ```
65
+
66
+ will result in `app/stylesheets/forums/main.less` producing CSS at
67
+ `public/stylesheets/forums/main.css`.
68
+
69
+ ### Import paths option
70
+
71
+ As each `.less` file is parsed, the directory containing the file is
72
+ automatically prepended to the import paths, so imports relative to your watched
73
+ dirs like `@import 'shared/_type-styles'` should always work. You can supply
74
+ additional paths with this option so that, for the `['lib/styles']` example, a
75
+ file at `lib/styles/reset.less` could be imported without a qualified path as
76
+ `@import 'reset'`.
34
77
 
35
78
  # License
36
79
 
@@ -11,44 +11,132 @@ module Guard
11
11
  # = Guard method =
12
12
  # ================
13
13
  def initialize(watchers=[], options={})
14
- super
15
- @all_after_change = options.delete(:all_after_change)
16
- @all_on_start = options.delete(:all_on_start)
14
+ defaults = {
15
+ :all_after_change => true,
16
+ :all_on_start => true,
17
+ :output => nil,
18
+ :import_paths => []
19
+ }
20
+
21
+ super(watchers, defaults.merge(options))
17
22
  end
18
23
 
19
24
  def start
20
25
  UI.info "Guard::Less #{LessVersion::VERSION} is on the job!"
21
- run_all unless @all_on_start == false
26
+ run_all if options[:all_on_start]
22
27
  end
23
28
 
24
29
  # Call with Ctrl-/ signal
25
30
  # This method should be principally used for long action like running all specs/tests/...
26
31
  def run_all
27
- patterns = @watchers.map { |w| w.pattern }
28
- files = Dir.glob('**/*.*')
29
- paths = []
30
- files.each do |file|
31
- patterns.each do |pattern|
32
- paths << file if file.match(Regexp.new(pattern))
33
- end
34
- end
35
- run(paths)
32
+ UI.info "Guard::Less: compiling all files"
33
+ patterns = watchers.map { |w| w.pattern }
34
+ files = Dir.glob('**/*.*')
35
+ paths = []
36
+ files.each do |file|
37
+ patterns.each do |pattern|
38
+ paths << file if file.match(Regexp.new(pattern))
39
+ end
40
+ end
41
+ run(paths)
36
42
  end
37
43
 
38
44
  # Call on file(s) modifications
39
45
  def run_on_change(paths)
40
- run_all if run(paths) && (@all_after_change != false)
46
+ options[:all_after_change] ? run_all : run(paths)
41
47
  end
42
48
 
43
49
  def run(paths)
44
- last_passed = false
45
- paths.each do |file|
46
- unless File.basename(file)[0] == "_"
47
- UI.info "lessc - #{file}\n"
48
- last_passed = system("lessc #{file} --verbose")
50
+ directories = nested_directory_map(paths)
51
+
52
+ directories.each do |destination, stylesheets|
53
+ stylesheets.each do |lessfile|
54
+ # Skip partials
55
+ basename = File.basename(lessfile)
56
+ next if basename[0,1] == "_"
57
+
58
+ cssfile = File.join(destination, basename.gsub(/\.less$/, '.css'))
59
+
60
+ # Just in case
61
+ if cssfile == lessfile
62
+ UI.info "Guard::Less: Skipping #{lessfile} since the output would overwrite the original file"
63
+ elsif mtime(cssfile) >= mtime_including_imports(lessfile)
64
+ UI.info "Guard::Less: Skipping #{lessfile} because #{cssfile} is already up-to-date"
65
+ else
66
+ UI.info "Guard::Less: #{lessfile} -> #{cssfile}\n"
67
+ FileUtils.mkdir_p(File.expand_path(destination))
68
+ compile(lessfile, cssfile)
69
+ end
70
+ end
71
+ end
72
+ end
73
+
74
+ private
75
+
76
+ # Parse the source lessfile and write to target cssfile
77
+ def compile(lessfile, cssfile)
78
+ import_paths = options[:import_paths].unshift(File.dirname(lessfile))
79
+ parser = ::Less::Parser.new :paths => import_paths, :filename => lessfile
80
+ File.open(lessfile,'r') do |infile|
81
+ File.open(cssfile,'w') do |outfile|
82
+ tree = parser.parse(infile.read)
83
+ outfile << tree.to_css
84
+ end
85
+ end
86
+ true
87
+ rescue Exception => e
88
+ UI.info "Guard::Less: Compiling #{lessfile} failed with message: #{e.message}"
89
+ false
90
+ end
91
+
92
+ # Creates a hash of changed files keyed by their target nested directory,
93
+ # which is based on either the original source directory or the :output
94
+ # option, plus the regex match group in a watcher like:
95
+ #
96
+ # %r{^app/stylesheets/(.+\.less)$}
97
+ def nested_directory_map(paths)
98
+ directories = {}
99
+
100
+ watchers.product(paths).each do |watcher, path|
101
+ if matches = path.match(watcher.pattern)
102
+ target = options[:output] || File.dirname(path)
103
+ if subpath = matches[1]
104
+ target = File.join(target, File.dirname(subpath)).gsub(/\/\.$/, '')
105
+ end
106
+
107
+ if directories[target]
108
+ directories[target] << path
109
+ else
110
+ directories[target] = [path]
111
+ end
112
+ end
113
+ end
114
+
115
+ directories
116
+ end
117
+
118
+ # mtime checking borrowed from the old official LESS Rails plugin:
119
+ # https://github.com/cloudhead/more
120
+ def mtime(file)
121
+ return 0 unless File.file?(file)
122
+ File.mtime(file).to_i
123
+ end
124
+
125
+ # consider imports for mtime
126
+ # just 1 level deep so we do not get any looping/nesting errors
127
+ def mtime_including_imports(file)
128
+ mtimes = [mtime(file)]
129
+ File.readlines(file).each do |line|
130
+ if line =~ /^\s*@import ['"]([^'"]+)/
131
+ imported = File.join(File.dirname(file), $1)
132
+ mtimes << if imported =~ /\.le?ss$/ # complete path given ?
133
+ mtime(imported)
134
+ else # we need to add .less or .lss
135
+ [mtime("#{imported}.less"), mtime("#{imported}.lss")].max
136
+ end
49
137
  end
50
138
  end
51
- last_passed
139
+ mtimes.max
52
140
  end
53
141
 
54
142
  end
@@ -1,3 +1,3 @@
1
- guard 'less', :all_on_start => true, :all_after_pass => true do
2
- watch(/.*\.less$/)
1
+ guard 'less', :all_on_start => true, :all_after_change => true do
2
+ watch(%r{^.+\.less$})
3
3
  end
@@ -1,5 +1,5 @@
1
1
  module Guard
2
2
  module LessVersion
3
- VERSION = "0.0.4"
3
+ VERSION = "0.1.0"
4
4
  end
5
5
  end
metadata CHANGED
@@ -4,9 +4,9 @@ version: !ruby/object:Gem::Version
4
4
  prerelease: false
5
5
  segments:
6
6
  - 0
7
+ - 1
7
8
  - 0
8
- - 4
9
- version: 0.0.4
9
+ version: 0.1.0
10
10
  platform: ruby
11
11
  authors:
12
12
  - Brendan Erwin
@@ -14,7 +14,7 @@ autorequire:
14
14
  bindir: bin
15
15
  cert_chain: []
16
16
 
17
- date: 2011-04-23 00:00:00 -04:00
17
+ date: 2011-08-23 00:00:00 -04:00
18
18
  default_executable:
19
19
  dependencies:
20
20
  - !ruby/object:Gem::Dependency
@@ -41,11 +41,68 @@ dependencies:
41
41
  - - ~>
42
42
  - !ruby/object:Gem::Version
43
43
  segments:
44
- - 1
45
44
  - 2
46
- version: "1.2"
45
+ - 0
46
+ - 5
47
+ version: 2.0.5
47
48
  type: :runtime
48
49
  version_requirements: *id002
50
+ - !ruby/object:Gem::Dependency
51
+ name: bundler
52
+ prerelease: false
53
+ requirement: &id003 !ruby/object:Gem::Requirement
54
+ none: false
55
+ requirements:
56
+ - - ~>
57
+ - !ruby/object:Gem::Version
58
+ segments:
59
+ - 1
60
+ - 0
61
+ version: "1.0"
62
+ type: :development
63
+ version_requirements: *id003
64
+ - !ruby/object:Gem::Dependency
65
+ name: fakefs
66
+ prerelease: false
67
+ requirement: &id004 !ruby/object:Gem::Requirement
68
+ none: false
69
+ requirements:
70
+ - - ~>
71
+ - !ruby/object:Gem::Version
72
+ segments:
73
+ - 0
74
+ - 3
75
+ version: "0.3"
76
+ type: :development
77
+ version_requirements: *id004
78
+ - !ruby/object:Gem::Dependency
79
+ name: guard-rspec
80
+ prerelease: false
81
+ requirement: &id005 !ruby/object:Gem::Requirement
82
+ none: false
83
+ requirements:
84
+ - - ~>
85
+ - !ruby/object:Gem::Version
86
+ segments:
87
+ - 0
88
+ - 4
89
+ version: "0.4"
90
+ type: :development
91
+ version_requirements: *id005
92
+ - !ruby/object:Gem::Dependency
93
+ name: rspec
94
+ prerelease: false
95
+ requirement: &id006 !ruby/object:Gem::Requirement
96
+ none: false
97
+ requirements:
98
+ - - ~>
99
+ - !ruby/object:Gem::Version
100
+ segments:
101
+ - 2
102
+ - 6
103
+ version: "2.6"
104
+ type: :development
105
+ version_requirements: *id006
49
106
  description: Guard::Less automatically compiles less (like lessc --watch)
50
107
  email:
51
108
  - brendanjerwin@gmail.com