lzell-mapricot 0.0.1 → 0.0.2.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/History.txt +13 -1
- data/README.rdoc +9 -8
- data/benchmark/benchmarks.rb +127 -0
- data/{test/mapricot_spec.rb → examples/facebook_api.rb} +23 -56
- data/examples/lastfm_api.rb +1 -1
- data/examples/lastfm_api_no_request.rb +1 -1
- data/examples/readme_examples.rb +1 -1
- data/examples/xml_with_attributes.rb +26 -0
- data/lib/abstract_doc.rb +121 -0
- data/lib/mapricot.rb +102 -66
- data/mapricot.gemspec +19 -6
- data/test/abstract_doc_spec.rb +79 -0
- data/test/has_attribute/has_attribute_spec.rb +57 -0
- data/test/has_many/has_many_ids_spec.rb +73 -0
- data/test/has_many/has_many_nested_spec.rb +116 -0
- data/test/has_one/has_one_id_spec.rb +64 -0
- data/test/has_one/has_one_nested_spec.rb +97 -0
- data/test/has_one/has_one_user_spec.rb +67 -0
- data/test/suite.rb +8 -0
- data/test/test_mapricot.rb +86 -0
- data/test/{mapricot_tests.rb → test_mapricot_readme.rb} +6 -6
- metadata +36 -4
data/History.txt
CHANGED
@@ -1,3 +1,15 @@
|
|
1
1
|
== 0.0.1 2009-03-06
|
2
2
|
|
3
|
-
First gem!
|
3
|
+
First gem!
|
4
|
+
|
5
|
+
== 0.0.2 2009-03-17
|
6
|
+
|
7
|
+
Nokogiri and Libxml support.
|
8
|
+
|
9
|
+
Select the parser to use by setting Mapricot.parser as follows:
|
10
|
+
|
11
|
+
Mapricot.parser = :nokogiri
|
12
|
+
Mapricot.parser = :libxml
|
13
|
+
Mapricot.parser = :hpricot
|
14
|
+
|
15
|
+
Hpricot will be used by default
|
data/README.rdoc
CHANGED
@@ -5,7 +5,7 @@ Makes working with XML stupid easy. XML to object mapper with an interface simi
|
|
5
5
|
|
6
6
|
== Install
|
7
7
|
|
8
|
-
|
8
|
+
sudo gem install lzell-mapricot
|
9
9
|
|
10
10
|
== Usage
|
11
11
|
|
@@ -20,16 +20,15 @@ When you instantiate a Mapricot::Base object, you can pass it a string of xml:
|
|
20
20
|
|
21
21
|
MapMe.new :xml => "<some_stuff>...</some_stuff>"
|
22
22
|
|
23
|
-
Or
|
24
|
-
class MapMeToo < Mapricot::Base
|
25
|
-
end
|
23
|
+
Or a url:
|
26
24
|
|
27
|
-
|
25
|
+
MapMe.new :url => "http://some_url"
|
28
26
|
|
29
27
|
== Examples
|
30
28
|
|
31
29
|
==== Super Simple
|
32
|
-
|
30
|
+
require 'mapricot'
|
31
|
+
|
33
32
|
simple_xml = %(
|
34
33
|
<user>
|
35
34
|
<id>1</name>
|
@@ -54,7 +53,8 @@ Or you can pass it a url:
|
|
54
53
|
|
55
54
|
|
56
55
|
==== A little more realistic
|
57
|
-
|
56
|
+
require 'mapricot'
|
57
|
+
|
58
58
|
xml = %(
|
59
59
|
<user>
|
60
60
|
<id>2</name>
|
@@ -110,9 +110,10 @@ Or you can pass it a url:
|
|
110
110
|
# => Skiing for 2 years
|
111
111
|
# => Hiking for 3 years
|
112
112
|
|
113
|
+
|
113
114
|
== Credits
|
114
115
|
|
115
|
-
|
116
|
+
Named after why the luck stiff's Hpricot, which made this possible.
|
116
117
|
|
117
118
|
|
118
119
|
Copyright (c) 2009 Lou Zell, released under the MIT license
|
@@ -0,0 +1,127 @@
|
|
1
|
+
require File.expand_path(File.dirname(__FILE__) + "/../lib/mapricot")
|
2
|
+
require 'benchmark'
|
3
|
+
|
4
|
+
last_fm_example = %(<?xml version="1.0" encoding="utf-8"?>
|
5
|
+
<events>
|
6
|
+
<event>
|
7
|
+
<id>895664</id>
|
8
|
+
<title>Lydia</title>
|
9
|
+
<artists>
|
10
|
+
<artist>Lydia</artist>
|
11
|
+
<artist>Black Gold</artist>
|
12
|
+
|
13
|
+
<headliner>Lydia</headliner>
|
14
|
+
</artists>
|
15
|
+
<venue>
|
16
|
+
<name>Mercury Lounge</name>
|
17
|
+
<location>
|
18
|
+
<city>New York</city>
|
19
|
+
<country>United States</country>
|
20
|
+
|
21
|
+
<street>217 East Houston Street</street>
|
22
|
+
<postalcode>10002</postalcode>
|
23
|
+
<timezone>EST</timezone>
|
24
|
+
|
25
|
+
</location>
|
26
|
+
<url>http://www.last.fm/venue/8899833</url>
|
27
|
+
</venue>
|
28
|
+
<startDate>Thu, 05 Mar 2009</startDate>
|
29
|
+
<startTime>18:30</startTime>
|
30
|
+
<description><![CDATA[<div class="bbcode">On Sale Fri 1/16 at Noon<br />
|
31
|
+
21+<br />
|
32
|
+
$10<br />
|
33
|
+
Doors 6:30pm/ Show 7:30pm</div>]]></description>
|
34
|
+
<image size="small">http://userserve-ak.last.fm/serve/34/9158303.jpg</image>
|
35
|
+
|
36
|
+
<image size="medium">http://userserve-ak.last.fm/serve/64/9158303.jpg</image>
|
37
|
+
<image size="large">http://userserve-ak.last.fm/serve/126/9158303.jpg</image>
|
38
|
+
<attendance>7</attendance>
|
39
|
+
<reviews>0</reviews>
|
40
|
+
<tag>lastfm:event=895664</tag>
|
41
|
+
<url>http://www.last.fm/event/895664</url>
|
42
|
+
|
43
|
+
</event>
|
44
|
+
<event>
|
45
|
+
<id>924763</id>
|
46
|
+
<title>Stars Like Fleas</title>
|
47
|
+
<artists>
|
48
|
+
<artist>Stars Like Fleas</artist>
|
49
|
+
|
50
|
+
<artist>Frances</artist>
|
51
|
+
<artist>twi the humble feather</artist>
|
52
|
+
<artist>La Strada</artist>
|
53
|
+
<headliner>Stars Like Fleas</headliner>
|
54
|
+
</artists>
|
55
|
+
<venue>
|
56
|
+
<name>Music Hall of Williamsburg</name>
|
57
|
+
|
58
|
+
<location>
|
59
|
+
<city>Brooklyn, NY</city>
|
60
|
+
<country>United States</country>
|
61
|
+
<street>66 North 6th Street</street>
|
62
|
+
<postalcode>11211</postalcode>
|
63
|
+
<timezone>EST</timezone>
|
64
|
+
</location>
|
65
|
+
<url>http://www.last.fm/venue/8851989</url>
|
66
|
+
</venue>
|
67
|
+
<startDate>Thu, 05 Mar 2009</startDate>
|
68
|
+
|
69
|
+
<startTime>19:00</startTime>
|
70
|
+
<description><![CDATA[<div class="bbcode">Doors 7 p.m. / Show 8 p.m.<br />
|
71
|
+
$10 advance / $12 day of show<br />
|
72
|
+
18+<br />
|
73
|
+
<br />
|
74
|
+
On sale Wed. 2/4 at noon</div>]]></description>
|
75
|
+
<image size="small">http://userserve-ak.last.fm/serve/34/3598785.jpg</image>
|
76
|
+
<image size="medium">http://userserve-ak.last.fm/serve/64/3598785.jpg</image>
|
77
|
+
<image size="large">http://userserve-ak.last.fm/serve/126/3598785.jpg</image>
|
78
|
+
<attendance>1</attendance>
|
79
|
+
|
80
|
+
<reviews>0</reviews>
|
81
|
+
<tag>lastfm:event=924763</tag>
|
82
|
+
<url>http://www.last.fm/event/924763</url>
|
83
|
+
</event>
|
84
|
+
</events>
|
85
|
+
|
86
|
+
)
|
87
|
+
|
88
|
+
|
89
|
+
class Response < Mapricot::Base
|
90
|
+
has_many :events, :xml
|
91
|
+
end
|
92
|
+
|
93
|
+
class Event < Mapricot::Base
|
94
|
+
has_one :id, :integer
|
95
|
+
has_one :title, :string
|
96
|
+
has_one :artist_group, :xml, :tag_name => "artists"
|
97
|
+
has_one :venue, :xml
|
98
|
+
end
|
99
|
+
|
100
|
+
class ArtistGroup < Mapricot::Base
|
101
|
+
has_many :artists, :string
|
102
|
+
has_one :headliner, :string
|
103
|
+
end
|
104
|
+
|
105
|
+
class Venue < Mapricot::Base
|
106
|
+
has_one :name, :string
|
107
|
+
end
|
108
|
+
|
109
|
+
lfm = Response.new(:xml => last_fm_example)
|
110
|
+
|
111
|
+
|
112
|
+
n = 200
|
113
|
+
Benchmark.bm do |x|
|
114
|
+
x.report {
|
115
|
+
Mapricot.parser = :hpricot;
|
116
|
+
n.times { lfm = Response.new(:xml => last_fm_example) }
|
117
|
+
}
|
118
|
+
x.report {
|
119
|
+
Mapricot.parser = :libxml;
|
120
|
+
n.times { lfm = Response.new(:xml => last_fm_example) }
|
121
|
+
}
|
122
|
+
x.report {
|
123
|
+
Mapricot.parser = :nokogiri;
|
124
|
+
n.times { lfm = Response.new(:xml => last_fm_example) }
|
125
|
+
}
|
126
|
+
|
127
|
+
end
|
@@ -1,10 +1,13 @@
|
|
1
|
-
require
|
2
|
-
require 'spec'
|
1
|
+
require File.expand_path(File.dirname(__FILE__) + "/../lib/mapricot")
|
3
2
|
|
4
|
-
# Using a Facebook example
|
5
|
-
|
6
|
-
|
7
|
-
|
3
|
+
# Using a Facebook example
|
4
|
+
# This xml is taken straight from here: http://wiki.developers.facebook.com/index.php/Users.getInfo
|
5
|
+
# Note, libxml does not like this for some reason:
|
6
|
+
# <users_getInfo_response xmlns="http://api.facebook.com/1.0/" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://api.facebook.com/1.0/ http://api.facebook.com/1.0/facebook.xsd" list="true">
|
7
|
+
# So 'xmlns="http://api.facebook.com/1.0/"' has been stripped from tag
|
8
|
+
# Because of this, for real requests we will have to use hpricot or nokogiri
|
9
|
+
FACEBOOK_XML = %(<?xml version="1.0" encoding="UTF-8"?>
|
10
|
+
<users_getInfo_response xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://api.facebook.com/1.0/ http://api.facebook.com/1.0/facebook.xsd" list="true">
|
8
11
|
<user>
|
9
12
|
<uid>8055</uid>
|
10
13
|
<about_me>This field perpetuates the glorification of the ego. Also, it has a character limit.</about_me>
|
@@ -27,7 +30,7 @@ XML = %(
|
|
27
30
|
<zip>94303</zip>
|
28
31
|
</current_location>
|
29
32
|
</user>
|
30
|
-
</
|
33
|
+
</users_getInfo_response>
|
31
34
|
)
|
32
35
|
|
33
36
|
|
@@ -64,55 +67,19 @@ class CurrentLocation < Mapricot::Base
|
|
64
67
|
end
|
65
68
|
|
66
69
|
|
67
|
-
|
68
|
-
|
69
|
-
|
70
|
-
it "should respond to users" do
|
71
|
-
@response.should respond_to(:users)
|
72
|
-
end
|
73
|
-
|
74
|
-
it "should return an array of size 1 when sent users" do
|
75
|
-
@response.users.class.should equal(Array)
|
76
|
-
@response.users.size.should equal(1)
|
77
|
-
end
|
78
|
-
end
|
79
|
-
|
80
|
-
describe "response.users.first" do
|
81
|
-
before(:all) do
|
82
|
-
response = UsersGetInfoResponse.new(:xml => XML)
|
83
|
-
@first_user = response.users.first
|
84
|
-
end
|
85
|
-
|
86
|
-
it "should be of class User" do
|
87
|
-
@first_user.class.should equal(User)
|
88
|
-
end
|
89
|
-
|
90
|
-
it "should respond to activities" do
|
91
|
-
@first_user.should respond_to(:activities)
|
92
|
-
@first_user.activities.should == "Here: facebook, etc. There: Glee Club, a capella, teaching."
|
93
|
-
end
|
94
|
-
|
95
|
-
it "should respond to current_location" do
|
96
|
-
@first_user.should respond_to(:current_location)
|
97
|
-
end
|
98
|
-
end
|
70
|
+
response = UsersGetInfoResponse.new(:xml => FACEBOOK_XML)
|
71
|
+
puts response.users.class # => Array
|
72
|
+
puts response.users.size # => 1
|
99
73
|
|
74
|
+
user = response.users.first
|
100
75
|
|
101
|
-
|
102
|
-
|
103
|
-
|
104
|
-
|
105
|
-
|
106
|
-
|
107
|
-
it "should have class CurrentLocation" do
|
108
|
-
@current_location.class.should equal(CurrentLocation)
|
109
|
-
end
|
110
|
-
|
111
|
-
it "should respond to city, state, country, and zip" do
|
112
|
-
@current_location.city.should == 'Palo Alto'
|
113
|
-
@current_location.state.should == 'CA'
|
114
|
-
@current_location.country.should == 'United States'
|
115
|
-
@current_location.zip.should == 94303
|
116
|
-
end
|
117
|
-
end
|
76
|
+
puts user.uid # => 8055
|
77
|
+
puts user.activities # => "Here: facebook, etc. There: Glee Club, a capella, teaching."
|
78
|
+
puts user.current_location.city # => "Palo Alto"
|
79
|
+
puts user.current_location.state # => "CA"
|
80
|
+
puts user.current_location.country # => "United States"
|
81
|
+
puts user.current_location.zip # => 94303
|
118
82
|
|
83
|
+
# need to remove redundancy here:
|
84
|
+
puts user.affiliation_list.affiliations.first.name # => "Facebook Developers"
|
85
|
+
puts user.affiliation_list.affiliations.first.nid # => 50453093
|
data/examples/lastfm_api.rb
CHANGED
@@ -8,7 +8,7 @@
|
|
8
8
|
# Response.new(:xml => "some string of xml")
|
9
9
|
|
10
10
|
# If you do not have an API key, see examples/lastfm_api_no_request.rb
|
11
|
-
require
|
11
|
+
require File.expand_path(File.dirname(__FILE__) + "/../lib/mapricot")
|
12
12
|
|
13
13
|
class Response < Mapricot::Base
|
14
14
|
has_many :events, :xml
|
data/examples/readme_examples.rb
CHANGED
@@ -0,0 +1,26 @@
|
|
1
|
+
require File.expand_path(File.dirname(__FILE__) + "/../lib/mapricot")
|
2
|
+
|
3
|
+
|
4
|
+
xml = %(
|
5
|
+
<response>
|
6
|
+
<user id="32" name="Bob"></user>
|
7
|
+
<user id="33" name="Sally"></user>
|
8
|
+
</response>
|
9
|
+
)
|
10
|
+
|
11
|
+
|
12
|
+
class Response < Mapricot::Base
|
13
|
+
has_many :users, :xml
|
14
|
+
end
|
15
|
+
|
16
|
+
class User < Mapricot::Base
|
17
|
+
has_attribute :id
|
18
|
+
has_attribute :name
|
19
|
+
end
|
20
|
+
|
21
|
+
response = Response.new(:xml => xml)
|
22
|
+
|
23
|
+
response.users.each do |user|
|
24
|
+
puts user.name
|
25
|
+
puts user.id
|
26
|
+
end
|
data/lib/abstract_doc.rb
ADDED
@@ -0,0 +1,121 @@
|
|
1
|
+
require 'open-uri'
|
2
|
+
require 'rubygems'
|
3
|
+
# gem 'hpricot', '=0.6.164'
|
4
|
+
require 'hpricot'
|
5
|
+
require 'libxml'
|
6
|
+
require 'nokogiri'
|
7
|
+
|
8
|
+
|
9
|
+
module Mapricot
|
10
|
+
@parser = :hpricot
|
11
|
+
class << self; attr_accessor :parser; end
|
12
|
+
|
13
|
+
# AbstractDoc should be able to find tags, get inner tag content. Find all tags (return an array)
|
14
|
+
# I think I will also need AbstractNode
|
15
|
+
|
16
|
+
class AbstractDoc
|
17
|
+
|
18
|
+
def self.from_url(url)
|
19
|
+
adoc = new
|
20
|
+
adoc.url = url
|
21
|
+
adoc
|
22
|
+
end
|
23
|
+
|
24
|
+
def self.from_string(string)
|
25
|
+
adoc = new
|
26
|
+
adoc.string = string
|
27
|
+
adoc
|
28
|
+
end
|
29
|
+
|
30
|
+
def url=(url)
|
31
|
+
if Mapricot.parser == :libxml
|
32
|
+
@udoc = LibXML::XML::Parser.file(url).parse
|
33
|
+
elsif Mapricot.parser == :hpricot
|
34
|
+
@udoc = Hpricot::XML(open(url))
|
35
|
+
elsif Mapricot.paser == :nokogiri
|
36
|
+
@udoc = Nokogiri::HTML(open(url))
|
37
|
+
end
|
38
|
+
end
|
39
|
+
|
40
|
+
def string=(string)
|
41
|
+
if Mapricot.parser == :libxml
|
42
|
+
@udoc = LibXML::XML::Parser.string(string).parse
|
43
|
+
elsif Mapricot.parser == :hpricot
|
44
|
+
@udoc = Hpricot::XML(string)
|
45
|
+
elsif Mapricot.parser == :nokogiri
|
46
|
+
@udoc = Nokogiri::XML(string)
|
47
|
+
end
|
48
|
+
end
|
49
|
+
|
50
|
+
|
51
|
+
def find(tagname)
|
52
|
+
if Mapricot.parser == :libxml
|
53
|
+
AbstractNodeList.new(@udoc.find("//#{tagname}")) # hmm...
|
54
|
+
elsif Mapricot.parser == :hpricot
|
55
|
+
# AbstractNodeList.new(@udoc/tagname)
|
56
|
+
AbstractNodeList.new(@udoc/"//#{tagname}")
|
57
|
+
elsif Mapricot.parser == :nokogiri
|
58
|
+
AbstractNodeList.new(@udoc.search(tagname))
|
59
|
+
# AbstractNodeList.new(@udoc.xpath("//#{tagname}"))
|
60
|
+
end
|
61
|
+
end
|
62
|
+
|
63
|
+
end
|
64
|
+
|
65
|
+
|
66
|
+
|
67
|
+
class AbstractNodeList
|
68
|
+
include Enumerable
|
69
|
+
|
70
|
+
def initialize(node_list)
|
71
|
+
@unode_list = node_list
|
72
|
+
end
|
73
|
+
|
74
|
+
def each(&block)
|
75
|
+
@unode_list.each {|unode| yield(AbstractNode.new(unode))}
|
76
|
+
end
|
77
|
+
|
78
|
+
def [](i)
|
79
|
+
@unode_list[i]
|
80
|
+
end
|
81
|
+
|
82
|
+
def first
|
83
|
+
AbstractNode.new(@unode_list.first)
|
84
|
+
end
|
85
|
+
|
86
|
+
def empty?
|
87
|
+
@unode_list.empty?
|
88
|
+
end
|
89
|
+
end
|
90
|
+
|
91
|
+
|
92
|
+
class AbstractNode
|
93
|
+
attr_reader :unode
|
94
|
+
|
95
|
+
def initialize(unode)
|
96
|
+
@unode = unode # unresolved node
|
97
|
+
end
|
98
|
+
|
99
|
+
def to_s
|
100
|
+
@unode.to_s
|
101
|
+
end
|
102
|
+
|
103
|
+
def attributes
|
104
|
+
if Mapricot.parser != :nokogiri
|
105
|
+
@unode.attributes
|
106
|
+
else
|
107
|
+
atts = {}
|
108
|
+
@unode.attributes.each {|k,v| atts[k] = v.value}
|
109
|
+
atts
|
110
|
+
end
|
111
|
+
end
|
112
|
+
|
113
|
+
def contents
|
114
|
+
if Mapricot.parser == :libxml || Mapricot.parser == :nokogiri
|
115
|
+
@unode.content
|
116
|
+
else
|
117
|
+
@unode.inner_html
|
118
|
+
end
|
119
|
+
end
|
120
|
+
end
|
121
|
+
end
|