right_scraper 1.0.26 → 3.0.0

Sign up to get free protection for your applications and to get access to all the features.
Files changed (92) hide show
  1. data/Gemfile +16 -0
  2. data/README.rdoc +9 -28
  3. data/Rakefile +51 -39
  4. data/lib/right_scraper/builders/base.rb +64 -0
  5. data/lib/right_scraper/builders/filesystem.rb +96 -0
  6. data/lib/right_scraper/builders/union.rb +57 -0
  7. data/lib/right_scraper/logger.rb +102 -0
  8. data/lib/right_scraper/loggers/noisy.rb +85 -0
  9. data/lib/right_scraper/processes/ssh.rb +188 -0
  10. data/lib/right_scraper/repositories/base.rb +299 -0
  11. data/lib/right_scraper/repositories/download.rb +90 -0
  12. data/lib/right_scraper/repositories/git.rb +92 -0
  13. data/lib/right_scraper/repositories/mock.rb +70 -0
  14. data/lib/right_scraper/repositories/svn.rb +96 -0
  15. data/lib/right_scraper/resources/base.rb +70 -0
  16. data/{spec/scraper_base_spec.rb → lib/right_scraper/resources/cookbook.rb} +9 -23
  17. data/lib/right_scraper/resources/workflow.rb +55 -0
  18. data/lib/right_scraper/retrievers/base.rb +114 -0
  19. data/lib/right_scraper/retrievers/checkout.rb +79 -0
  20. data/lib/right_scraper/retrievers/download.rb +97 -0
  21. data/lib/right_scraper/retrievers/git.rb +140 -0
  22. data/lib/right_scraper/retrievers/svn.rb +87 -0
  23. data/lib/right_scraper/scanners/base.rb +111 -0
  24. data/lib/right_scraper/scanners/cookbook_manifest.rb +59 -0
  25. data/lib/right_scraper/scanners/cookbook_metadata.rb +69 -0
  26. data/lib/right_scraper/scanners/cookbook_s3_upload.rb +84 -0
  27. data/lib/right_scraper/scanners/union.rb +89 -0
  28. data/lib/right_scraper/scanners/workflow_manifest.rb +86 -0
  29. data/lib/right_scraper/scanners/workflow_metadata.rb +70 -0
  30. data/lib/right_scraper/scanners/workflow_s3_upload.rb +85 -0
  31. data/lib/right_scraper/scraper.rb +81 -57
  32. data/lib/right_scraper/scraper_logger.rb +61 -0
  33. data/lib/right_scraper/scrapers/base.rb +262 -0
  34. data/lib/right_scraper/scrapers/cookbook.rb +73 -0
  35. data/lib/right_scraper/scrapers/workflow.rb +88 -0
  36. data/lib/right_scraper/svn_client.rb +101 -0
  37. data/lib/right_scraper/version.rb +28 -0
  38. data/lib/right_scraper.rb +35 -11
  39. data/right_scraper.gemspec +26 -13
  40. data/right_scraper.rconf +13 -0
  41. data/spec/builder_spec.rb +50 -0
  42. data/spec/cookbook_helper.rb +73 -0
  43. data/spec/cookbook_manifest_spec.rb +55 -0
  44. data/spec/cookbook_s3_upload_spec.rb +152 -0
  45. data/spec/download/download_retriever_spec.rb +118 -0
  46. data/spec/download/download_retriever_spec_helper.rb +72 -0
  47. data/spec/download/download_spec.rb +130 -0
  48. data/spec/download/multi_dir_spec.rb +106 -0
  49. data/spec/download/multi_dir_spec_helper.rb +40 -0
  50. data/spec/git/cookbook_spec.rb +166 -0
  51. data/spec/git/demokey +27 -0
  52. data/spec/git/demokey.pub +1 -0
  53. data/spec/git/password_key +30 -0
  54. data/spec/git/password_key.pub +1 -0
  55. data/spec/git/repository_spec.rb +110 -0
  56. data/spec/git/retriever_spec.rb +505 -0
  57. data/spec/git/retriever_spec_helper.rb +112 -0
  58. data/spec/git/scraper_spec.rb +136 -0
  59. data/spec/git/ssh_spec.rb +170 -0
  60. data/spec/git/url_spec.rb +103 -0
  61. data/spec/logger_spec.rb +185 -0
  62. data/spec/repository_spec.rb +89 -23
  63. data/spec/{scraper_spec_helper_base.rb → retriever_spec_helper.rb} +41 -27
  64. data/spec/scanner_spec.rb +61 -0
  65. data/spec/scraper_helper.rb +96 -0
  66. data/spec/scraper_spec.rb +123 -45
  67. data/spec/spec_helper.rb +87 -14
  68. data/spec/svn/cookbook_spec.rb +97 -0
  69. data/spec/svn/multi_svn_spec.rb +64 -0
  70. data/spec/svn/multi_svn_spec_helper.rb +40 -0
  71. data/spec/svn/repository_spec.rb +72 -0
  72. data/spec/svn/retriever_spec.rb +261 -0
  73. data/spec/svn/scraper_spec.rb +90 -0
  74. data/spec/svn/{svn_scraper_spec_helper.rb → svn_retriever_spec_helper.rb} +46 -27
  75. data/spec/svn/url_spec.rb +47 -0
  76. data/spec/url_spec.rb +164 -0
  77. metadata +203 -31
  78. data/lib/right_scraper/linux/process_monitor.rb +0 -84
  79. data/lib/right_scraper/repository.rb +0 -78
  80. data/lib/right_scraper/scraper_base.rb +0 -175
  81. data/lib/right_scraper/scrapers/download_scraper.rb +0 -67
  82. data/lib/right_scraper/scrapers/git_scraper.rb +0 -283
  83. data/lib/right_scraper/scrapers/svn_scraper.rb +0 -119
  84. data/lib/right_scraper/watcher.rb +0 -158
  85. data/lib/right_scraper/win32/process_monitor.rb +0 -98
  86. data/spec/download/download_scraper_spec.rb +0 -94
  87. data/spec/git/git_scraper_spec.rb +0 -165
  88. data/spec/git/git_scraper_spec_helper.rb +0 -72
  89. data/spec/rcov.opts +0 -1
  90. data/spec/spec.opts +0 -2
  91. data/spec/svn/svn_scraper_spec.rb +0 -148
  92. data/spec/watcher_spec.rb +0 -74
