guard-less 0.0.4 → 0.1.0

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/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