guard-copy 0.0.2 → 0.0.3

Sign up to get free protection for your applications and to get access to all the features.
data/README.md CHANGED
@@ -36,7 +36,7 @@ guard :copy, :from => 'source', :to => 'target'
36
36
  ### Multiple Targets
37
37
 
38
38
  ``` ruby
39
- guard :copy, :from => 'source', :to => ['t1', 't2']
39
+ guard :copy, :from => 'source', :to => ['t1', 't2']
40
40
  ```
41
41
 
42
42
  ### Newest Wildcard Target
@@ -48,6 +48,45 @@ guard :copy, :from => 'source', :to => 'target*', :glob => :newest
48
48
  This guard will copy files from the source directory to the newest
49
49
  directory starting with 'target'.
50
50
 
51
+ ## Options
52
+
53
+ By default, Guard::Copy will copy modified and newly created files from
54
+ the directory specified by the `:from` option to that specified by the
55
+ `:to` option.
56
+
57
+ ### List of available options:
58
+
59
+ ``` ruby
60
+ :from => 'source' # directory to copy files from
61
+ :to => 'target' # directory or glob to copy files to; can be an array
62
+ :glob => :newest # how to handle globs; default: :all
63
+ # :newest - copy to only the newest directory
64
+ # :all - copy to all directories
65
+ :delete => true # delete files from target directories
66
+ :verbose => true # log all operations as info messages
67
+ ```
68
+
69
+ ## Watchers
70
+
71
+ The paths that files are ultimately copied to are generated by
72
+ substituting the `:from` portion of the changed file's path with each of
73
+ the `:to` directories. Because of this, any watchers you define will be
74
+ modified to be relative to the `:from` directory.
75
+
76
+ Therefore the following two watcher definitions are equivalent.
77
+
78
+ ``` ruby
79
+ guard :copy, :from => 'source', :to => 'target' do
80
+ watch(%r{^source/.+\.js$})
81
+ end
82
+ ```
83
+
84
+ ``` ruby
85
+ guard :copy, :from => 'source', :to => 'target' do
86
+ watch(%r{^.+\.js$})
87
+ end
88
+ ```
89
+
51
90
  ## Author
52
91
 