data/Gemfile ADDED
@@ -0,0 +1,16 @@
1
+ source "http://rubygems.org"
2
+
3
+ gem "json", "~> 1.4.5"
4
+ gem "blackwinter-git", "~> 1.2.7"
5
+ gem "libarchive", "~> 0.1.2"
6
+ gem "right_aws", "~> 2.0"
7
+ gem "process_watcher", "~> 0.3"
8
+
9
+ group :development do
10
+ gem "rspec", "~> 2.3"
11
+ gem "rake"
12
+ gem "flexmock"
13
+ gem "ruby-debug", :platform=>"ruby_18"
14
+ gem "ruby-debug19", :platform=>"ruby_19"
15
+ gem "rdoc", "~> 2.4"
16
+ end
data/README.rdoc CHANGED
@@ -4,24 +4,11 @@
4
4
 
5
5
  === Synopsis
6
6
 
7
- RightScraper provides a simple interface to download and keep repositories up-to-date
8
- using various protocols.
9
-
10
- The supported protocols include:
11
- - *git*: RightScraper will clone then pull repos from git
12
- - *SVN*: RightScraper will checkout then update SVN repositories
13
- - *tarballs*: Includes uncompressed (.tar), gzip (.tgz, .gzip) and bzip (.bzip, .bzip2) tar files.
14
-
15
- The scraper first inspects the local directory to see if the repo has already been scraped
16
- and if so runs some basic checks before it tries to update it. Incremental updates are not
17
- supported with tar files.
18
-
19
- === Rationale
20
-
21
- The idea is to have many repos that need to be downloaded/kept up-to-date in a central
22
- place. Point the scraper to this central place and it will take care of creating unique
23
- local directories for each remote repository and keep that mapping to download changes
24
- incrementally upon request.
7
+ RightScraper provides a simple interface to download and keep repositories up-to-date
8
+ using various protocols. It has been decomposed into various modules so that you
9
+ may specify only the functionality (and required libraries and gems) you require.
10
+ This gem depends on all available RightScraper modules, enabling full support at the
11
+ cost of requiring some systems administration work external to Ruby.
25
12
 
26
13
  == USAGE
27
14
 
@@ -30,22 +17,16 @@ incrementally upon request.
30
17
  require 'rubygems'
31
18
  require 'right_scraper'
32
19
 
