wtch 0.0.1

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/bin/wtch ADDED
@@ -0,0 +1,11 @@
1
+ #!/usr/bin/env ruby
2
+
3
+ require 'wtch'
4
+ require 'wtch/cli'
5
+
6
+ begin
7
+ Wtch::CLI.start
8
+ rescue Interrupt
9
+ Wtch.ui.error "\nQuitting..."
10
+ exit 1
11
+ end
data/lib/wtch.rb ADDED
@@ -0,0 +1,34 @@
1
+ require 'mechanize'
2
+
3
+ module Wtch
4
+ autoload :UI, 'wtch/ui'
5
+
6
+ class << self
7
+ attr_writer :ui
8
+
9
+ def ui
10
+ @ui ||= UI.new
11
+ end
12
+
13
+ def watch(url)
14
+ agent = Mechanize.new
15
+ stack = agent.get(url).links
16
+
17
+ while l = stack.pop
18
+ begin
19
+ next unless l.uri.host.nil? || l.uri.host == agent.history.first.uri.host
20
+ unless agent.visited? l.href
21
+ stack.push(*(agent.click(l).links))
22
+ Wtch.ui.info l.uri
23
+ end
24
+ rescue Errno::ECONNREFUSED, Mechanize::ResponseCodeError, ArgumentError, URI::InvalidComponentError, URI::InvalidURIError, NoMethodError, Mechanize::UnsupportedSchemeError => e
25
+ begin
26
+ Wtch.ui.error "#{e.message} (#{l.uri})"
27
+ rescue
28
+ Wtch.ui.error "#{e.message} (#{l.inspect})"
29
+ end
30
+ end
31
+ end
32
+ end
33
+ end
34
+ end
data/lib/wtch/cli.rb ADDED
@@ -0,0 +1,32 @@
1
+ $:.unshift File.expand_path('../vendor', __FILE__)
2
+ require 'thor'
3
+ require 'thor/actions'
4
+ require 'rubygems/config_file'
5
+
6
+ module Wtch
7
+ class CLI < Thor
8
+ include Thor::Actions
9
+
10
+ def initialize(*)
11
+ super
12
+ use_shell = options["no-color"] ? Thor::Shell::Basic.new : shell
13
+
14
+ Wtch.ui = UI::Shell.new(use_shell)
15
+ Gem::DefaultUserInteraction.ui = UI::RGProxy.new(Wtch.ui)
16
+ end
17
+
18
+ check_unknown_options! unless ARGV.include?("exec") || ARGV.include?("config")
19
+
20
+ default_task :watch
21
+ class_option "no-color", :type => :boolean, :banner => "Disable colorization in output"
22
+
23
+
24
+ desc "watch URL", "Watch an url"
25
+ method_option "url", :type => :string, :banner => "Url to use"
26
+
27
+ def watch(url)
28
+ Wtch.ui.info "Checking out #{url}"
29
+ Wtch.watch(url)
30
+ end
31
+ end
32
+ end
data/lib/wtch/ui.rb ADDED
@@ -0,0 +1,60 @@
1
+ module Wtch
2
+ class UI
3
+ def warn(message)
4
+ end
5
+
6
+ def error(message)
7
+ end
8
+
9
+ def info(message)
10
+ end
11
+
12
+ def confirm(message)
13
+ end
14
+
15
+ class Shell < UI
16
+ def initialize(shell)
17
+ @shell = shell
18
+ @quiet = false
19
+ end
20
+
21
+ def debug(msg)
22
+ @shell.say(msg) if ENV['DEBUG'] && !@quiet
23
+ end
24
+
25
+ def info(msg)
26
+ @shell.say(msg) if !@quiet
27
+ end
28
+
29
+ def confirm(msg)
30
+ @shell.say(msg, :green) if !@quiet
31
+ end
32
+
33
+ def warn(msg)
34
+ @shell.say(msg, :yellow)
35
+ end
36
+
37
+ def error(msg)
38
+ @shell.say(msg, :red)
39
+ end
40
+
41
+ def be_quiet!
42
+ @quiet = true
43
+ end
44
+ end
45
+
46
+ class RGProxy < Gem::SilentUI
47
+ def initialize(ui)
48
+ @ui = ui
49
+ end
50
+
51
+ def say(message)
52
+ if message =~ /native extensions/
53
+ @ui.info "with native extensions "
54
+ else
55
+ @ui.debug(message)
56
+ end
57
+ end
58
+ end
59
+ end
60
+ end
@@ -0,0 +1,319 @@
1
+ require 'thor/base'
2
+
3
+ class Thor
4
+ class << self
5
+ # Sets the default task when thor is executed without an explicit task to be called.
6
+ #
7
+ # ==== Parameters
8
+ # meth<Symbol>:: name of the defaut task
9
+ #
10
+ def default_task(meth=nil)
11
+ case meth
12
+ when :none
13
+ @default_task = 'help'
14
+ when nil
15
+ @default_task ||= from_superclass(:default_task, 'help')
16
+ else
17
+ @default_task = meth.to_s
18
+ end
19
+ end
20
+
21
+ # Defines the usage and the description of the next task.
22
+ #
23
+ # ==== Parameters
24
+ # usage<String>
25
+ # description<String>
26
+ # options<String>
27
+ #
28
+ def desc(usage, description, options={})
29
+ if options[:for]
30
+ task = find_and_refresh_task(options[:for])
31
+ task.usage = usage if usage
32
+ task.description = description if description
33
+ else
34
+ @usage, @desc, @hide = usage, description, options[:hide] || false
35
+ end
36
+ end
37
+
38
+ # Defines the long description of the next task.
39
+ #
40
+ # ==== Parameters
41
+ # long description<String>
42
+ #
43
+ def long_desc(long_description, options={})
44
+ if options[:for]
45
+ task = find_and_refresh_task(options[:for])
46
+ task.long_description = long_description if long_description
47
+ else
48
+ @long_desc = long_description
49
+ end
50
+ end
51
+
52
+ # Maps an input to a task. If you define:
53
+ #
54
+ # map "-T" => "list"
55
+ #
56
+ # Running:
57
+ #
58
+ # thor -T
59
+ #
60
+ # Will invoke the list task.
61
+ #
62
+ # ==== Parameters
63
+ # Hash[String|Array => Symbol]:: Maps the string or the strings in the array to the given task.
64
+ #
65
+ def map(mappings=nil)
66
+ @map ||= from_superclass(:map, {})
67
+
68
+ if mappings
69
+ mappings.each do |key, value|
70
+ if key.respond_to?(:each)
71
+ key.each {|subkey| @map[subkey] = value}
72
+ else
73
+ @map[key] = value
74
+ end
75
+ end
76
+ end
77
+
78
+ @map
79
+ end
80
+
81
+ # Declares the options for the next task to be declared.
82
+ #
83
+ # ==== Parameters
84
+ # Hash[Symbol => Object]:: The hash key is the name of the option and the value
85
+ # is the type of the option. Can be :string, :array, :hash, :boolean, :numeric
86
+ # or :required (string). If you give a value, the type of the value is used.
87
+ #
88
+ def method_options(options=nil)
89
+ @method_options ||= {}
90
+ build_options(options, @method_options) if options
91
+ @method_options
92
+ end
93
+
94
+ # Adds an option to the set of method options. If :for is given as option,
95
+ # it allows you to change the options from a previous defined task.
96
+ #
97
+ # def previous_task
98
+ # # magic
99
+ # end
100
+ #
101
+ # method_option :foo => :bar, :for => :previous_task
102
+ #
103
+ # def next_task
104
+ # # magic
105
+ # end
106
+ #
107
+ # ==== Parameters
108
+ # name<Symbol>:: The name of the argument.
109
+ # options<Hash>:: Described below.
110
+ #
111
+ # ==== Options
112
+ # :desc - Description for the argument.
113
+ # :required - If the argument is required or not.
114
+ # :default - Default value for this argument. It cannot be required and have default values.
115
+ # :aliases - Aliases for this option.
116
+ # :type - The type of the argument, can be :string, :hash, :array, :numeric or :boolean.
117
+ # :banner - String to show on usage notes.
118
+ #
119
+ def method_option(name, options={})
120
+ scope = if options[:for]
121
+ find_and_refresh_task(options[:for]).options
122
+ else
123
+ method_options
124
+ end
125
+
126
+ build_option(name, options, scope)
127
+ end
128
+
129
+ # Prints help information for the given task.
130
+ #
131
+ # ==== Parameters
132
+ # shell<Thor::Shell>
133
+ # task_name<String>
134
+ #
135
+ def task_help(shell, task_name)
136
+ meth = normalize_task_name(task_name)
137
+ task = all_tasks[meth]
138
+ handle_no_task_error(meth) unless task
139
+
140
+ shell.say "Usage:"
141
+ shell.say " #{banner(task)}"
142
+ shell.say
143
+ class_options_help(shell, nil => task.options.map { |_, o| o })
144
+ if task.long_description
145
+ shell.say "Description:"
146
+ shell.print_wrapped(task.long_description, :ident => 2)
147
+ else
148
+ shell.say task.description
149
+ end
150
+ end
151
+
152
+ # Prints help information for this class.
153
+ #
154
+ # ==== Parameters
155
+ # shell<Thor::Shell>
156
+ #
157
+ def help(shell, subcommand = false)
158
+ list = printable_tasks(true, subcommand)
159
+ Thor::Util.thor_classes_in(self).each do |klass|
160
+ list += klass.printable_tasks(false)
161
+ end
162
+ list.sort!{ |a,b| a[0] <=> b[0] }
163
+
164
+ shell.say "Tasks:"
165
+ shell.print_table(list, :ident => 2, :truncate => true)
166
+ shell.say
167
+ class_options_help(shell)
168
+ end
169
+
170
+ # Returns tasks ready to be printed.
171
+ def printable_tasks(all = true, subcommand = false)
172
+ (all ? all_tasks : tasks).map do |_, task|
173
+ next if task.hidden?
174
+ item = []
175
+ item << banner(task, false, subcommand)
176
+ item << (task.description ? "# #{task.description.gsub(/\s+/m,' ')}" : "")
177
+ item
178
+ end.compact
179
+ end
180
+
181
+ def subcommands
182
+ @subcommands ||= from_superclass(:subcommands, [])
183
+ end
184
+
185
+ def subcommand(subcommand, subcommand_class)
186
+ self.subcommands << subcommand.to_s
187
+ subcommand_class.subcommand_help subcommand
188
+ define_method(subcommand) { |*args| invoke subcommand_class, args }
189
+ end
190
+
191
+ # Extend check unknown options to accept a hash of conditions.
192
+ #
193
+ # === Parameters
194
+ # options<Hash>: A hash containing :only and/or :except keys
195
+ def check_unknown_options!(options={})
196
+ @check_unknown_options ||= Hash.new
197
+ options.each do |key, value|
198
+ if value
199
+ @check_unknown_options[key] = Array(value)
200
+ else
201
+ @check_unknown_options.delete(key)
202
+ end
203
+ end
204
+ @check_unknown_options
205
+ end
206
+
207
+ # Overwrite check_unknown_options? to take subcommands and options into account.
208
+ def check_unknown_options?(config) #:nodoc:
209
+ options = check_unknown_options
210
+ return false unless options
211
+
212
+ task = config[:current_task]
213
+ return true unless task
214
+
215
+ name = task.name
216
+
217
+ if subcommands.include?(name)
218
+ false
219
+ elsif options[:except]
220
+ !options[:except].include?(name.to_sym)
221
+ elsif options[:only]
222
+ options[:only].include?(name.to_sym)
223
+ else
224
+ true
225
+ end
226
+ end
227
+
228
+ protected
229
+
230
+ # The method responsible for dispatching given the args.
231
+ def dispatch(meth, given_args, given_opts, config) #:nodoc:
232
+ meth ||= retrieve_task_name(given_args)
233
+ task = all_tasks[normalize_task_name(meth)]
234
+
235
+ if task
236
+ args, opts = Thor::Options.split(given_args)
237
+ else
238
+ args, opts = given_args, nil
239
+ task = Thor::DynamicTask.new(meth)
240
+ end
241
+
242
+ opts = given_opts || opts || []
243
+ config.merge!(:current_task => task, :task_options => task.options)
244
+
245
+ trailing = args[Range.new(arguments.size, -1)]
246
+ new(args, opts, config).invoke_task(task, trailing || [])
247
+ end
248
+
249
+ # The banner for this class. You can customize it if you are invoking the
250
+ # thor class by another ways which is not the Thor::Runner. It receives
251
+ # the task that is going to be invoked and a boolean which indicates if
252
+ # the namespace should be displayed as arguments.
253
+ #
254
+ def banner(task, namespace = nil, subcommand = false)
255
+ base = File.basename($0).split(" ").first
256
+ "#{base} #{task.formatted_usage(self, $thor_runner, subcommand)}"
257
+ end
258
+
259
+ def baseclass #:nodoc:
260
+ Thor
261
+ end
262
+
263
+ def create_task(meth) #:nodoc:
264
+ if @usage && @desc
265
+ base_class = @hide ? Thor::HiddenTask : Thor::Task
266
+ tasks[meth] = base_class.new(meth, @desc, @long_desc, @usage, method_options)
267
+ @usage, @desc, @long_desc, @method_options, @hide = nil
268
+ true
269
+ elsif self.all_tasks[meth] || meth == "method_missing"
270
+ true
271
+ else
272
+ puts "[WARNING] Attempted to create task #{meth.inspect} without usage or description. " <<
273
+ "Call desc if you want this method to be available as task or declare it inside a " <<
274
+ "no_tasks{} block. Invoked from #{caller[1].inspect}."
275
+ false
276
+ end
277
+ end
278
+
279
+ def initialize_added #:nodoc:
280
+ class_options.merge!(method_options)
281
+ @method_options = nil
282
+ end
283
+
284
+ # Retrieve the task name from given args.
285
+ def retrieve_task_name(args) #:nodoc:
286
+ meth = args.first.to_s unless args.empty?
287
+
288
+ if meth && (map[meth] || meth !~ /^\-/)
289
+ args.shift
290
+ else
291
+ nil
292
+ end
293
+ end
294
+
295
+ # Receives a task name (can be nil), and try to get a map from it.
296
+ # If a map can't be found use the sent name or the default task.
297
+ def normalize_task_name(meth) #:nodoc:
298
+ meth = map[meth.to_s] || meth || default_task
299
+ meth.to_s.gsub('-','_') # treat foo-bar > foo_bar
300
+ end
301
+
302
+ def subcommand_help(cmd)
303
+ desc "help [COMMAND]", "Describe subcommands or one specific subcommand"
304
+ class_eval <<-RUBY
305
+ def help(task = nil, subcommand = true); super; end
306
+ RUBY
307
+ end
308
+
309
+ end
310
+
311
+ include Thor::Base
312
+
313
+ map HELP_MAPPINGS => :help
314
+
315
+ desc "help [TASK]", "Describe available tasks or one specific task"
316
+ def help(task = nil, subcommand = false)
317
+ task ? self.class.task_help(shell, task) : self.class.help(shell, subcommand)
318
+ end
319
+ end