wombat 0.1.6 → 0.2.0

Sign up to get free protection for your applications and to get access to all the features.
data/Gemfile CHANGED
@@ -10,6 +10,7 @@ group :development, :test do
10
10
  gem 'jeweler'
11
11
  gem 'rspec'
12
12
  gem 'guard'
13
+ gem 'growl_notify'
13
14
  gem 'guard-rspec'
14
15
  gem 'guard-bundler'
15
16
  gem 'vcr', '2.0.0.rc1'
data/Gemfile.lock CHANGED
@@ -8,6 +8,8 @@ GEM
8
8
  fakeweb (1.3.0)
9
9
  ffi (1.0.11)
10
10
  git (1.2.5)
11
+ growl_notify (0.0.3)
12
+ rb-appscript
11
13
  guard (0.9.4)
12
14
  ffi (>= 0.5.0)
13
15
  thor (~> 0.14.6)
@@ -32,6 +34,7 @@ GEM
32
34
  nokogiri (1.5.0)
33
35
  ntlm-http (0.1.1)
34
36
  rake (0.9.2.2)
37
+ rb-appscript (0.6.1)
35
38
  rspec (2.7.0)
36
39
  rspec-core (~> 2.7.0)
37
40
  rspec-expectations (~> 2.7.0)
@@ -56,6 +59,7 @@ DEPENDENCIES
56
59
  activesupport
57
60
  bundler
58
61
  fakeweb
62
+ growl_notify
59
63
  guard
60
64
  guard-bundler
61
65
  guard-rspec
data/README.md CHANGED
@@ -8,6 +8,8 @@ Generic Web crawler with a DSL that parses structured data from web pages.
8
8
 
9
9
  ``gem install wombat``
10
10
 
11
+ Creating a crawler:
12
+
11
13
  ```ruby
12
14
 
13
15
  # => github_crawler.rb
@@ -37,8 +39,11 @@ class GithubCrawler
37
39
  end
38
40
  end
39
41
  ```
