duck_test 0.1.4

Sign up to get free protection for your applications and to get access to all the features.
Files changed (45) hide show
  1. data/bin/ducktest +29 -0
  2. data/lib/duck_test/autoload_config.rb +103 -0
  3. data/lib/duck_test/base.rb +41 -0
  4. data/lib/duck_test/commands.rb +208 -0
  5. data/lib/duck_test/config.rb +675 -0
  6. data/lib/duck_test/config_helper.rb +99 -0
  7. data/lib/duck_test/console.rb +18 -0
  8. data/lib/duck_test/default_config.rb +48 -0
  9. data/lib/duck_test/frame_work/base.rb +587 -0
  10. data/lib/duck_test/frame_work/file_manager.rb +511 -0
  11. data/lib/duck_test/frame_work/filter_set.rb +233 -0
  12. data/lib/duck_test/frame_work/map.rb +331 -0
  13. data/lib/duck_test/frame_work/queue.rb +221 -0
  14. data/lib/duck_test/frame_work/queue_event.rb +29 -0
  15. data/lib/duck_test/frame_work/rspec/base.rb +17 -0
  16. data/lib/duck_test/frame_work/rspec/frame_work.rb +30 -0
  17. data/lib/duck_test/frame_work/test_unit/base.rb +14 -0
  18. data/lib/duck_test/frame_work/test_unit/frame_work.rb +33 -0
  19. data/lib/duck_test/frame_work/watch_config.rb +69 -0
  20. data/lib/duck_test/gem/helper.rb +107 -0
  21. data/lib/duck_test/logger.rb +127 -0
  22. data/lib/duck_test/option_parser.rb +30 -0
  23. data/lib/duck_test/platforms/base.rb +18 -0
  24. data/lib/duck_test/platforms/dependencies.rb +18 -0
  25. data/lib/duck_test/platforms/generic/base.rb +15 -0
  26. data/lib/duck_test/platforms/generic/listener.rb +104 -0
  27. data/lib/duck_test/platforms/linux/base.rb +15 -0
  28. data/lib/duck_test/platforms/linux/listener.rb +76 -0
  29. data/lib/duck_test/platforms/listener.rb +303 -0
  30. data/lib/duck_test/platforms/mac/base.rb +15 -0
  31. data/lib/duck_test/platforms/mac/listener.rb +79 -0
  32. data/lib/duck_test/platforms/mac/listener.rb.orig +147 -0
  33. data/lib/duck_test/platforms/os_helper.rb +102 -0
  34. data/lib/duck_test/platforms/watch_event.rb +47 -0
  35. data/lib/duck_test/platforms/windows/base.rb +15 -0
  36. data/lib/duck_test/platforms/windows/listener.rb +123 -0
  37. data/lib/duck_test/railtie.rb +29 -0
  38. data/lib/duck_test/usage.rb +34 -0
  39. data/lib/duck_test/usage.yml +112 -0
  40. data/lib/duck_test/version.rb +3 -0
  41. data/lib/duck_test.rb +6 -0
  42. data/lib/notes.txt +215 -0
  43. data/lib/tasks/duck_tests.rake +35 -0
  44. data/lib/tasks/gem_tasks.rake +18 -0
  45. metadata +92 -0