53
92
  [Marc Schwieterman](https://github.com/marcisme)
@@ -0,0 +1,37 @@
1
+ module Guard
2
+ class Copy
3
+ class Target
4
+
5
+ attr_reader :pattern, :options, :paths
6
+
7
+ # Initialize a new target
8
+ #
9
+ # @param [String] pattern the pattern for this target
10
+ # @option options [Symbol] glob target resolution mode, `:newest` or `:all`
11
+ #
12
+ def initialize(pattern, options = {})
13
+ raise ArgumentError, 'pattern cannot be nil' unless pattern
14
+ raise ArgumentError, 'pattern cannot be empty' if pattern.empty?
15
+ @pattern = pattern
16
+ @options = {
17
+ :glob => :all
18
+ }.merge(options)
19
+ @paths = []
20
+ end
21
+
22
+ # Resolve the target into one or more paths
23
+ #
24
+ # @return [Boolean] true if the pattern resolved to any paths
25
+ def resolve
26
+ @paths.clear
27
+ if @options[:glob] == :newest
28
+ @paths.concat(Dir[@pattern].sort_by { |f| File.mtime(f) }.last(1))
29
+ else
30
+ @paths.concat(Dir[@pattern])
31
+ end
32
+ @paths.any?
33
+ end
34
+
35
+ end
36
+ end
37
+ end
@@ -1,5 +1,5 @@
1
1
  module Guard
2
2
  module CopyVersion
3
- VERSION = "0.0.2"
3
+ VERSION = "0.0.3"
4
4
  end
5
5
  end
data/lib/guard/copy.rb CHANGED
@@ -1,34 +1,51 @@
1
1
  require 'guard'
2
2
  require 'guard/guard'
3
- require 'guard/copy/version'
4
3
  require 'fileutils'
5
4
 
6
5
  module Guard
7
6
  class Copy < Guard
8
7
 
8
+ autoload :Target, 'guard/copy/target'
9
+
9
10
  attr_reader :targets
10
11
 
11
12
  # Initialize a Guard.
12
13
  # @param [Array<Guard::Watcher>] watchers the Guard file watchers
13
14
  # @param [Hash] options the custom Guard options
14
15
  def initialize(watchers = [], options = {})
15
- # watchers are currently ignored
16
- watchers << ::Guard::Watcher.new(%r{#{options[:from]}/.*})
17
- options[:to] = Array(options[:to]).freeze
18
16
  super
17
+ if watchers.empty?
18
+ watchers << ::Guard::Watcher.new(%r{^#{options[:from]}/.*$})
19
+ else
20
+ watchers.each { |w| normalize_watcher(w, options[:from]) }
21
+ end
22
+ @targets = Array(options[:to]).map { |to| Target.new(to, options) }
19
23
  end
20
24
 
21
25
  # Call once when Guard starts. Please override initialize method to init stuff.
22
26
  # @raise [:task_has_failed] when start has failed
23
27
  def start
24
- validate_from
25
- validate_to
26
- @targets = resolve_targets
27
- if @targets.any?
28
+ validate_presence_of(:from)
29
+ validate_from_is_directory
30
+ validate_presence_of(:to)
31
+ validate_to_does_not_include_from
32
+ @targets.each do |target|
33
+ unless target.resolve
34
+ UI.warning("Guard::Copy - '#{target.pattern}' does not match a valid directory")
35
+ end
36
+ end
37
+ if target_paths.any?
28
38
  UI.info("Guard::Copy will copy files from:")
29
39
  UI.info(" #{options[:from]}")
30
40
  UI.info("to:")
31
- @targets.each { |target| UI.info(" #{target}") }
41
+ target_paths.each { |target_path| UI.info(" #{target_path}") }
42
+
43
+ if options[:delete]
44
+ UI.info("Guard::Copy will delete files removed from:")
45
+ UI.info(" #{options[:from]}")
46
+ UI.info("from:")
47
+ target_paths.each { |target_path| UI.info(" #{target_path}") }
48
+ end
32
49
  end
33
50
  end
34
51
 
@@ -40,55 +57,85 @@ module Guard
40
57
 
41
58
  # Called on file(s) modifications that the Guard watches.
42
59
  # @param [Array<String>] paths the changes files or paths
43
- # @raise [:task_has_failed] when run_on_change has failed
44
- def run_on_change(paths)
45
- validate_targets
46
- paths.each do |from_path|
47
- @targets.each do |target|
48
- to_path = from_path.sub(@options[:from], target)
49
- validate_to_path(to_path)
50
- FileUtils.cp(from_path, to_path)
51
- end
60
+ # @raise [:task_has_failed] when run_on_changes has failed
61
+ def run_on_changes(paths)
62
+ validate_at_least_one_target('copy')
63
+ with_all_target_paths(paths) do |from_path, to_path|
64
+ validate_to_path(to_path)
65
+ UI.info("copying to #{to_path}") if options[:verbose]
66
+ FileUtils.cp(from_path, to_path)
52
67
  end
53
68
  end
54
69
 
55
70
  # Called on file(s) deletions that the Guard watches.
56
71
  # @param [Array<String>] paths the deleted files or paths
57
- # @raise [:task_has_failed] when run_on_deletion has failed
58
- def run_on_deletion(paths)
72
+ # @raise [:task_has_failed] when run_on_removals has failed
73
+ def run_on_removals(paths)
74
+ return unless options[:delete]
75
+ validate_at_least_one_target('delete')
76
+ with_all_target_paths(paths) do |_, to_path|
77
+ validate_to_file(to_path)
78
+ UI.info("deleting #{to_path}") if options[:verbose]
79
+ FileUtils.rm(to_path)
80
+ end
59
81
  end
60
82
 
61
83
  private
62
84
 
63
- def validate_from
64
- unless options[:from]
65
- UI.error('Guard::Copy - :from option is required')
66
- throw :task_has_failed
85
+ def target_paths
86
+ @targets.map { |t| t.paths }.flatten
87
+ end
88
+
89
+ def with_all_target_paths(paths)
90
+ paths.each do |from_path|
91
+ target_paths.each do |target_path|
92
+ to_path = from_path.sub(@options[:from], target_path)
93
+ yield(from_path, to_path)
94
+ end
67
95
  end
68
- if File.file?(options[:from])
69
- UI.error("Guard::Copy - '#{options[:from]}' is a file and must be a directory")
70
- throw :task_has_failed
96
+ end
97
+
98
+ def normalize_watcher(watcher, from)
99
+ unless watcher.pattern.source =~ %r{^\^#{from}/.*}
100
+ normalized_source = watcher.pattern.source.sub(%r{^\^?(#{from})?/?}, "^#{from}/")
101
+ UI.info('Guard::Copy is changing watcher pattern:')
102
+ UI.info(" #{watcher.pattern.source}")
103
+ UI.info('to:')
104
+ UI.info(" #{normalized_source}")
105
+ watcher.pattern = Regexp.new(normalized_source)
71
106
  end
72
- unless File.directory?(options[:from])
73
- UI.error('Guard::Copy - :from option does not contain a valid directory')
107
+ end
108
+
109
+ def validate_presence_of(option)
110
+ unless options[option]
111
+ UI.error("Guard::Copy - :#{option} option is required")
74
112
  throw :task_has_failed
75
113
  end
76
114
  end
77
115
 
78
- def validate_to
79
- if options[:to].empty?
80
- UI.error('Guard::Copy - :to option is required')
81
- throw :task_has_failed
116
+ def validate_from_is_directory
117
+ path = options[:from]
118
+ unless File.directory?(path)
119
+ if File.file?(path)
120
+ UI.error("Guard::Copy - '#{path}' is a file and must be a directory")
121
+ throw :task_has_failed
122
+ else
123
+ UI.error("Guard::Copy - :from option does not contain a valid directory")
124
+ throw :task_has_failed
125
+ end
82
126
  end
127
+ end
128
+
129
+ def validate_to_does_not_include_from
83
130
  if options[:to].include?(options[:from])
84
131
  UI.error('Guard::Copy - :to must not include :from')
85
132
  throw :task_has_failed
86
133
  end
87
134
  end
88
135
 
89
- def validate_targets
90
- if @targets.empty?
91
- UI.error('Guard::Copy - cannot copy, no valid :to directories')
136
+ def validate_at_least_one_target(operation)
137
+ if target_paths.empty?
138
+ UI.error("Guard::Copy - cannot #{operation}, no valid :to directories")
92
139
  throw :task_has_failed
93
140
  end
94
141
  end
@@ -102,23 +149,11 @@ module Guard
102
149
  end
103
150
  end
104
151
 
105
- def resolve_targets
106
- Array.new.tap do |targets|
107
- options[:to].each do |to|
108
- if @options[:glob] == :newest
109
- dirs = Dir[to].sort_by { |f| File.mtime(f) }.last(1)
110
- else
111
- dirs = Dir[to]
112
- end
113
- if dirs.any?
114
- #dirs.each do |dir|
115
- #throw :task_has_failed if File.file?(dir)
116
- #end
117
- targets.concat(dirs)
118
- else
119
- UI.warning("Guard::Copy - '#{to}' does not match a valid directory")
120
- end
121
- end
152
+ def validate_to_file(to_file)
153
+ unless File.file?(to_file)
154
+ UI.error('Guard::Copy - cannot delete, file does not exist:')
155
+ UI.error(" #{to_file}")
156
+ throw :task_has_failed
122
157
  end
123
158
  end
124
159
 
metadata CHANGED
@@ -1,7 +1,7 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: guard-copy
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.0.2
4
+ version: 0.0.3
5
5
  prerelease:
6
6
  platform: ruby
7
7
  authors:
@@ -9,7 +9,7 @@ authors:
9
9
  autorequire:
10
10
  bindir: bin
11
11
  cert_chain: []
12
- date: 2012-05-24 00:00:00.000000000 Z
12
+ date: 2012-06-24 00:00:00.000000000 Z
13
13
  dependencies:
14
14
  - !ruby/object:Gem::Dependency
15
15
  name: guard
@@ -18,7 +18,7 @@ dependencies:
18
18
  requirements:
19
19
  - - ~>
20
20
  - !ruby/object:Gem::Version
21
- version: '1.0'
21
+ version: 1.1.1
22
22
  type: :runtime
23
23
  prerelease: false
24
24
  version_requirements: !ruby/object:Gem::Requirement
@@ -26,119 +26,7 @@ dependencies:
26
26
  requirements:
27
27
  - - ~>
28
28
  - !ruby/object:Gem::Version
29
- version: '1.0'
30
- - !ruby/object:Gem::Dependency
31
- name: bundler
32
- requirement: !ruby/object:Gem::Requirement
33
- none: false
34
- requirements:
35
- - - ! '>='
36
- - !ruby/object:Gem::Version
37
- version: 1.1.0
38
- type: :development
39
- prerelease: false
40
- version_requirements: !ruby/object:Gem::Requirement
41
- none: false
42
- requirements:
43
- - - ! '>='
44
- - !ruby/object:Gem::Version
45
- version: 1.1.0
46
- - !ruby/object:Gem::Dependency
47
- name: rake
48
- requirement: !ruby/object:Gem::Requirement
49
- none: false
50
- requirements:
51
- - - ! '>='
52
- - !ruby/object:Gem::Version
53
- version: 0.9.2
54
- type: :development
55
- prerelease: false
56
- version_requirements: !ruby/object:Gem::Requirement
57
- none: false
58
- requirements:
59
- - - ! '>='
60
- - !ruby/object:Gem::Version
61
- version: 0.9.2
62
- - !ruby/object:Gem::Dependency
63
- name: aruba
64
- requirement: !ruby/object:Gem::Requirement
65
- none: false
66
- requirements:
67
- - - ~>
68
- - !ruby/object:Gem::Version
69
- version: '0.4'
70
- type: :development
71
- prerelease: false
72
- version_requirements: !ruby/object:Gem::Requirement
73
- none: false
74
- requirements:
75
- - - ~>
76
- - !ruby/object:Gem::Version
77
- version: '0.4'
78
- - !ruby/object:Gem::Dependency
79
- name: guard-cucumber
80
- requirement: !ruby/object:Gem::Requirement
81
- none: false
82
- requirements:
83
- - - ! '>='
84
- - !ruby/object:Gem::Version
85
- version: '0.8'
86
- type: :development
87
- prerelease: false
88
- version_requirements: !ruby/object:Gem::Requirement
89
- none: false
90
- requirements:
91
- - - ! '>='
92
- - !ruby/object:Gem::Version
93
- version: '0.8'
94
- - !ruby/object:Gem::Dependency
95
- name: guard-rspec
96
- requirement: !ruby/object:Gem::Requirement
97
- none: false
98
- requirements:
99
- - - ! '>='
100
- - !ruby/object:Gem::Version
101
- version: 0.7.2
102
- type: :development
103
- prerelease: false
104
- version_requirements: !ruby/object:Gem::Requirement
105
- none: false
106
- requirements:
107
- - - ! '>='
108
- - !ruby/object:Gem::Version
109
- version: 0.7.2
110
- - !ruby/object:Gem::Dependency
111
- name: fakefs
112
- requirement: !ruby/object:Gem::Requirement
113
- none: false
114
- requirements:
115
- - - ! '>='
116
- - !ruby/object:Gem::Version
117
- version: 0.4.0
118
- type: :development
119
- prerelease: false
120
- version_requirements: !ruby/object:Gem::Requirement
121
- none: false
122
- requirements:
123
- - - ! '>='
124
- - !ruby/object:Gem::Version
125
- version: 0.4.0
126
- - !ruby/object:Gem::Dependency
127
- name: mocha
128
- requirement: !ruby/object:Gem::Requirement
129
- none: false
130
- requirements:
131
- - - ! '>='
132
- - !ruby/object:Gem::Version
133
- version: 0.11.4
134
- type: :development
135
- prerelease: false
136
- version_requirements: !ruby/object:Gem::Requirement
137
- none: false
138
- requirements:
139
- - - ! '>='
140
- - !ruby/object:Gem::Version
141
- version: 0.11.4
29
+ version: 1.1.1
142
30
  description: Guard::Copy automatically copies files.
143
31
  email:
144
32
  - marc.schwieterman@gmail.com
@@ -146,6 +34,7 @@ executables: []
146
34
  extensions: []
147
35
  extra_rdoc_files: []
148
36
  files:
37
+ - lib/guard/copy/target.rb
149
38
  - lib/guard/copy/templates/Guardfile
150
39
  - lib/guard/copy/version.rb
151
40
  - lib/guard/copy.rb
@@ -165,7 +54,7 @@ required_ruby_version: !ruby/object:Gem::Requirement
165
54
  version: '0'
166
55
  segments:
167
56
  - 0
168
- hash: 964757571042390819
57
+ hash: 1790192604358185863
169
58
  required_rubygems_version: !ruby/object:Gem::Requirement
170
59
  none: false
171
60
  requirements:
@@ -174,7 +63,7 @@ required_rubygems_version: !ruby/object:Gem::Requirement
174
63
  version: '0'
175
64
  segments:
176
65
  - 0
177
- hash: 964757571042390819
66
+ hash: 1790192604358185863
178
67
  requirements: []
179
68
  rubyforge_project:
180
69
  rubygems_version: 1.8.24