42
+
43
+ Running it:
44
+
40
45
  ```ruby
41
- irb> SampleCrawler.new.crawl
46
+ irb> GithubCrawler.new.crawl
42
47
  =>
43
48
  {
44
49
  "headline" => "1,316,633 people hosting over 3,951,378 git repositories",
data/VERSION CHANGED
@@ -1 +1 @@
1
- 0.1.6
1
+ 0.2.0
@@ -21,12 +21,7 @@ module Wombat
21
21
 
22
22
  module ClassMethods
23
23
  def method_missing method, *args, &block
24
- if args.empty? && block
25
- metadata["#{method.to_s}"] = PropertyContainer.new unless metadata["#{method.to_s}"]
26
- block.call(metadata["#{method.to_s}"])
27
- else
28
- metadata.send method, *args, &block
29
- end
24
+ metadata.send method, *args, &block
30
25
  end
31
26
 
32
27
  def for_each selector, &block
@@ -4,6 +4,7 @@ module Wombat
4
4
 
5
5
  def initialize selector
6
6
  @selector = selector
7
+ super()
7
8
  end
8
9
  end
9
10
  end
@@ -4,12 +4,6 @@ require 'wombat/iterator'
4
4
 
5
5
  module Wombat
6
6
  class Metadata < PropertyContainer
7
- attr_accessor :iterators
8
-
9
- def initialize
10
- @iterators = []
11
- end
12
-
13
7
  def base_url url
14
8
  self[:base_url] = url
15
9
  end
@@ -17,11 +11,5 @@ module Wombat
17
11
  def list_page url
18
12
  self[:list_page] = url
19
13
  end
20
-
21
- def for_each selector
22
- Iterator.new(selector).tap do |i|
23
- iterators << i
24
- end
25
- end
26
14
  end
27
15
  end
@@ -0,0 +1,10 @@
1
+ module Wombat
2
+ module NodeSelector
3
+ def select_nodes selector, namespaces = nil
4
+ return [selector.to_s] if selector.is_a? Symbol
5
+ return context.xpath selector[6..-1], namespaces if selector.start_with? "xpath="
6
+ return context.css selector[4..-1] if selector.start_with? "css="
7
+ nil
8
+ end
9
+ end
10
+ end
data/lib/wombat/parser.rb CHANGED
@@ -12,14 +12,24 @@ module Wombat
12
12
  end
13
13
 
14
14
  def parse metadata
15
- @context = @mechanize.get("#{metadata[:base_url]}#{metadata[:list_page]}").parser
15
+ self.context = @mechanize.get("#{metadata[:base_url]}#{metadata[:list_page]}").parser
16
+ original_context = self.context
16
17
 
17
- props = metadata.all_properties
18
+ metadata.iterators.each do |it|
19
+ select_nodes(it.selector).each do |n|
20
+ self.context = n
21
+ it.all_properties.each do |p|
22
+ p.result ||= []
23
+ p.result << locate_first(p)
24
+ end
25
+ end
26
+ end
18
27
 
19
- locate props
28
+ self.context = original_context
20
29
 
21
- props.each do |p|
22
- p.result = p.callback.call(p.result) if p.callback
30
+ metadata.all_properties.each do |p|
31
+ result = locate_first p
32
+ p.result = p.callback ? p.callback.call(result) : result
23
33
  end
24
34
 
25
35
  metadata.flatten
@@ -9,5 +9,9 @@ module Wombat
9
9
  @namespaces = options[:namespaces]
10
10
  @callback = options[:callback]
11
11
  end
12
+
13
+ def flatten
14
+ result
15
+ end
12
16
  end
13
17
  end
@@ -2,42 +2,54 @@
2
2
 
3
3
  module Wombat
4
4
  class PropertyContainer < Hash
5
- def add_property property
6
- self[property.name] = property
7
- end
5
+ attr_accessor :iterators
8
6
 
9
- def get_property name
10
- self[name]
7
+ def initialize
8
+ @iterators = []
11
9
  end
12
10
 
13
11
  def method_missing method, *args, &block
14
- self[method.to_s] = Property.new(
15
- name: method.to_s,
16
- selector: args.first,
17
- format: args[1],
18
- namespaces: args[2],
19
- callback: block)
12
+ if args.empty? && block
13
+ self["#{method.to_s}"] = PropertyContainer.new unless self["#{method.to_s}"]
14
+ block.call(self["#{method.to_s}"])
15
+ else
16
+ self[method.to_s] = Property.new(
17
+ name: method.to_s,
18
+ selector: args.first,
19
+ format: args[1],
20
+ namespaces: args[2],
21
+ callback: block)
22
+ end
23
+ end
24
+
25
+ def to_ary
20
26
  end
21
27
 
22
28
  def all_properties
23
29
  values.flat_map { |v|
24
- v.kind_of?(PropertyContainer) \
25
- ? v.all_properties \
26
- : v.kind_of?(Property) \
27
- ? v \
28
- : nil
30
+ if v.kind_of? PropertyContainer
31
+ v.all_properties
32
+ elsif v.kind_of? Property
33
+ v
34
+ else
35
+ nil
36
+ end
29
37
  }.compact
30
38
  end
31
39
 
32
40
  def flatten
33
41
  Hash.new.tap do |h|
34
42
  keys.map do |k|
35
- if self[k].kind_of?(PropertyContainer)
43
+ if self[k].kind_of?(PropertyContainer) || self[k].kind_of?(Property)
36
44
  h[k] = self[k].flatten
37
- elsif self[k].kind_of?(Property)
38
- h[k] = self[k].result
39
45
  end
40
46
  end
47
+ end.merge(iterators.inject({}) {|memo, i| memo.merge(i.flatten) })
48
+ end
49
+
50
+ def for_each selector
51
+ Iterator.new(selector).tap do |i|
52
+ iterators << i
41
53
  end
42
54
  end
43
55
  end
@@ -1,25 +1,18 @@
1
1
  #coding: utf-8
2
+ require 'wombat/node_selector'
2
3
 
3
4
  module Wombat
4
5
  module PropertyLocator
5
- def locate properties
6
- properties.each do |p|
7
- p.result = locate_property(p).first
8
- end
6
+ include NodeSelector
7
+
8
+ def locate_first property
9
+ locate(property).first
9
10
  end
10
11
 
11
- private
12
- def locate_property property
13
- result = locate_selector(property.selector, property.namespaces).to_a
12
+ def locate property
13
+ result = select_nodes(property.selector, property.namespaces).to_a
14
14
  result.map! {|r| r.inner_html.strip } if property.format == :html
15
15
  result.map {|r| r.kind_of?(String) ? r : r.inner_text }.map(&:strip)
16
16
  end
17
-
18
- def locate_selector selector, namespaces = nil
19
- return [selector.to_s] if selector.is_a? Symbol
20
- return context.xpath selector[6..-1], namespaces if selector.start_with? "xpath="
21
- return context.css selector[4..-1] if selector.start_with? "css="
22
- nil
23
- end
24
17
  end
25
18
  end
data/spec/crawler_spec.rb CHANGED
@@ -33,10 +33,10 @@ describe Wombat::Crawler do
33
33
  @crawler.location { |v| v.latitude -50.2323 }
34
34
 
35
35
  @crawler_instance.should_receive(:parse) do |arg|
36
- arg["event"].get_property("title").selector.should == "Fulltronic Dezembro"
37
- arg["event"].get_property("time").selector.to_s.should == time.to_s
38
- arg["venue"].get_property("name").selector.should == "Scooba"
39
- arg["location"].get_property("latitude").selector.should == -50.2323
36
+ arg["event"]["title"].selector.should == "Fulltronic Dezembro"
37
+ arg["event"]["time"].selector.to_s.should == time.to_s
38
+ arg["venue"]["name"].selector.should == "Scooba"
39
+ arg["location"]["latitude"].selector.should == -50.2323
40
40
  end
41
41
 
42
42
  @crawler_instance.crawl
@@ -48,11 +48,11 @@ describe Wombat::Crawler do
48
48
  another_crawler_instance = another_crawler.new
49
49
 
50
50
  another_crawler.event { |e| e.title 'Ibiza' }
51
- another_crawler_instance.should_receive(:parse) { |arg| arg["event"].get_property("title").selector.should == "Ibiza" }
51
+ another_crawler_instance.should_receive(:parse) { |arg| arg["event"]["title"].selector.should == "Ibiza" }
52
52
  another_crawler_instance.crawl
53
53
 
54
54
  @crawler.event { |e| e.title 'Fulltronic Dezembro' }
55
- @crawler_instance.should_receive(:parse) { |arg| arg["event"].get_property("title").selector.should == "Fulltronic Dezembro" }
55
+ @crawler_instance.should_receive(:parse) { |arg| arg["event"]["title"].selector.should == "Fulltronic Dezembro" }
56
56
  @crawler_instance.crawl
57
57
  end
58
58
 
@@ -96,12 +96,17 @@ describe Wombat::Crawler do
96
96
  @crawler.for_each "css=.element" do
97
97
  title "css=.title"
98
98
  body "css=.body"
99
+ event do |e|
100
+ e.all "yeah"
101
+ end
99
102
  end
100
103
 
101
104
  @crawler_instance.should_receive(:parse) do |arg|
102
- arg.iterators.first.selector.should == "css=.element"
103
- arg.iterators.first["title"].selector.should == "css=.title"
104
- arg.iterators.first["body"].selector.should == "css=.body"
105
+ it = arg.iterators.first
106
+ it.selector.should == "css=.element"
107
+ it["title"].selector.should == "css=.title"
108
+ it["body"].selector.should == "css=.body"
109
+ it["event"]["all"].selector.should == "yeah"
105
110
  end
106
111
 
107
112
  @crawler_instance.crawl
@@ -13,24 +13,24 @@ class SampleCrawler
13
13
  e.date "xpath=//div[@class='scrollable-items']/div[@class='s-item active']//a" do |d|
14
14
  DateTime.strptime(d, '%d/%m')
15
15
  end
16
- e.type("xpath=.") { |t| t.split(" | ").first.strip.casecmp('SHOW') == 0 ? :show : :party }
16
+ e.type("xpath=.type") { |t| t.split(" | ").first.strip.casecmp('SHOW') == 0 ? :show : :party }
17
17
  end
18
18
 
19
19
  venue do |v|
20
20
  v.name("xpath=.") { |n| name.split(" | ")[2].strip }
21
21
  end
22
+
23
+ # follow_links "xpath=.//a[1]/@href" do
24
+ # event { |e| e.description "css=#main-node-content", :html }
25
+ # venue do |v|
26
+ # v.phone "css=span.tel .value"
27
+ # v.image "xpath=//div[@id='article-image']/div/img/@src"
28
+ # end
22
29
 
23
- follow_links "xpath=.//a[1]/@href" do
24
- event { |e| e.description "css=#main-node-content", :html }
25
- venue do |v|
26
- v.phone "css=span.tel .value"
27
- v.image "xpath=//div[@id='article-image']/div/img/@src"
28
- end
29
-
30
- location do |l|
31
- l.city "css=span.locality"
32
- l.street("css=span.street-address") { |s| s.gsub(/\n/, '').gsub(/ /, '') }
33
- end
34
- end
35
- end
30
+ # location do |l|
31
+ # l.city "css=span.locality"
32
+ # l.street("css=span.street-address") { |s| s.gsub(/\n/, '').gsub(/ /, '') }
33
+ # end
34
+ # end
35
+ end
36
36
  end
@@ -15,6 +15,10 @@ describe 'basic crawler setup' do
15
15
  s.twitter "css=.ctn-bar li.last"
16
16
  end
17
17
 
18
+ crawler.for_each "css=.ctn-links" do
19
+ menu "css=a"
20
+ end
21
+
18
22
  crawler.subheader "css=h2.ttl-dynamic" do |h|
19
23
  h.gsub("London", "Londres")
20
24
  end
@@ -24,6 +28,7 @@ describe 'basic crawler setup' do
24
28
  results = crawler_instance.crawl
25
29
 
26
30
  results["search"].should == "Buscar"
31
+ results["menu"].should =~ ["Agenda", "Brasileiro", "Brasil", "Bolsas", "Cinema", "Galerias de Fotos", "Beleza", "Esportes", "Assine o RSS"]
27
32
  results["subheader"].should == "Londres 2012"
28
33
  results["social"]["twitter"].should == "Verão"
29
34
  end
data/spec/parser_spec.rb CHANGED
@@ -24,7 +24,7 @@ describe Wombat::Parser do
24
24
  fake_parser = double :parser
25
25
  fake_document.should_receive(:parser).and_return(fake_parser)
26
26
  @parser.mechanize.stub(:get).and_return fake_document
27
- @parser.should_receive(:locate).with(@metadata.all_properties)
27
+ @parser.should_not_receive :locate
28
28
  @parser.parse @metadata
29
29
  end
30
30
 
@@ -42,7 +42,7 @@ describe Wombat::Parser do
42
42
 
43
43
  @parser.mechanize.stub(:get).and_return fake_document
44
44
  @metadata.stub(:all_properties).and_return [property]
45
- @parser.should_receive(:locate).with(@metadata.all_properties)
45
+ @parser.should_receive(:locate_first).with(property)
46
46
 
47
47
  @parser.parse @metadata
48
48
 
@@ -59,21 +59,20 @@ describe Wombat::Parser do
59
59
  p.should == "blah"
60
60
  }
61
61
 
62
- property.should_receive(:result).and_return("blah")
63
62
  fake_document.should_receive(:parser).and_return(fake_parser)
64
63
  property.should_receive(:callback).twice.and_return(block)
65
64
  property.should_receive(:result=).with(true)
66
65
 
67
66
  @parser.mechanize.stub(:get).and_return fake_document
68
67
  @metadata.stub(:all_properties).and_return [property]
69
- @parser.should_receive(:locate).with(@metadata.all_properties)
68
+ @parser.should_receive(:locate_first).with(property).and_return("blah")
70
69
 
71
70
  @parser.parse @metadata
72
71
 
73
72
  block_called.should be_true
74
73
  end
75
74
 
76
- it 'should return array with requested properties' do
75
+ it 'should return hash with requested properties' do
77
76
  hash = double :results
78
77
  fake_parser = double :parser
79
78
  fake_document = double :document
@@ -81,7 +80,34 @@ describe Wombat::Parser do
81
80
  fake_document.should_receive(:parser).and_return fake_parser
82
81
  @parser.mechanize.stub(:get).and_return fake_document
83
82
  @metadata.should_receive(:flatten).and_return hash
84
-
83
+
85
84
  @parser.parse(@metadata).should == hash
86
85
  end
87
- end
86
+
87
+ it 'should iterate in for_each properties' do
88
+ fake_parser = double :parser
89
+ fake_document = double :document
90
+ c1 = double :context
91
+ c2 = double :context
92
+ it = Wombat::Iterator.new "it_selector"
93
+ it.prop_1 "some_selector"
94
+ it.prop_2 "another_selector"
95
+
96
+ @parser.should_receive(:context=).ordered
97
+ @metadata.should_receive(:iterators).and_return [it]
98
+ @metadata.should_receive(:flatten)
99
+ fake_document.should_receive(:parser).and_return(fake_parser)
100
+ it['prop_1'].should_receive(:result).exactly(4).times.and_return([])
101
+ it['prop_2'].should_receive(:result).exactly(4).times.and_return([])
102
+ @parser.mechanize.stub(:get).and_return fake_document
103
+ @parser.should_receive(:select_nodes).with("it_selector").and_return [c1, c2]
104
+ @parser.should_receive(:context=).with(c1).ordered
105
+ @parser.should_receive(:context=).with(c2).ordered
106
+ @parser.should_receive(:context=).ordered
107
+ @parser.should_receive(:locate_first).with(it['prop_1']).twice
108
+ @parser.should_receive(:locate_first).with(it['prop_2']).twice
109
+ @parser.stub(:locate)
110
+
111
+ @parser.parse(@metadata)
112
+ end
113
+ end
@@ -5,17 +5,24 @@ describe Wombat::PropertyContainer do
5
5
  @metadata = Wombat::PropertyContainer.new
6
6
  end
7
7
 
8
- it 'should return an array with all the metadata properties' do
8
+ it 'should return an array with all the metadata properties excluding iterators' do
9
9
  @metadata["event"] = Wombat::PropertyContainer.new
10
10
  @metadata["venue"] = Wombat::PropertyContainer.new
11
11
  @metadata.another_property "/some/selector", :text
12
12
  @metadata["event"]["something"] = Wombat::PropertyContainer.new
13
13
  @metadata["event"]["something"].else "Wohooo"
14
14
  @metadata["venue"].awesome "whooea"
15
+ it = Wombat::Iterator.new "it_selector"
16
+ it.felipe "lima"
17
+ @metadata.iterators << it
15
18
 
16
19
  all_propes = @metadata.all_properties
17
20
 
18
- all_propes.should =~ [@metadata["another_property"], @metadata["event"]["something"]["else"], @metadata["venue"]["awesome"]]
21
+ all_propes.should =~ [
22
+ @metadata["another_property"],
23
+ @metadata["event"]["something"]["else"],
24
+ @metadata["venue"]["awesome"]
25
+ ]
19
26
  end
20
27
 
21
28
  it 'should be able to change properties via all_properties' do
@@ -24,7 +31,7 @@ describe Wombat::PropertyContainer do
24
31
  @metadata["another_property"].selector.should == "abc"
25
32
  end
26
33
 
27
- it 'should return metadata in plain hash format' do
34
+ it 'should return metadata in plain hash format including iterators' do
28
35
  @metadata.title "/some/selector"
29
36
  @metadata["title"].result = "Gogobot Inc."
30
37
  @metadata["holder"] = Wombat::PropertyContainer.new
@@ -33,6 +40,10 @@ describe Wombat::PropertyContainer do
33
40
  @metadata["holder"]["subheader"] = Wombat::PropertyContainer.new
34
41
  @metadata["holder"]["subheader"].section "/blah"
35
42
  @metadata["holder"]["subheader"]["section"].result = "Lorem Ipsum"
43
+ it = Wombat::Iterator.new "it_selector"
44
+ it.felipe "lima"
45
+ it["felipe"].result = ["correa", "de souza", "lima"]
46
+ @metadata.iterators = [it]
36
47
  @metadata.footer("another thing", :html) { |a| true }
37
48
  @metadata["footer"].result = "bla bla bla"
38
49
 
@@ -44,6 +55,7 @@ describe Wombat::PropertyContainer do
44
55
  "section" => "Lorem Ipsum"
45
56
  }
46
57
  },
58
+ "felipe" => ["correa", "de souza", "lima"],
47
59
  "footer" => "bla bla bla"
48
60
  }
49
61
  end
@@ -28,12 +28,12 @@ describe Wombat::PropertyLocator do
28
28
 
29
29
  @locator_instance.stub(:context).and_return context
30
30
 
31
- @locator_instance.locate @metadata.all_properties
31
+ @metadata.all_properties.each { |p| p.result = @locator_instance.locate_first p }
32
32
 
33
- @metadata.get_property("blah").result.should == "abc"
34
- @metadata["event"].get_property("data1").result.should == "Something cool"
35
- @metadata["venue"].get_property("data2").result.should == "farms"
36
- @metadata["location"].get_property("data3").result.should == "Another stuff"
33
+ @metadata["blah"].result.should == "abc"
34
+ @metadata["event"]["data1"].result.should == "Something cool"
35
+ @metadata["venue"]["data2"].result.should == "farms"
36
+ @metadata["location"]["data3"].result.should == "Another stuff"
37
37
  end
38
38
 
39
39
  it 'should support properties with html format' do
@@ -47,9 +47,9 @@ describe Wombat::PropertyLocator do
47
47
 
48
48
  @metadata["event"].another_info "xpath=/anotherData", :html
49
49
 
50
- @locator_instance.locate @metadata.all_properties
50
+ @metadata.all_properties.each { |p| p.result = @locator_instance.locate_first p }
51
51
 
52
- @metadata["event"].get_property("another_info").result.should == "some another info"
52
+ @metadata["event"]["another_info"].result.should == "some another info"
53
53
  end
54
54
 
55
55
  it 'should trim property contents and use namespaces if present' do
@@ -59,8 +59,8 @@ describe Wombat::PropertyLocator do
59
59
  @locator_instance.stub(:context).and_return context
60
60
  @metadata["event"].description "xpath=/event/some/description", :text, "blah"
61
61
 
62
- @locator_instance.locate @metadata.all_properties
62
+ @metadata.all_properties.each { |p| p.result = @locator_instance.locate_first p }
63
63
 
64
- @metadata["event"].get_property("description").result.should == "awesome event"
64
+ @metadata["event"]["description"].result.should == "awesome event"
65
65
  end
66
66
  end
@@ -6,17 +6,21 @@ describe SampleCrawler do
6
6
  @sample_crawler = SampleCrawler.new
7
7
  end
8
8
 
9
- xit 'should correctly assign event metadata' do
9
+ it 'should correctly assign event metadata' do
10
10
  @sample_crawler.should_receive(:parse) do |args|
11
- args.event["title"].selector.should == "xpath=."
12
- args.event["description"].selector.should == "css=#main-node-content"
13
- args.event["date"].selector.should == DateTime.now.to_date
11
+ # args["event"]["description"].selector.should == "css=#main-node-content"
14
12
 
15
- args.venue["name"].selector.should == "Cafe de La Musique"
16
- args.venue["address"].selector.should == "324 Dom Pedro II Street"
13
+ # args["venue"]["address"].selector.should == "324 Dom Pedro II Street"
17
14
 
18
- args[:base_url].should == 'http://www.google.com/'
19
- args[:list_page].should == 'shows.php'
15
+ it = args.iterators.first
16
+ it.selector.should == "css=div.title-agenda"
17
+ it["event"]["title"].selector.should == "xpath=."
18
+ it["event"]["date"].selector.should == "xpath=//div[@class='scrollable-items']/div[@class='s-item active']//a"
19
+ it["event"]["type"].selector.should == "xpath=.type"
20
+ it["venue"]["name"].selector.should == "xpath=."
21
+
22
+ args[:base_url].should == 'http://www.obaoba.com.br'
23
+ args[:list_page].should == '/porto-alegre/agenda'
20
24
  end
21
25
 
22
26
  @sample_crawler.crawl
data/wombat.gemspec CHANGED
@@ -5,11 +5,11 @@
5
5
 
6
6
  Gem::Specification.new do |s|
7
7
  s.name = "wombat"
8
- s.version = "0.1.6"
8
+ s.version = "0.2.0"
9
9
 
10
10
  s.required_rubygems_version = Gem::Requirement.new(">= 0") if s.respond_to? :required_rubygems_version=
11
11
  s.authors = ["Felipe Lima"]
12
- s.date = "2012-02-08"
12
+ s.date = "2012-02-14"
13
13
  s.description = "Generic Web crawler with a DSL that parses structured data from web pages"
14
14
  s.email = "felipe.lima@gmail.com"
15
15
  s.extra_rdoc_files = [
@@ -32,6 +32,7 @@ Gem::Specification.new do |s|
32
32
  "lib/wombat/crawler.rb",
33
33
  "lib/wombat/iterator.rb",
34
34
  "lib/wombat/metadata.rb",
35
+ "lib/wombat/node_selector.rb",
35
36
  "lib/wombat/parser.rb",
36
37
  "lib/wombat/property.rb",
37
38
  "lib/wombat/property_container.rb",
@@ -66,6 +67,7 @@ Gem::Specification.new do |s|
66
67
  s.add_development_dependency(%q<jeweler>, [">= 0"])
67
68
  s.add_development_dependency(%q<rspec>, [">= 0"])
68
69
  s.add_development_dependency(%q<guard>, [">= 0"])
70
+ s.add_development_dependency(%q<growl_notify>, [">= 0"])
69
71
  s.add_development_dependency(%q<guard-rspec>, [">= 0"])
70
72
  s.add_development_dependency(%q<guard-bundler>, [">= 0"])
71
73
  s.add_development_dependency(%q<vcr>, ["= 2.0.0.rc1"])
@@ -79,6 +81,7 @@ Gem::Specification.new do |s|
79
81
  s.add_dependency(%q<jeweler>, [">= 0"])
80
82
  s.add_dependency(%q<rspec>, [">= 0"])
81
83
  s.add_dependency(%q<guard>, [">= 0"])
84
+ s.add_dependency(%q<growl_notify>, [">= 0"])
82
85
  s.add_dependency(%q<guard-rspec>, [">= 0"])
83
86
  s.add_dependency(%q<guard-bundler>, [">= 0"])
84
87
  s.add_dependency(%q<vcr>, ["= 2.0.0.rc1"])
@@ -93,6 +96,7 @@ Gem::Specification.new do |s|
93
96
  s.add_dependency(%q<jeweler>, [">= 0"])
94
97
  s.add_dependency(%q<rspec>, [">= 0"])
95
98
  s.add_dependency(%q<guard>, [">= 0"])
99
+ s.add_dependency(%q<growl_notify>, [">= 0"])
96
100
  s.add_dependency(%q<guard-rspec>, [">= 0"])
97
101
  s.add_dependency(%q<guard-bundler>, [">= 0"])
98
102
  s.add_dependency(%q<vcr>, ["= 2.0.0.rc1"])
metadata CHANGED
@@ -1,7 +1,7 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: wombat
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.1.6
4
+ version: 0.2.0
5
5
  prerelease:
6
6
  platform: ruby
7
7
  authors:
@@ -9,11 +9,11 @@ authors:
9
9
  autorequire:
10
10
  bindir: bin
11
11
  cert_chain: []
12
- date: 2012-02-08 00:00:00.000000000 Z
12
+ date: 2012-02-14 00:00:00.000000000 Z
13
13
  dependencies:
14
14
  - !ruby/object:Gem::Dependency
15
15
  name: mechanize
16
- requirement: &70163534813940 !ruby/object:Gem::Requirement
16
+ requirement: &70107446346260 !ruby/object:Gem::Requirement
17
17
  none: false
18
18
  requirements:
19
19
  - - ! '>='
@@ -21,10 +21,10 @@ dependencies:
21
21
  version: '0'
22
22
  type: :runtime
23
23
  prerelease: false
24
- version_requirements: *70163534813940
24
+ version_requirements: *70107446346260
25
25
  - !ruby/object:Gem::Dependency
26
26
  name: activesupport
27
- requirement: &70163534813160 !ruby/object:Gem::Requirement
27
+ requirement: &70107446345760 !ruby/object:Gem::Requirement
28
28
  none: false
29
29
  requirements:
30
30
  - - ! '>='
@@ -32,10 +32,10 @@ dependencies:
32
32
  version: '0'
33
33
  type: :runtime
34
34
  prerelease: false
35
- version_requirements: *70163534813160
35
+ version_requirements: *70107446345760
36
36
  - !ruby/object:Gem::Dependency
37
37
  name: bundler
38
- requirement: &70163534812680 !ruby/object:Gem::Requirement
38
+ requirement: &70107446345060 !ruby/object:Gem::Requirement
39
39
  none: false
40
40
  requirements:
41
41
  - - ! '>='
@@ -43,10 +43,10 @@ dependencies:
43
43
  version: '0'
44
44
  type: :development
45
45
  prerelease: false
46
- version_requirements: *70163534812680
46
+ version_requirements: *70107446345060
47
47
  - !ruby/object:Gem::Dependency
48
48
  name: rake
49
- requirement: &70163534812200 !ruby/object:Gem::Requirement
49
+ requirement: &70107446344320 !ruby/object:Gem::Requirement
50
50
  none: false
51
51
  requirements:
52
52
  - - ! '>='
@@ -54,10 +54,10 @@ dependencies:
54
54
  version: '0'
55
55
  type: :development
56
56
  prerelease: false
57
- version_requirements: *70163534812200
57
+ version_requirements: *70107446344320
58
58
  - !ruby/object:Gem::Dependency
59
59
  name: yard
60
- requirement: &70163534811700 !ruby/object:Gem::Requirement
60
+ requirement: &70107446359960 !ruby/object:Gem::Requirement
61
61
  none: false
62
62
  requirements:
63
63
  - - ! '>='
@@ -65,10 +65,10 @@ dependencies:
65
65
  version: '0'
66
66
  type: :development
67
67
  prerelease: false
68
- version_requirements: *70163534811700
68
+ version_requirements: *70107446359960
69
69
  - !ruby/object:Gem::Dependency
70
70
  name: jeweler
71
- requirement: &70163534811200 !ruby/object:Gem::Requirement
71
+ requirement: &70107446359480 !ruby/object:Gem::Requirement
72
72
  none: false
73
73
  requirements:
74
74
  - - ! '>='
@@ -76,10 +76,10 @@ dependencies:
76
76
  version: '0'
77
77
  type: :development
78
78
  prerelease: false
79
- version_requirements: *70163534811200
79
+ version_requirements: *70107446359480
80
80
  - !ruby/object:Gem::Dependency
81
81
  name: rspec
82
- requirement: &70163534810720 !ruby/object:Gem::Requirement
82
+ requirement: &70107446358960 !ruby/object:Gem::Requirement
83
83
  none: false
84
84
  requirements:
85
85
  - - ! '>='
@@ -87,10 +87,10 @@ dependencies:
87
87
  version: '0'
88
88
  type: :development
89
89
  prerelease: false
90
- version_requirements: *70163534810720
90
+ version_requirements: *70107446358960
91
91
  - !ruby/object:Gem::Dependency
92
92
  name: guard
93
- requirement: &70163534810240 !ruby/object:Gem::Requirement
93
+ requirement: &70107446358460 !ruby/object:Gem::Requirement
94
94
  none: false
95
95
  requirements:
96
96
  - - ! '>='
@@ -98,10 +98,21 @@ dependencies:
98
98
  version: '0'
99
99
  type: :development
100
100
  prerelease: false
101
- version_requirements: *70163534810240
101
+ version_requirements: *70107446358460
102
+ - !ruby/object:Gem::Dependency
103
+ name: growl_notify
104
+ requirement: &70107446357840 !ruby/object:Gem::Requirement
105
+ none: false
106
+ requirements:
107
+ - - ! '>='
108
+ - !ruby/object:Gem::Version
109
+ version: '0'
110
+ type: :development
111
+ prerelease: false
112
+ version_requirements: *70107446357840
102
113
  - !ruby/object:Gem::Dependency
103
114
  name: guard-rspec
104
- requirement: &70163535048080 !ruby/object:Gem::Requirement
115
+ requirement: &70107446357240 !ruby/object:Gem::Requirement
105
116
  none: false
106
117
  requirements:
107
118
  - - ! '>='
@@ -109,10 +120,10 @@ dependencies:
109
120
  version: '0'
110
121
  type: :development
111
122
  prerelease: false
112
- version_requirements: *70163535048080
123
+ version_requirements: *70107446357240
113
124
  - !ruby/object:Gem::Dependency
114
125
  name: guard-bundler
115
- requirement: &70163535047560 !ruby/object:Gem::Requirement
126
+ requirement: &70107446356580 !ruby/object:Gem::Requirement
116
127
  none: false
117
128
  requirements:
118
129
  - - ! '>='
@@ -120,10 +131,10 @@ dependencies:
120
131
  version: '0'
121
132
  type: :development
122
133
  prerelease: false
123
- version_requirements: *70163535047560
134
+ version_requirements: *70107446356580
124
135
  - !ruby/object:Gem::Dependency
125
136
  name: vcr
126
- requirement: &70163535047060 !ruby/object:Gem::Requirement
137
+ requirement: &70107446355780 !ruby/object:Gem::Requirement
127
138
  none: false
128
139
  requirements:
129
140
  - - =
@@ -131,10 +142,10 @@ dependencies:
131
142
  version: 2.0.0.rc1
132
143
  type: :development
133
144
  prerelease: false
134
- version_requirements: *70163535047060
145
+ version_requirements: *70107446355780
135
146
  - !ruby/object:Gem::Dependency
136
147
  name: fakeweb
137
- requirement: &70163535046440 !ruby/object:Gem::Requirement
148
+ requirement: &70107446355300 !ruby/object:Gem::Requirement
138
149
  none: false
139
150
  requirements:
140
151
  - - ! '>='
@@ -142,7 +153,7 @@ dependencies:
142
153
  version: '0'
143
154
  type: :development
144
155
  prerelease: false
145
- version_requirements: *70163535046440
156
+ version_requirements: *70107446355300
146
157
  description: Generic Web crawler with a DSL that parses structured data from web pages
147
158
  email: felipe.lima@gmail.com
148
159
  executables: []
@@ -166,6 +177,7 @@ files:
166
177
  - lib/wombat/crawler.rb
167
178
  - lib/wombat/iterator.rb
168
179
  - lib/wombat/metadata.rb
180
+ - lib/wombat/node_selector.rb
169
181
  - lib/wombat/parser.rb
170
182
  - lib/wombat/property.rb
171
183
  - lib/wombat/property_container.rb