guard-copy 0.0.2 → 0.0.3

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