@@ -0,0 +1,233 @@
1
+ module DuckTest
2
+ module FrameWork
3
+
4
+ # Data and methods that are used to filter directories and files.
5
+ class FilterSet
6
+ include LoggerHelper
7
+
8
+ attr_accessor :included
9
+ attr_accessor :included_dirs
10
+ attr_accessor :excluded
11
+ attr_accessor :excluded_dirs
12
+ attr_accessor :non_loadable
13
+
14
+ ##################################################################################
15
+ # Initialize a new FilterSet
16
+ # @return [FilterSet]
17
+ def initialize(options = {})
18
+ super()
19
+
20
+ self.included = options[:included]
21
+ self.included_dirs = options[:included_dirs]
22
+ self.excluded = options[:excluded]
23
+ self.excluded_dirs = options[:excluded_dirs]
24
+ self.non_loadable = options[:non_loadable]
25
+
26
+ return self
27
+ end
28
+
29
+ ##################################################################################
30
+ # @note You can pass a Symbol named :all instead of a Regexp and the method will always return true. This provides the ability to specify "all" files as part of a filter.
31
+ # Compares a file_spec against an array of {http://www.ruby-doc.org/core-1.9.3/Regexp.html Regexp's} (filters).
32
+ #
33
+ # match_filters?("test.rb", /^book/) # => false
34
+ #
35
+ # match_filters?("test.rb", [/^book/]) # => false
36
+ #
37
+ # match_filters?("test.rb", [/^book/, /^test/]) # => true
38
+ #
39
+ # match_filters?("test.rb", :all) # => true
40
+ #
41
+ # match_filters?("test.rb", [:all]) # => true
42
+ #
43
+ # @param [String] file_spec A file name that adheres to {http://ruby-doc.org/core-1.9.3/File.html#method-c-basename File.basename}.
44
+ # @param [Array] filters An Array of {http://www.ruby-doc.org/core-1.9.3/Regexp.html Regexp's}. Each Regexp is compared against file_spec.
45
+ # The first Regexp that matches file_spec wins and returns true, otherwise, this method returns false.
46
+ # Also, you can pass an Array with a single Symbol named :all and match_filters? will always return true.
47
+ # @param [String] subdirectory If file_spec is a directory, then, subdirectory will be used during the evaluations instead of file_spec.
48
+ # @return [Boolean] Returns true if a match is found, otherwise, false
49
+ def match_filters?(file_spec = nil, filters = nil, subdirectory = nil)
50
+
51
+ begin
52
+
53
+ unless file_spec.blank? || filters.blank?
54
+
55
+ ducklog.system %(FilterSet.match_filters?(#{file_spec}, #{filters}, #{subdirectory}))
56
+
57
+ filters = filters.kind_of?(Array) ? filters : [filters]
58
+ subdirectory = subdirectory.blank? ? "" : subdirectory
59
+
60
+ filters.each do |expression|
61
+
62
+ if expression.kind_of?(Symbol) && expression.eql?(:all)
63
+ ducklog.system "returning true due to :all flag"
64
+ return true
65
+
66
+ elsif expression.kind_of?(Regexp)
67
+
68
+ if File.directory?(file_spec)
69
+
70
+ # blank subdirectory is allowed, because, we might be comparing against a
71
+ # watch root directory.
72
+ if subdirectory.blank?
73
+ return true
74
+
75
+ elsif subdirectory.match(expression)
76
+ ducklog.system "returning true due to Regexp MATCH !!!!"
77
+ return true
78
+
79
+ end
80
+
81
+ elsif File.basename(file_spec).match(expression)
82
+ ducklog.system "returning true due to Regexp MATCH !!!!"
83
+ return true
84
+
85
+ end
86
+ end
87
+ end
88
+ end
89
+
90
+ rescue Exception => e
91
+ ducklog.exception e
92
+ end
93
+
94
+ ducklog.system "FilterSet.match_filters?() returning false"
95
+
96
+ return false
97
+ end
98
+
99
+ ##################################################################################
100
+ # Conveinence method that calls {FilterSet#match_filters? match_filters?} with the current value of {FilterSet#included included}
101
+ # - In this context, file_spec cannot be blank.
102
+ # - Will return true if self.included array is nil or empty.
103
+ # @param [String] file_spec See {FilterSet#match_filters? match_filters?}
104
+ # @return [Boolean] Returns true if a match is found, otherwise, false
105
+ def included?(file_spec)
106
+ ducklog.system %(FilterSet.included?(#{file_spec}, #{self.included}))
107
+ if file_spec.blank?
108
+ return false
109
+
110
+ elsif self.has_included?
111
+ return self.match_filters?(file_spec, self.included)
112
+
113
+ else
114
+ return true
115
+
116
+ end
117
+ end
118
+
119
+ ##################################################################################
120
+ # Conveinence method that determines if an {FilterSet#included included} filter exists.
121
+ # @return [Boolean] Returns true if the {FilterSet#included included} filters exists, otherwise, false.
122
+ def has_included?
123
+ return self.included.blank? ? false : true
124
+ end
125
+
126
+ ##################################################################################
127
+ # Conveinence method that calls {FilterSet#match_filters? match_filters?} with the current value of {FilterSet#included_dirs included_dirs}
128
+ # - In this context, file_spec cannot be blank.
129
+ # - In this context, subdirectory CAN BE blank.
130
+ # - Will return true if self.included_dirs array is nil or empty.
131
+ # @param [String] file_spec See {FilterSet#match_filters? match_filters?}
132
+ # @param [String] subdirectory Subdirectory is any directory directly off of the watch root directory. Subdirectory CAN BE blank.
133
+ # @return [Boolean] Returns true if a match is found, otherwise, false
134
+ def included_dirs?(file_spec, subdirectory)
135
+ ducklog.system %(FilterSet.included?(#{file_spec}, #{self.included}, #{subdirectory}))
136
+ if file_spec.blank?
137
+ return false
138
+
139
+ elsif self.has_included_dirs?
140
+ return self.match_filters?(file_spec, self.included_dirs, subdirectory)
141
+
142
+ else
143
+ return true
144
+
145
+ end
146
+ end
147
+
148
+ ##################################################################################
149
+ # Conveinence method that determines if an {FilterSet#included_dirs included_dirs} filter exists.
150
+ # @return [Boolean] Returns true if the {FilterSet#included_dirs included_dirs} filters exists, otherwise, false.
151
+ def has_included_dirs?
152
+ return self.included_dirs.blank? ? false : true
153
+ end
154
+
155
+ ##################################################################################
156
+ # Conveinence method that calls {FilterSet#match_filters? match_filters?} with the current value of {FilterSet#excluded excluded}
157
+ # - In this context, file_spec cannot be blank.
158
+ # - Will return true if self.excluded array is nil or empty.
159
+ # @param [String] file_spec See {FilterSet#match_filters? match_filters?}
160
+ # @return [Boolean] Returns true if a match is found, otherwise, false
161
+ def excluded?(file_spec)
162
+ ducklog.system %(FilterSet.excluded?(#{file_spec}, #{self.excluded}))
163
+ if file_spec.blank?
164
+ return false
165
+
166
+ else
167
+ return self.match_filters?(file_spec, self.excluded)
168
+
169
+ end
170
+ end
171
+
172
+ ##################################################################################
173
+ # Conveinence method that determines if an {FilterSet#excluded excluded} filter exists.
174
+ # @return [Boolean] Returns true if the {FilterSet#excluded excluded} filters exists, otherwise, false.
175
+ def has_excluded?
176
+ return self.excluded.blank? ? false : true
177
+ end
178
+
179
+ ##################################################################################
180
+ # Conveinence method that calls {FilterSet#match_filters? match_filters?} with the current value of {FilterSet#excluded_dirs excluded_dirs}
181
+ # - In this context, file_spec cannot be blank.
182
+ # - In this context, subdirectory CAN BE blank.
183
+ # - Will return true if self.excluded_dirs array is nil or empty.
184
+ # @param [String] file_spec See {FilterSet#match_filters? match_filters?}
185
+ # @param [String] subdirectory Subdirectory is any directory directly off of the watch root directory. Subdirectory CAN BE blank.
186
+ # @return [Boolean] Returns true if a match is found, otherwise, false
187
+ def excluded_dirs?(file_spec, subdirectory)
188
+ ducklog.system %(FilterSet.excluded_dirs?(#{file_spec}, #{self.excluded_dirs}, #{subdirectory}))
189
+ if file_spec.blank?
190
+ return false
191
+
192
+ else
193
+ return self.match_filters?(file_spec, self.excluded_dirs, subdirectory)
194
+
195
+ end
196
+ end
197
+
198
+ ##################################################################################
199
+ # Conveinence method that determines if an {FilterSet#excluded_dirs excluded_dirs} filter exists.
200
+ # @return [Boolean] Returns true if the {FilterSet#excluded_dirs excluded_dirs} filters exists, otherwise, false.
201
+ def has_excluded_dirs?
202
+ return self.excluded_dirs.blank? ? false : true
203
+ end
204
+
205
+ ##################################################################################
206
+ # Conveinence method that calls {FilterSet#match_filters? match_filters?} with the current value of {FilterSet#non_loadable non_loadable}
207
+ # - In this context, file_spec cannot be blank.
208
+ # - In this context, subdirectory CAN BE blank.
209
+ # - Will return true if self.non_loadable array is nil or empty.
210
+ # @param [String] file_spec See {FilterSet#match_filters? match_filters?}
211
+ # @param [String] subdirectory Subdirectory is any directory directly off of the watch root directory. Subdirectory CAN BE blank.
212
+ # @return [Boolean] Returns true if a match is found, otherwise, false
213
+ def non_loadable?(file_spec, subdirectory)
214
+ ducklog.system %(FilterSet.non_loadable?(#{file_spec}, #{self.non_loadable}, #{subdirectory}))
215
+ if file_spec.blank?
216
+ return false
217
+
218
+ else
219
+ return self.match_filters?(file_spec, self.non_loadable, subdirectory)
220
+
221
+ end
222
+ end
223
+
224
+ ##################################################################################
225
+ # Conveinence method that determines if an {FilterSet#non_loadable non_loadable} filter exists.
226
+ # @return [Boolean] Returns true if the {FilterSet#non_loadable non_loadable} filters exists, otherwise, false.
227
+ def has_non_loadable?
228
+ return self.non_loadable.blank? ? false : true
229
+ end
230
+
231
+ end
232
+ end
233
+ end
@@ -0,0 +1,331 @@
1
+ module DuckTest
2
+ # ...
3
+ module FrameWork
4
+
5
+ ##################################################################################
6
+ # {include:file:MAPS.md}
7
+ class Map
8
+ include DuckTest::ConfigHelper
9
+
10
+ # An Array of {Map} objects representing target runnables tests.
11
+ attr_accessor :maps
12
+
13
+ ##################################################################################
14
+ # Initialize a new Map. A Map can be initialized in multiple variations.
15
+ # sub_directory and file_name are defaulted to nil, therefore, those values are optional. However, the
16
+ # gotcha is that you cannot specify file_name without first specifying sub_directory which make the order of
17
+ # the arguments important. If you omit file_name or sub_directory and file_name initialize will recognize
18
+ # it by checking the sub_directory and file_name arguments. If either of them are a Hash, then, it assumes
19
+ # that value is the options Hash.
20
+ #
21
+ # # normal form
22
+ # Map.new(/models/, /bike/, watch_basedir: :app, runnable_basedir: :test)
23
+ #
24
+ # # specify sub_directory and options
25
+ # Map.new(/models/, watch_basedir: :app, runnable_basedir: :test)
26
+ #
27
+ # # options only
28
+ # Map.new(watch_basedir: :app, runnable_basedir: :test)
29
+ #
30
+ # Any value passed as part of the options Hash will override the preceding sub_directory or file_name value set by a normal argument.
31
+ #
32
+ # # specify all arguments as part of the options Hash
33
+ # Map.new(sub_directory: /models/, file_name: /bike/, watch_basedir: :app, runnable_basedir: :test)
34
+ #
35
+ # Since {DuckTest::Config} uses the Map class directly for creating Map objects this feature
36
+ # should allow developer to create maps in the config file like the following:
37
+ #
38
+ # DuckTest.config do
39
+ # watch "**/*" do
40
+ # map /models/, /bike/
41
+ # end
42
+ # end
43
+ #
44
+ # If a block is passed, it is executed against self. Therefore, all of the instance methods are available to be called within the block.
45
+ #
46
+ # Map.new(/^models/, /[a-z]_store/, watch_basedir: :app) do
47
+ # target /^unit/, /[a-z]_spec.rb/, watch_basedir: :spec
48
+ # target /^functional/, /[a-z]_controller_[a-z]/, watch_basedir: :test
49
+ # end
50
+ #
51
+ # @param [Proc, Regexp, Symbol, String] sub_directory See {#sub_directory}
52
+ # @param [Proc, Regexp, Symbol, String] file_name See {#file_name}
53
+ # @param [Hash] options An options Hash containing values used to initialize the object.
54
+ # @option options [String] :file_name See {#file_name}
55
+ # @option options [String] :runnable_basedir See {DuckTest::ConfigHelper#runnable_basedir}
56
+ # @option options [String] :sub_directory See {#sub_directory}
57
+ # @option options [String] :watch_basedir See {DuckTest::ConfigHelper#watch_basedir}
58
+ # @param [Block] &block A standard Ruby code block.
59
+ # @return [Map]
60
+ def initialize(sub_directory = nil, file_name = nil, options = {}, &block)
61
+ super()
62
+
63
+ options = sub_directory.kind_of?(Hash) ? sub_directory : options
64
+ options = file_name.kind_of?(Hash) ? file_name : options
65
+
66
+ self.watch_basedir = options[:watch_basedir]
67
+
68
+ self.file_name file_name unless file_name.blank? || file_name.kind_of?(Hash)
69
+ self.file_name options[:file_name] unless options[:file_name].blank?
70
+
71
+ self.maps = [] # don't accept a maps array as a parameter
72
+
73
+ self.sub_directory sub_directory unless sub_directory.blank? || sub_directory.kind_of?(Hash)
74
+ self.sub_directory options[:sub_directory] unless options[:sub_directory].blank?
75
+
76
+ self.runnable_basedir = options[:runnable_basedir]
77
+
78
+ self.instance_exec(&block) if block_given?
79
+ return self
80
+ end
81
+
82
+ ##################################################################################
83
+ # Creates a new Map object based on options and adds it to self. Must be called within a {#initialize} block.
84
+ #
85
+ # # in block form
86
+ # Map.new(/^models/, /[a-z]_store/, watch_basedir: :app).build do
87
+ # target /^functional/, /[a-z]_controller_[a-z]/ # runnable_basedir trickles down to watch_basedir
88
+ # target /^unit/, /[a-z]_spec.rb/, watch_basedir: :spec # overrides tickled runnable_basedir
89
+ # end
90
+ #
91
+ # It is important to note that the runnable_basedir is passed to the target block as watch_basedir. This allows
92
+ # you to specify a runnable_basedir to the Map object and have it trickle down to all of the targets eliminating the
93
+ # need to specify it for every target block. Also, {DuckTest::Config} will use this feature by allowing you to specify
94
+ # watch_basedir and runnable_basedir at the top of a config file and have those values be used for all mappings.
95
+ #
96
+ # @param [Hash] options See {#initialize}
97
+ # @param [Block] &block A standard Ruby code block.
98
+ # @return [Map] The recently created Map object.
99
+ def target(sub_directory = nil, file_name = nil, options = {}, &block)
100
+ options = sub_directory.kind_of?(Hash) ? sub_directory : options
101
+ options = file_name.kind_of?(Hash) ? file_name : options
102
+ config = {watch_basedir: self.runnable_basedir}.merge(options)
103
+ self.maps.push(Map.new(sub_directory, file_name, config, &block))
104
+ return self.maps.last
105
+ end
106
+
107
+ ##################################################################################
108
+ # @note See {Map} overview for details on how Regexp, String, Symbols are evaluated and a general understanding of mappings.
109
+ # Sets the current value of the file name expression.
110
+ # See {#file_name_match?}
111
+ # @param [Array, Proc, Regexp, String, Symbol] value An expression to compare against file names.
112
+ # If the value is an Array, then, file_name expects the Array to contain
113
+ # Procs, Regexps, Strings, or Symbols and not sub-arrays.
114
+ # @return [Array, Proc, Regexp, String, Symbol] value Returns the current value of the file_name expression.
115
+ def file_name(value = nil, &block)
116
+ @file_name ||= "all"
117
+ value = value.kind_of?(Symbol) ? value.to_s : value
118
+ if value.kind_of?(Array)
119
+ value.each_with_index {|item, index| value[index] = item.to_s if item.kind_of?(Symbol)}
120
+ end
121
+ @file_name = value unless value.blank?
122
+ @file_name = block if block_given?
123
+ return @file_name
124
+ end
125
+
126
+ ##################################################################################
127
+ # @note See {Map} overview for details on how Regexp, String, Symbols are evaluated and a general understanding of mappings.
128
+ # The current {#file_name} value should be an expression Proc, Regexp, or String. This value is compared against file names
129
+ # retrieved from the file system based on the pattern option of {DuckTest::Config#watch}.
130
+ #
131
+ # map = Map.new
132
+ # map.file_name(/^bike/)
133
+ # map.file_name_match?("bike_spec.rb") # => true
134
+ # map.file_name_match?("truck_spec.rb") # => false
135
+ #
136
+ # @param [String] value The file name being evaluated. Typically, this is the source or target file name.
137
+ # @param [String] cargo A value that is passed to the block if {#file_name} is a block.
138
+ # @return [Boolean] True if match, otherwise, false.
139
+ def file_name_match?(value, cargo = nil)
140
+ result = false
141
+ expressions = self.file_name.blank? ? "all" : self.file_name
142
+ expressions = expressions.kind_of?(Array) ? self.file_name : [self.file_name]
143
+
144
+ expressions.each do |expression|
145
+
146
+ if expression.kind_of?(Regexp)
147
+ result = value =~ expression
148
+
149
+ elsif expression.kind_of?(String)
150
+ result = value =~ /^#{expression}/ || expression.eql?("all")
151
+
152
+ elsif expression.kind_of?(Proc)
153
+ result = expression.call value, cargo
154
+
155
+ end
156
+
157
+ break if result
158
+ end
159
+
160
+ return result
161
+ end
162
+
163
+ ##################################################################################
164
+ # @note See {Map} overview for details on how Regexp, String, Symbols are evaluated and a general understanding of mappings.
165
+ # Sets the current value of the sub-directory expression.
166
+ # See {#sub_directory_match?}
167
+ # @param [Array, Proc, Regexp, String, Symbol] value An expression to compare against directory names.
168
+ # If the value is an Array, then, sub_directory expects the Array to contain
169
+ # Procs, Regexps, Strings, or Symbols and not sub-arrays.
170
+ # @return [Array, Proc, Regexp, String, Symbol] value Returns the current value of the sub_directory expression.
171
+ def sub_directory(value = nil, &block)
172
+ @sub_directory ||= "all"
173
+ value = value.kind_of?(Symbol) ? value.to_s : value
174
+ if value.kind_of?(Array)
175
+ value.each_with_index {|item, index| value[index] = item.to_s if item.kind_of?(Symbol)}
176
+ end
177
+ @sub_directory = value unless value.blank?
178
+ @sub_directory = block if block_given?
179
+ return @sub_directory
180
+ end
181
+
182
+ ##################################################################################
183
+ def match?(target)
184
+ value = false
185
+
186
+ if self.match.kind_of?(Proc)
187
+ value = self.match.call target
188
+ else
189
+ value = self.sub_directory_match?(target[:sub_directory]) && self.file_name_match?(target[:file_name]) ? true : value
190
+ end
191
+
192
+ return value
193
+ end
194
+
195
+ ##################################################################################
196
+ def match_target?(target, source)
197
+ value = false
198
+
199
+ if self.match.kind_of?(Proc)
200
+ value = self.match.call target, source
201
+ else
202
+ value = self.sub_directory_match?(target[:sub_directory]) && self.file_name_match?(target[:file_name], source[:file_name])
203
+ end
204
+
205
+ return value
206
+ end
207
+
208
+ ##################################################################################
209
+ def match(&block)
210
+ @match ||= nil
211
+ @match = block if block_given?
212
+ return @match
213
+ end
214
+
215
+ ##################################################################################
216
+ # @note See {Map} overview for details on how Regexp, String, Symbols are evaluated and a general understanding of mappings.
217
+ # The current {#sub_directory} value should be an expression Proc, Regexp, or String. This value is compared against directory paths
218
+ # retrieved from the file system based on the pattern option of {DuckTest::Config#watch}.
219
+ #
220
+ # Examples:
221
+ #
222
+ # map = Map.new sub_directory: :models
223
+ # map.sub_directory_match?("models") # => true
224
+ # map.sub_directory_match?("controllers") # => false
225
+ #
226
+ # map = Map.new sub_directory: "models"
227
+ # map.sub_directory_match?("models") # => true
228
+ # map.sub_directory_match?("controllers") # => false
229
+ #
230
+ # map = Map.new sub_directory: /models/
231
+ # map.sub_directory_match?("models") # => true
232
+ # map.sub_directory_match?("controllers") # => false
233
+ #
234
+ # map = Map.new sub_directory: :models, watch_basedir: :app
235
+ # map.sub_directory_match?("app/models") # => true
236
+ # map.sub_directory_match?("app/models/sec") # => true
237
+ # map.sub_directory_match?("my_app/models") # => false
238
+ # map.sub_directory_match?("my_app/models/sec") # => false
239
+ # map.sub_directory_match?("app/controllers") # => false
240
+ #
241
+ # map = Map.new sub_directory: "models", watch_basedir: :app
242
+ # map.sub_directory_match?("app/models") # => true
243
+ # map.sub_directory_match?("app/models/sec") # => true
244
+ # map.sub_directory_match?("my_app/models") # => false
245
+ # map.sub_directory_match?("my_app/models/sec") # => false
246
+ # map.sub_directory_match?("app/controllers") # => false
247
+ #
248
+ # map = Map.new sub_directory: /^models/, watch_basedir: :app
249
+ # map.sub_directory_match?("app/models") # => true
250
+ # map.sub_directory_match?("app/models/sec") # => true
251
+ # map.sub_directory_match?("my_app/models") # => false
252
+ # map.sub_directory_match?("my_app/models/sec") # => false
253
+ # map.sub_directory_match?("app/controllers") # => false
254
+ #
255
+ # map = Map.new sub_directory: /models/, watch_basedir: :app
256
+ # map.sub_directory_match?("app/models") # => true
257
+ # map.sub_directory_match?("app/models/sec") # => true
258
+ # map.sub_directory_match?("my_app/models") # => true
259
+ # map.sub_directory_match?("my_app/models/sec") # => true
260
+ # map.sub_directory_match?("app/controllers") # => false
261
+ #
262
+ # @param [String] value The file name being evaluated. Typically, this is the source or target file name.
263
+ # @param [String] cargo A value that is passed to the block if {#sub_directory} is a block.
264
+ # @return [Boolean] True if match, otherwise, false.
265
+ def sub_directory_match?(value, cargo = nil)
266
+ result = false
267
+ expressions = self.sub_directory.blank? ? "all" : self.sub_directory
268
+ expressions = expressions.kind_of?(Array) ? self.sub_directory : [self.sub_directory]
269
+
270
+ # the value being compared via this method is expecting to be a directory
271
+ # possibly containing path separators. The following expressions interrogate
272
+ # value for the existence of the current value of self.watch_basedir. If it exists, then,
273
+ # it is removed, otherwise, value is left in tact.
274
+ # The reason for this is that runnable and watch definitions require a pattern
275
+ # to retrieve files. The self.watch_basedir provides the developer with the conveience
276
+ # of not having to include the watch_basedir directory in all of the mappings.
277
+ # see the description and examples under the watch_basedir method.
278
+
279
+ # first look for watch_basedir with SEPARATOR appended to it: "models/"
280
+ unless self.watch_basedir.blank?
281
+ if value =~ /^#{%(#{self.watch_basedir}#{File::SEPARATOR})}/
282
+ value = value.gsub(%(#{self.watch_basedir}#{File::SEPARATOR}), "")
283
+
284
+ elsif value =~ /^#{self.watch_basedir}/
285
+ value = value.gsub(self.watch_basedir, "")
286
+
287
+ end
288
+ end
289
+
290
+ expressions.each do |expression|
291
+
292
+ if expression.kind_of?(Regexp)
293
+ result = value =~ expression
294
+
295
+ elsif expression.kind_of?(String)
296
+ result = value =~ /^#{expression}/ || expression.eql?("all")
297
+
298
+ elsif expression.kind_of?(Proc)
299
+ result = expression.call value, cargo
300
+
301
+ end
302
+
303
+ break if result
304
+ end
305
+
306
+ return result
307
+ end
308
+
309
+ ##################################################################################
310
+ # Determines if the current {Map} object is valid by examining the sub_directory and file_name values.
311
+ # Both value must not be nil to be valid.
312
+ # @return [Boolean] Returns true if valid, otherwise, false
313
+ def valid?
314
+ return !self.sub_directory.blank? && !self.file_name.blank?
315
+ end
316
+
317
+ ##################################################################################
318
+ # ...
319
+ def to_s(margin = "")
320
+ buffer = ""
321
+ self.maps.each do |item|
322
+ buffer = "\r\n#{margin} maps:" if buffer.blank?
323
+ buffer += "\r\n#{margin}#{item.to_s(' ')}"
324
+ end
325
+ buffer_basedir = self.watch_basedir.blank? ? "" : self.watch_basedir.ljust(15)
326
+ return "\r\n#{margin}watch_basedir: #{buffer_basedir} runnable_basedir: #{self.runnable_basedir}\r\n#{margin}sub_directory: #{self.sub_directory} file_name: #{self.file_name}#{buffer}"
327
+ end
328
+
329
+ end
330
+ end
331
+ end