33
- scraper = RightScale::Scraper.new('/tmp')
20
+ scraper = RightScale::Scraper.new(:basedir => '/tmp', :kind => :cookbook)
34
21
  scraper.scrape(:type => :git, :url => 'git://github.com/rightscale/right_scraper.git')
35
22
 
36
23
  == INSTALLATION
37
24
 
38
- RightScraper can be installed by entering the following at the command prompt:
25
+ This module for RightScraper can be installed by entering the following at the command
26
+ prompt:
39
27
 
40
28
  gem install right_scraper
41
29
 
42
- == DEPENDENCIES
43
-
44
- RightScraper relies on the following tools:
45
- - git
46
- - svn
47
- - curl
48
-
49
30
  == TESTING
50
31
 
51
32
  Install the following RubyGems required for testing:
@@ -59,7 +40,7 @@ The build can be tested using the RSpec gem.
59
40
 
60
41
  <b>RightScraper</b>
61
42
 
62
- Copyright:: Copyright (c) 2010 RightScale, Inc.
43
+ Copyright:: Copyright (c) 2010 RightScale, Inc.
63
44
 
64
45
  Permission is hereby granted, free of charge, to any person obtaining
65
46
  a copy of this software and associated documentation files (the
data/Rakefile CHANGED
@@ -1,18 +1,18 @@
1
- #--
2
- # Copyright: Copyright (c) 2010 RightScale, Inc.
1
+ #-- -*-ruby-*-
2
+ # Copyright: Copyright (c) 2010-2011 RightScale, Inc.
3
3
  #
4
4
  # Permission is hereby granted, free of charge, to any person obtaining
5
5
  # a copy of this software and associated documentation files (the
6
6
  # 'Software'), to deal in the Software without restriction, including
7
7
  # without limitation the rights to use, copy, modify, merge, publish,
8
- # distribute, sublicense, and/or sell copies of the Software, and to
8
+ # distribute, sublicense, and/or sell copies of the Software, and to
9
9
  # permit persons to whom the Software is furnished to do so, subject to
10
10
  # the following conditions:
11
11
  #
12
12
  # The above copyright notice and this permission notice shall be
13
13
  # included in all copies or substantial portions of the Software.
14
14
  #
15
- # THE SOFTWARE IS PROVIDED 'AS IS', WITHOUT WARRANTY OF ANY KIND,
15
+ # THE SOFTWARE IS PROVIDED 'AS IS', WITHOUT WARRANTY OF ANY KIND,
16
16
  # EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
17
17
  # MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
18
18
  # IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY
@@ -22,56 +22,68 @@
22
22
  #++
23
23
 
24
24
  require 'rubygems'
25
+ require 'rubygems/package_task'
26
+ require 'bundler/setup'
27
+
25
28
  require 'fileutils'
26
29
  require 'rake'
27
- require 'spec/rake/spectask'
30
+ require 'rspec/core/rake_task'
31
+ require 'rdoc/task'
32
+ require 'rake/clean'
28
33
 
29
34
  task :default => 'spec'
30
35
 
36
+ # == Gem packaging == #
37
+
38
+ desc "Build right_scraper gem"
39
+ Gem::PackageTask.new(Gem::Specification.load("right_scraper.gemspec")) do |package|
40
+ package.need_zip = true
41
+ package.need_tar = true
42
+ end
43
+
44
+ CLEAN.include('pkg')
45
+
31
46
  # == Unit Tests == #
32
47
 
33
- desc "Run unit tests"
34
- Spec::Rake::SpecTask.new do |t|
35
- t.spec_files = Dir['**/*_spec.rb']
36
- t.spec_opts = lambda do
37
- IO.readlines(File.join(File.dirname(__FILE__), 'spec', 'spec.opts')).map {|l| l.chomp.split " "}.flatten
38
- end
48
+ task :specs => :spec
49
+
50
+ # == Unit Tests == #
51
+
52
+ desc 'Run unit tests'
53
+ RSpec::Core::RakeTask.new do |t|
54
+ t.pattern = 'spec/**/*_spec.rb'
55
+ t.rspec_opts = ["--color", "--format", "nested"]
39
56
  end
40
57
 
41
- desc "Run unit tests with RCov"
42
- Spec::Rake::SpecTask.new(:rcov) do |t|
43
- t.spec_files = Dir['**/*_spec.rb']
44
- t.rcov = true
45
- t.rcov_opts = lambda do
46
- IO.readlines(File.join(File.dirname(__FILE__), 'spec', 'rcov.opts')).map {|l| l.chomp.split " "}.flatten
58
+ namespace :spec do
59
+ desc "Run unit tests with RCov"
60
+ RSpec::Core::RakeTask.new(:rcov) do |t|
61
+ t.pattern = '*/spec/**/*_spec.rb'
62
+ t.rcov = true
63
+ t.rcov_opts = %q[--exclude "spec"]
47
64
  end
48
- end
49
65
 
50
- desc "Print Specdoc for unit tests"
51
- Spec::Rake::SpecTask.new(:doc) do |t|
52
- t.spec_opts = ["--format", "specdoc", "--dry-run"]
53
- t.spec_files = Dir['**/*_spec.rb']
66
+ desc "Print Specdoc for unit tests"
67
+ RSpec::Core::RakeTask.new(:doc) do |t|
68
+ t.pattern = '*/spec/**/*_spec.rb'
69
+ t.rspec_opts = ["--format", "documentation"]
70
+ end
54
71
  end
55
72
 
56
- # == Gem Management == #
73
+ # == Documentation == #
57
74
 
58
- desc "Build right_scraper gem"
59
- task :gem do
60
- ruby 'right_scraper.gemspec'
61
- pkg_dir = File.join(File.dirname(__FILE__), 'pkg')
62
- FileUtils.mkdir_p(pkg_dir)
63
- FileUtils.mv(Dir.glob(File.join(File.dirname(__FILE__), '*.gem')), pkg_dir)
64
- end
75
+ desc "Generate API documentation to doc/rdocs/index.html"
76
+ RDoc::Task.new do |rd|
77
+ rd.rdoc_dir = 'doc/rdocs'
78
+ rd.main = 'README.rdoc'
79
+ rd.rdoc_files.include 'README.rdoc', 'lib/**/*.rb'
65
80
 
66
- desc 'Install the right_scraper library as a gem'
67
- task :install => [:gem] do
68
- file = Dir["pkg/*.gem"].last
69
- sh "gem install #{file}"
81
+ rd.options << '--all'
82
+ rd.options << '--diagram'
70
83
  end
71
84
 
72
- desc 'Uninstalls and reinstalls the right_scraper library as a gem'
73
- task :reinstall do
74
- sh "gem uninstall right_scraper"
75
- sh "rake install"
85
+ # == Emacs integration == #
86
+ desc "Rebuild TAGS file"
87
+ task :tags do
88
+ sh "rtags -R */{lib,spec}"
76
89
  end
77
-
@@ -0,0 +1,64 @@
1
+ #--
2
+ # Copyright: Copyright (c) 2010-2011 RightScale, Inc.
3
+ #
4
+ # Permission is hereby granted, free of charge, to any person obtaining
5
+ # a copy of this software and associated documentation files (the
6
+ # 'Software'), to deal in the Software without restriction, including
7
+ # without limitation the rights to use, copy, modify, merge, publish,
8
+ # distribute, sublicense, and/or sell copies of the Software, and to
9
+ # permit persons to whom the Software is furnished to do so, subject to
10
+ # the following conditions:
11
+ #
12
+ # The above copyright notice and this permission notice shall be
13
+ # included in all copies or substantial portions of the Software.
14
+ #
15
+ # THE SOFTWARE IS PROVIDED 'AS IS', WITHOUT WARRANTY OF ANY KIND,
16
+ # EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
17
+ # MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
18
+ # IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY
19
+ # CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT,
20
+ # TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE
21
+ # SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
22
+ #++
23
+
24
+ require File.expand_path(File.join(File.dirname(__FILE__), '..', 'logger'))
25
+
26
+ module RightScraper
27
+ module Builders
28
+ # Base class for building additional metadata from filesystem
29
+ # based checkouts. Subclasses should override #go, and possibly
30
+ # #new if they require additional arguments.
31
+ #
32
+ # The lifecycle for a builder is as follows:
33
+ # - builder = Builder.new (once)
34
+ # - builder.go(dir, resource) (many times)
35
+ # - builder.finish (once)
36
+ class Builder
37
+ # Create a new Builder. Recognizes options as given. Some
38
+ # options may be required, others optional. This class recognizes
39
+ # only :logger.
40
+ #
41
+ # === Options
42
+ # <tt>:logger</tt>:: Optional. Logger currently being used
43
+ #
44
+ # === Parameters
45
+ # options(Hash):: builder options
46
+ def initialize(options={})
47
+ @logger = options.fetch(:logger, Logger.new)
48
+ end
49
+
50
+ # Run builder for this resource.
51
+ #
52
+ # === Parameters
53
+ # dir(String):: directory resource exists at
54
+ # resource(Object):: resource instance being built
55
+ def go(dir, resource)
56
+ end
57
+
58
+ # Notification that all scans for this repository have
59
+ # completed.
60
+ def finish
61
+ end
62
+ end
63
+ end
64
+ end
@@ -0,0 +1,96 @@
1
+ #--
2
+ # Copyright: Copyright (c) 2010-2011 RightScale, Inc.
3
+ #
4
+ # Permission is hereby granted, free of charge, to any person obtaining
5
+ # a copy of this software and associated documentation files (the
6
+ # 'Software'), to deal in the Software without restriction, including
7
+ # without limitation the rights to use, copy, modify, merge, publish,
8
+ # distribute, sublicense, and/or sell copies of the Software, and to
9
+ # permit persons to whom the Software is furnished to do so, subject to
10
+ # the following conditions:
11
+ #
12
+ # The above copyright notice and this permission notice shall be
13
+ # included in all copies or substantial portions of the Software.
14
+ #
15
+ # THE SOFTWARE IS PROVIDED 'AS IS', WITHOUT WARRANTY OF ANY KIND,
16
+ # EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
17
+ # MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
18
+ # IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY
19
+ # CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT,
20
+ # TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE
21
+ # SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
22
+ #++
23
+
24
+ require File.expand_path(File.join(File.dirname(__FILE__), 'base'))
25
+
26
+ module RightScraper
27
+ module Builders
28
+ # Build metadata by scanning the filesystem.
29
+ class Filesystem < Builder
30
+
31
+ # Create a new filesystem scanner. In addition to the options
32
+ # recognized by Builder, this class recognizes <tt>:retriever</tt> and
33
+ # <tt>:scanner</tt>.
34
+ #
35
+ # === Options
36
+ # <tt>:scanner</tt>:: Required. Scanner currently being used
37
+ # <tt>:ignorable_paths</tt>:: Ignore directories whose name belong to this list
38
+ #
39
+ # === Parameters
40
+ # options(Hash):: scraper options
41
+ def initialize(options={})
42
+ super
43
+ @scanner = options.fetch(:scanner)
44
+ @ignorable_paths = options[:ignorable_paths]
45
+ end
46
+
47
+ # Tell the scanner we're done.
48
+ def finish
49
+ super
50
+ @scanner.finish
51
+ end
52
+
53
+ # Run builder for this resource.
54
+ #
55
+ # === Parameters
56
+ # dir(String):: directory resource exists at
57
+ # resource(Object):: resource instance being built
58
+ def go(dir, resource)
59
+ @logger.operation(:scanning_filesystem, "rooted at #{dir}") do
60
+ @scanner.begin(resource)
61
+ maybe_scan(Dir.new(dir), nil)
62
+ @scanner.end(resource)
63
+ end
64
+ end
65
+
66
+ def maybe_scan(directory, position)
67
+ if @scanner.notice_dir(position)
68
+ scan(directory, position)
69
+ end
70
+ end
71
+
72
+ # Scan the contents of directory.
73
+ #
74
+ # === Parameters
75
+ # directory(Dir):: directory to scan
76
+ # position(String):: relative pathname for _directory_ from root of resource
77
+ def scan(directory, position)
78
+ directory.each do |entry|
79
+ next if entry == '.' || entry == '..'
80
+ next if @ignorable_paths && @ignorable_paths.include?(entry)
81
+
82
+ fullpath = File.join(directory.path, entry)
83
+ relative_position = position ? File.join(position, entry) : entry
84
+
85
+ if File.directory?(fullpath)
86
+ maybe_scan(Dir.new(fullpath), relative_position)
87
+ else
88
+ @scanner.notice(relative_position) do
89
+ open(fullpath) {|f| f.read}
90
+ end
91
+ end
92
+ end
93
+ end
94
+ end
95
+ end
96
+ end
@@ -0,0 +1,57 @@
1
+ #--
2
+ # Copyright: Copyright (c) 2010-2011 RightScale, Inc.
3
+ #
4
+ # Permission is hereby granted, free of charge, to any person obtaining
5
+ # a copy of this software and associated documentation files (the
6
+ # 'Software'), to deal in the Software without restriction, including
7
+ # without limitation the rights to use, copy, modify, merge, publish,
8
+ # distribute, sublicense, and/or sell copies of the Software, and to
9
+ # permit persons to whom the Software is furnished to do so, subject to
10
+ # the following conditions:
11
+ #
12
+ # The above copyright notice and this permission notice shall be
13
+ # included in all copies or substantial portions of the Software.
14
+ #
15
+ # THE SOFTWARE IS PROVIDED 'AS IS', WITHOUT WARRANTY OF ANY KIND,
16
+ # EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
17
+ # MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
18
+ # IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY
19
+ # CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT,
20
+ # TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE
21
+ # SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
22
+ #++
23
+
24
+ module RightScraper
25
+ module Builders
26
+ # Union builder, to permit running multiple builders in sequence
27
+ # with the same interface as running one.
28
+ class Union
29
+ # (Array) subcomponents of this union
30
+ attr_reader :subbuilders
31
+
32
+ # Create a new union builder. Recognizes no new options.
33
+ #
34
+ # === Parameters
35
+ # classes(List):: List of Builder classes to run
36
+ # options(Hash):: options to initialize each Builder with
37
+ def initialize(classes, options={})
38
+ @subbuilders = classes.map {|klass| klass.new(options)}
39
+ end
40
+
41
+ # Run each builder for this resource.
42
+ #
43
+ # === Parameters
44
+ # dir(String):: directory resource exists at
45
+ # resource(RightScraper::Resources::Base):: resource instance being built
46
+ def go(dir, resource)
47
+ @subbuilders.each {|builder| builder.go(dir, resource)}
48
+ end
49
+
50
+ # Notify subbuilders that all scans for this repository have
51
+ # completed.
52
+ def finish
53
+ @subbuilders.each {|builder| builder.finish}
54
+ end
55
+ end
56
+ end
57
+ end
@@ -0,0 +1,102 @@
1
+ #--
2
+ # Copyright: Copyright (c) 2010-2011 RightScale, Inc.
3
+ #
4
+ # Permission is hereby granted, free of charge, to any person obtaining
5
+ # a copy of this software and associated documentation files (the
6
+ # 'Software'), to deal in the Software without restriction, including
7
+ # without limitation the rights to use, copy, modify, merge, publish,
8
+ # distribute, sublicense, and/or sell copies of the Software, and to
9
+ # permit persons to whom the Software is furnished to do so, subject to
10
+ # the following conditions:
11
+ #
12
+ # The above copyright notice and this permission notice shall be
13
+ # included in all copies or substantial portions of the Software.
14
+ #
15
+ # THE SOFTWARE IS PROVIDED 'AS IS', WITHOUT WARRANTY OF ANY KIND,
16
+ # EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
17
+ # MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
18
+ # IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY
19
+ # CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT,
20
+ # TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE
21
+ # SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
22
+ #++
23
+
24
+ require 'logger'
25
+
26
+ module RightScraper
27
+ # Very simplistic logger for scraper operations.
28
+ class Logger < ::Logger
29
+ # If no arguments, create a Logger that logs to nothing.
30
+ # Otherwise pass the arguments on to ::Logger.
31
+ def initialize(*args)
32
+ if args.empty?
33
+ super('/dev/null')
34
+ self.level = ::Logger::ERROR
35
+ else
36
+ super
37
+ end
38
+ @exceptional = false
39
+ end
40
+
41
+ # (RightScraper::Repositories::Base) Repository currently being examined.
42
+ attr_writer :repository
43
+
44
+ # Begin an operation that merits logging. Will call #note_error
45
+ # if an exception is raised during the operation.
46
+ #
47
+ # === Parameters
48
+ # type(Symbol):: operation type identifier
49
+ # explanation(String):: optional explanation
50
+ def operation(type, explanation="")
51
+ begin
52
+ note_phase(:begin, type, explanation)
53
+ result = yield
54
+ note_phase(:commit, type, explanation)
55
+ result
56
+ rescue
57
+ note_phase(:abort, type, explanation, $!)
58
+ raise
59
+ end
60
+ end
61
+
62
+ # Note an event to the log. In this base class this calls
63
+ # note_error when an error occurs, but subclasses will presumably
64
+ # want to override it.
65
+ #
66
+ # === Parameters
67
+ # phase(Symbol):: phase of operation; one of :begin, :commit, :abort
68
+ # type(Symbol):: operation type identifier
69
+ # explanation(String):: explanation of operation
70
+ # exception(Exception):: optional exception (only if +phase+ is :abort)
71
+ def note_phase(phase, type, explanation, exception=nil)
72
+ case phase
73
+ when :begin then @exceptional = false
74
+ when :abort then
75
+ unless @exceptional
76
+ note_error(exception, type, explanation)
77
+ @exceptional = true
78
+ end
79
+ end
80
+ end
81
+
82
+ # Log an error, with the given exception and explanation of what
83
+ # was going on.
84
+ #
85
+ # === Parameters
86
+ # exception(Exception):: exception raised
87
+ # type(Symbol):: operation type identifier
88
+ # explanation(String):: optional explanation
89
+ def note_error(exception, type, explanation="")
90
+ error("Saw #{exception} during #{type}#{maybe_explain(explanation)}")
91
+ end
92
+
93
+ protected
94
+ def maybe_explain(explanation)
95
+ if explanation.nil? || explanation.empty?
96
+ ""
97
+ else
98
+ ": #{explanation}"
99
+ end
100
+ end
101
+ end
102
+ end
@@ -0,0 +1,85 @@
1
+ #--
2
+ # Copyright: Copyright (c) 2010 RightScale, Inc.
3
+ #
4
+ # Permission is hereby granted, free of charge, to any person obtaining
5
+ # a copy of this software and associated documentation files (the
6
+ # 'Software'), to deal in the Software without restriction, including
7
+ # without limitation the rights to use, copy, modify, merge, publish,
8
+ # distribute, sublicense, and/or sell copies of the Software, and to
9
+ # permit persons to whom the Software is furnished to do so, subject to
10
+ # the following conditions:
11
+ #
12
+ # The above copyright notice and this permission notice shall be
13
+ # included in all copies or substantial portions of the Software.
14
+ #
15
+ # THE SOFTWARE IS PROVIDED 'AS IS', WITHOUT WARRANTY OF ANY KIND,
16
+ # EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
17
+ # MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
18
+ # IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY
19
+ # CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT,
20
+ # TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE
21
+ # SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
22
+ #++
23
+
24
+ require File.expand_path(File.join(File.dirname(__FILE__), '..', 'logger'))
25
+
26
+ module RightScraper
27
+ module Loggers
28
+ # A very noisy logger, useful for debugging.
29
+ class NoisyLogger < Logger
30
+ # Initialize the logger, setting the current operation depth to 1.
31
+ def initialize(*args)
32
+ super
33
+ @pending = []
34
+ end
35
+
36
+ # Note an event to the log, including a visual indicator of how
37
+ # many nested operations are currently pending.
38
+ #
39
+ # === Parameters
40
+ # phase(Symbol):: phase of operation; one of :begin, :commit, :abort
41
+ # type(Symbol):: operation type identifier
42
+ # explanation(String):: explanation of operation
43
+ # exception(Exception):: optional exception (only if +phase+ is :abort)
44
+ def note_phase(phase, type, explanation, exception=nil)
45
+ if phase == :begin
46
+ @pending.push [type, explanation]
47
+ end
48
+ super
49
+ debug("#{depth_str} #{phase} #{immediate_context}")
50
+ unless phase == :begin
51
+ @pending.pop
52
+ end
53
+ end
54
+
55
+ def note_error(exception, type, explanation="")
56
+ recordedtype, recordedexplanation = @pending[-1]
57
+ if recordedtype != type || recordedexplanation != explanation
58
+ @pending.push [type, explanation]
59
+ end
60
+ error("Saw #{exception} during #{context}")
61
+ if recordedtype != type || recordedexplanation != explanation
62
+ @pending.pop
63
+ end
64
+ end
65
+
66
+ private
67
+ def depth_str
68
+ '>' * @pending.size
69
+ end
70
+
71
+ def context
72
+ @pending.reverse.map {|pair| contextify(pair)}.join(" in ")
73
+ end
74
+
75
+ def immediate_context
76
+ contextify(@pending[-1])
77
+ end
78
+
79
+ def contextify(pair)
80
+ type, explanation = pair
81
+ "#{type}#{maybe_explain(explanation)}"
82
+ end
83
+ end
84
+ end
85
+ end