sergio 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/Gemfile +5 -0
- data/Gemfile.lock +24 -0
- data/Rakefile +12 -0
- data/lib/sergio.rb +32 -0
- data/lib/sergio/hash_methods.rb +58 -0
- data/lib/sergio/sergio_config.rb +81 -0
- data/lib/sergio/sergio_element.rb +8 -0
- data/lib/sergio/sergio_parsed_document.rb +32 -0
- data/lib/sergio/sergio_sax.rb +45 -0
- data/spec/spec_helper.rb +20 -0
- data/spec/support/buzz_activity_stream.xml +48 -0
- data/spec/support/facebook_activity_stream.xml +43 -0
- data/spec/support/twitter_activity_stream.xml +52 -0
- data/spec/unit/sergio_hash_methods_spec.rb +79 -0
- data/spec/unit/sergio_spec.rb +248 -0
- metadata +108 -0
data/Gemfile
ADDED
data/Gemfile.lock
ADDED
@@ -0,0 +1,24 @@
|
|
1
|
+
GEM
|
2
|
+
remote: http://rubygems.org/
|
3
|
+
specs:
|
4
|
+
diff-lcs (1.1.2)
|
5
|
+
nokogiri (1.4.4)
|
6
|
+
rake (0.9.0)
|
7
|
+
rcov (0.9.9)
|
8
|
+
rspec (2.6.0)
|
9
|
+
rspec-core (~> 2.6.0)
|
10
|
+
rspec-expectations (~> 2.6.0)
|
11
|
+
rspec-mocks (~> 2.6.0)
|
12
|
+
rspec-core (2.6.3)
|
13
|
+
rspec-expectations (2.6.0)
|
14
|
+
diff-lcs (~> 1.1.2)
|
15
|
+
rspec-mocks (2.6.0)
|
16
|
+
|
17
|
+
PLATFORMS
|
18
|
+
ruby
|
19
|
+
|
20
|
+
DEPENDENCIES
|
21
|
+
nokogiri
|
22
|
+
rake
|
23
|
+
rcov
|
24
|
+
rspec
|
data/Rakefile
ADDED
@@ -0,0 +1,12 @@
|
|
1
|
+
# Get your spec rake tasks working in RSpec 2.0
|
2
|
+
|
3
|
+
require 'rspec/core/rake_task'
|
4
|
+
|
5
|
+
desc 'Default: run specs.'
|
6
|
+
task :default => :spec
|
7
|
+
|
8
|
+
desc "Run specs"
|
9
|
+
RSpec::Core::RakeTask.new do |t|
|
10
|
+
t.pattern = "./spec/**/*_spec.rb" # don't need this, it's default.
|
11
|
+
# Put spec opts in a file named .rspec in root
|
12
|
+
end
|
data/lib/sergio.rb
ADDED
@@ -0,0 +1,32 @@
|
|
1
|
+
require 'nokogiri'
|
2
|
+
require File.dirname(__FILE__) + '/sergio/hash_methods'
|
3
|
+
require File.dirname(__FILE__) + '/sergio/sergio_sax'
|
4
|
+
require File.dirname(__FILE__) + '/sergio/sergio_element'
|
5
|
+
require File.dirname(__FILE__) + '/sergio/sergio_config'
|
6
|
+
require File.dirname(__FILE__) + '/sergio/sergio_parsed_document'
|
7
|
+
|
8
|
+
module Sergio
|
9
|
+
def self.included(base)
|
10
|
+
base.extend(ClassMethods)
|
11
|
+
include(Sergio::HashMethods)
|
12
|
+
end
|
13
|
+
|
14
|
+
def sergio_parsed_document
|
15
|
+
@sergio_parsed_document ||= Sergio::ParsedDocument.new
|
16
|
+
end
|
17
|
+
|
18
|
+
def parse(doc)
|
19
|
+
Nokogiri::XML::SAX::Parser.new(SergioSax.new(self)).parse(doc)
|
20
|
+
sergio_parsed_document.parsed_hash
|
21
|
+
end
|
22
|
+
|
23
|
+
module ClassMethods
|
24
|
+
def element(name, newname = nil, args = {}, &blk)
|
25
|
+
sergio_config.element(name, newname, args, &blk)
|
26
|
+
end
|
27
|
+
|
28
|
+
def sergio_config
|
29
|
+
@sergio_config ||= Sergio::Config.new
|
30
|
+
end
|
31
|
+
end
|
32
|
+
end
|
@@ -0,0 +1,58 @@
|
|
1
|
+
module Sergio
|
2
|
+
module HashMethods
|
3
|
+
def hash_from_path(path, val)
|
4
|
+
path = path.clone
|
5
|
+
k = path.shift
|
6
|
+
h = {}
|
7
|
+
h[k] = if path.empty?
|
8
|
+
val
|
9
|
+
else
|
10
|
+
hash_from_path(path, val)
|
11
|
+
end
|
12
|
+
h
|
13
|
+
end
|
14
|
+
|
15
|
+
def value_at_path(path, hash)
|
16
|
+
k = path.shift
|
17
|
+
k = k.is_a?(Array) ? k[0] : k
|
18
|
+
v = hash[k]
|
19
|
+
if v
|
20
|
+
if v.is_a?(Hash) && path.length > 0
|
21
|
+
value_at_path(path, v)
|
22
|
+
else
|
23
|
+
v
|
24
|
+
end
|
25
|
+
else
|
26
|
+
nil
|
27
|
+
end
|
28
|
+
end
|
29
|
+
|
30
|
+
def hash_recursive_merge_to_arrays(lval, rval)
|
31
|
+
r = {}
|
32
|
+
v = lval.merge(rval) do |key, oldval, newval|
|
33
|
+
v = if oldval.class == lval.class
|
34
|
+
hash_recursive_merge_to_arrays(oldval, newval)
|
35
|
+
else
|
36
|
+
if oldval.is_a?(Array)
|
37
|
+
oldval << newval
|
38
|
+
elsif newval.is_a?(Array)
|
39
|
+
newval << oldval
|
40
|
+
else
|
41
|
+
[oldval] << newval
|
42
|
+
end
|
43
|
+
end
|
44
|
+
r[key] = v
|
45
|
+
end
|
46
|
+
v
|
47
|
+
end
|
48
|
+
|
49
|
+
#FROM https://gist.github.com/6391/62b6aae9206abe7b3fea6d4659e4c246f8cf7632
|
50
|
+
def hash_recursive_merge(lval, rval)
|
51
|
+
r = {}
|
52
|
+
v = lval.merge(rval) do |key, oldval, newval|
|
53
|
+
r[key] = oldval.class == Hash && newval.class == Hash ? hash_recursive_merge(oldval, newval) : newval
|
54
|
+
end
|
55
|
+
v
|
56
|
+
end
|
57
|
+
end
|
58
|
+
end
|
@@ -0,0 +1,81 @@
|
|
1
|
+
module Sergio
|
2
|
+
class Config
|
3
|
+
include HashMethods
|
4
|
+
attr_accessor :sergio_elements, :new_path, :current_path
|
5
|
+
def initialize
|
6
|
+
@parsing_elements = {}
|
7
|
+
@new_path, @current_path = [], []
|
8
|
+
end
|
9
|
+
|
10
|
+
def element(name, newname = nil, args = {}, &blk)
|
11
|
+
if newname.is_a?(Hash)
|
12
|
+
args = newname
|
13
|
+
newname = nil
|
14
|
+
end
|
15
|
+
|
16
|
+
args = args.inject({}) do |args, k_v|
|
17
|
+
args[k_v[0].to_sym] = k_v[1]
|
18
|
+
args
|
19
|
+
end
|
20
|
+
|
21
|
+
args[:attribute] ||= '@text'
|
22
|
+
args[:having] ||= false
|
23
|
+
newname = name unless newname
|
24
|
+
name = [name] unless name.is_a?(Array)
|
25
|
+
newname = [newname] unless newname.is_a?(Array)
|
26
|
+
|
27
|
+
name.each do |n|
|
28
|
+
@current_path << n
|
29
|
+
end
|
30
|
+
|
31
|
+
newname.each do |n|
|
32
|
+
@new_path << n
|
33
|
+
end
|
34
|
+
|
35
|
+
unless block_given?
|
36
|
+
blk = lambda do |val|
|
37
|
+
val
|
38
|
+
end
|
39
|
+
end
|
40
|
+
|
41
|
+
#block with more calls to #element
|
42
|
+
if blk.arity < 1
|
43
|
+
blk.call
|
44
|
+
callback = lambda {|v|{}}
|
45
|
+
else
|
46
|
+
callback = blk
|
47
|
+
end
|
48
|
+
|
49
|
+
elem = SergioElement.new(new_path, args, callback)
|
50
|
+
|
51
|
+
@parsing_elements = hash_recursive_merge_to_arrays(@parsing_elements, hash_from_path(current_path, {:sergio_elem => elem}))
|
52
|
+
current_path.pop(name.length)
|
53
|
+
new_path.pop(newname.length)
|
54
|
+
end
|
55
|
+
|
56
|
+
def get_element_configs(path)
|
57
|
+
path = path.clone
|
58
|
+
current_elem = path.last
|
59
|
+
v = value_at_path(path, @parsing_elements)
|
60
|
+
if v
|
61
|
+
v = v[:sergio_elem]
|
62
|
+
if v
|
63
|
+
v = [v] if v.is_a?(SergioElement)
|
64
|
+
vs = v.select do |v|
|
65
|
+
if v.options[:having]
|
66
|
+
match = v.options[:having].any? do |attr,value|
|
67
|
+
current_elem_attrs = current_elem[1]
|
68
|
+
elem_val = current_elem_attrs.assoc(attr.to_s)
|
69
|
+
elem_val[1] == value.to_s if elem_val
|
70
|
+
end
|
71
|
+
true if match
|
72
|
+
else
|
73
|
+
true
|
74
|
+
end
|
75
|
+
end
|
76
|
+
vs
|
77
|
+
end
|
78
|
+
end
|
79
|
+
end
|
80
|
+
end
|
81
|
+
end
|
@@ -0,0 +1,32 @@
|
|
1
|
+
module Sergio
|
2
|
+
class ParsedDocument
|
3
|
+
include HashMethods
|
4
|
+
attr_accessor :parsed_hash
|
5
|
+
|
6
|
+
def initialize
|
7
|
+
@parsed_hash = {}
|
8
|
+
end
|
9
|
+
|
10
|
+
def set_element(path, val, options = {})
|
11
|
+
v = value_at_path(path.clone, self.parsed_hash)
|
12
|
+
|
13
|
+
val = if v
|
14
|
+
if v.is_a? Array
|
15
|
+
v << val
|
16
|
+
else
|
17
|
+
if val.is_a?(Hash) && val.empty?
|
18
|
+
options[:as_array] ? [v] : v
|
19
|
+
else
|
20
|
+
[v] << val
|
21
|
+
end
|
22
|
+
end
|
23
|
+
else
|
24
|
+
options[:as_array] ? [val] : val
|
25
|
+
end
|
26
|
+
|
27
|
+
h = hash_from_path(path, val)
|
28
|
+
@parsed_hash = hash_recursive_merge(self.parsed_hash, h)
|
29
|
+
@parsed_hash
|
30
|
+
end
|
31
|
+
end
|
32
|
+
end
|
@@ -0,0 +1,45 @@
|
|
1
|
+
class SergioSax < Nokogiri::XML::SAX::Document
|
2
|
+
def initialize(object)
|
3
|
+
@stack = []
|
4
|
+
@object = object
|
5
|
+
end
|
6
|
+
|
7
|
+
def start_element(name, attrs = [])
|
8
|
+
@stack << [name, attrs]
|
9
|
+
end
|
10
|
+
|
11
|
+
def characters(string)
|
12
|
+
name, attrs = @stack.last
|
13
|
+
attrs << ['@text', string]
|
14
|
+
end
|
15
|
+
|
16
|
+
def cdata_block(string)
|
17
|
+
characters(string)
|
18
|
+
end
|
19
|
+
|
20
|
+
def end_element(name)
|
21
|
+
e_context = @stack.clone
|
22
|
+
name, attrs = @stack.pop
|
23
|
+
if sergio_elements = @object.class.sergio_config.get_element_configs(e_context)
|
24
|
+
sergio_elements.each do |sergio_element|
|
25
|
+
attr = sergio_element.options[:attribute]
|
26
|
+
val = attrs.assoc(attr)
|
27
|
+
if val
|
28
|
+
val = val[1]
|
29
|
+
hash_path = sergio_element.new_path
|
30
|
+
callback = sergio_element.callback
|
31
|
+
|
32
|
+
r = if callback.arity == 1
|
33
|
+
callback.call(val)
|
34
|
+
elsif callback.arity == 2
|
35
|
+
h = Hash[*attrs.flatten]
|
36
|
+
h.delete('@text')
|
37
|
+
callback.call(val, Hash[*attrs.flatten])
|
38
|
+
end
|
39
|
+
|
40
|
+
@object.sergio_parsed_document.set_element(hash_path, r, sergio_element.options)
|
41
|
+
end
|
42
|
+
end
|
43
|
+
end
|
44
|
+
end
|
45
|
+
end
|
data/spec/spec_helper.rb
ADDED
@@ -0,0 +1,20 @@
|
|
1
|
+
require 'rspec'
|
2
|
+
require File.dirname(__FILE__) + '/../lib/sergio'
|
3
|
+
|
4
|
+
# Requires supporting files with custom matchers and macros, etc,
|
5
|
+
# in ./support/ and its subdirectories.
|
6
|
+
Dir["#{File.dirname(__FILE__)}/support/**/*.rb"].each {|f| require f}
|
7
|
+
|
8
|
+
RSpec.configure do |config|
|
9
|
+
|
10
|
+
end
|
11
|
+
|
12
|
+
def new_sergio(&blk)
|
13
|
+
c = Class.new do
|
14
|
+
include Sergio
|
15
|
+
end
|
16
|
+
if block_given?
|
17
|
+
c.class_exec(&blk)
|
18
|
+
end
|
19
|
+
c
|
20
|
+
end
|
@@ -0,0 +1,48 @@
|
|
1
|
+
<entry xmlns="http://www.w3.org/2005/Atom" xmlns:georss="http://www.georss.org/georss" xmlns:media="http://search.yahoo.com/mrss/" xmlns:buzz="http://schemas.google.com/buzz/2010" xmlns:crosspost="http://purl.org/syndication/cross-posting" xmlns:gd="http://schemas.google.com/g/2005" xmlns:thr="http://purl.org/syndication/thread/1.0" xmlns:activity="http://activitystrea.ms/spec/1.0/" xmlns:poco="http://portablecontacts.net/ns/1.0" gd:kind="buzz#activity">
|
2
|
+
<title>Seated figure</title>
|
3
|
+
<published>2011-05-20T15:30:02.000Z</published>
|
4
|
+
<updated>2011-05-20T15:35:30.184Z</updated>
|
5
|
+
<id>tag:google.com,2010:buzz:z12dgfjgkwabv3egs04ch3kqzqnscp2xvhc</id>
|
6
|
+
<link href="https://profiles.google.com/101327999921150687436/posts/SUjANDpeWVo" type="text/html" rel="alternate"/>
|
7
|
+
<link href="https://www.googleapis.com/buzz/v1/activities/101327999921150687436/@self/B:z12dgfjgkwabv3egs04ch3kqzqnscp2xvhc?alt=atom" type="application/atom+xml" rel="self"/>
|
8
|
+
<link thr:count="0" href="https://www.googleapis.com/buzz/v1/activities/101327999921150687436/@self/B:z12dgfjgkwabv3egs04ch3kqzqnscp2xvhc/@comments?alt=atom" type="application/atom+xml" thr:updated="2011-05-20T15:35:30.184Z" rel="replies"/>
|
9
|
+
<author>
|
10
|
+
<poco:id>101327999921150687436</poco:id>
|
11
|
+
<poco:photoUrl/>
|
12
|
+
<name>Roy Patrick Tan</name>
|
13
|
+
<uri>https://profiles.google.com/101327999921150687436</uri>
|
14
|
+
<link href="" type="image/jpeg" rel="photo"/>
|
15
|
+
<activity:object-type>http://activitystrea.ms/schema/1.0/person</activity:object-type>
|
16
|
+
</author>
|
17
|
+
<content type="html">Seated figure</content>
|
18
|
+
<activity:verb>http://activitystrea.ms/schema/1.0/post</activity:verb>
|
19
|
+
<activity:object>
|
20
|
+
<activity:object-type>http://activitystrea.ms/schema/1.0/note</activity:object-type>
|
21
|
+
<content type="html">Seated figure</content>
|
22
|
+
<buzz:original-content type="text"/>
|
23
|
+
<link href="https://profiles.google.com/101327999921150687436/posts/SUjANDpeWVo" type="text/html" rel="alternate"/>
|
24
|
+
<buzz:attachment>
|
25
|
+
<activity:object-type>http://activitystrea.ms/schema/1.0/photo</activity:object-type>
|
26
|
+
<title>Seated figure</title>
|
27
|
+
<link media:width="1744" href="http://farm4.static.flickr.com/3539/5740322886_f9273216ea_o.jpg" type="image/jpeg" rel="enclosure" media:height="2331"/>
|
28
|
+
<link href="http://www.flickr.com/photos/8962549@N02/5740322886" type="text/html" rel="alternate"/>
|
29
|
+
<link href="http://images0-focus-opensocial.googleusercontent.com/gadgets/proxy?container=focus&gadget=a&resize_h=100&url=http%3A%2F%2Ffarm4.static.flickr.com%2F3539%2F5740322886_ebcef1bbec_m.jpg" type="image/jpeg" rel="preview"/>
|
30
|
+
<link href="http://images0-focus-opensocial.googleusercontent.com/gadgets/proxy?container=focus&gadget=a&resize_h=100&url=http%3A%2F%2Ffarm4.static.flickr.com%2F3539%2F5740322886_ebcef1bbec_b.jpg" type="image/jpeg" rel="preview"/>
|
31
|
+
</buzz:attachment>
|
32
|
+
</activity:object>
|
33
|
+
<source>
|
34
|
+
<activity:service>
|
35
|
+
<title>Flickr</title>
|
36
|
+
</activity:service>
|
37
|
+
<id>tag:google.com,2010:buzz-feed:public:posted:101327999921150687436</id>
|
38
|
+
<updated>2011-05-20T15:35:30.184Z</updated>
|
39
|
+
<link href="https://www.googleapis.com/buzz/v1/activities/101327999921150687436/@public?alt=atom" type="application/atom+xml" rel="self"/>
|
40
|
+
<link href="http://pubsubhubbub.appspot.com/" rel="hub"/>
|
41
|
+
</source>
|
42
|
+
<buzz:visibility>
|
43
|
+
<buzz:aclentry type="group">
|
44
|
+
<poco:name>Public</poco:name>
|
45
|
+
</buzz:aclentry>
|
46
|
+
</buzz:visibility>
|
47
|
+
<link buzz:count="0" href="https://www.googleapis.com/buzz/v1/activities/101327999921150687436/@self/B:z12dgfjgkwabv3egs04ch3kqzqnscp2xvhc/@liked?alt=atom" type="application/poco+xml" rel="http://schemas.google.com/buzz/2010#liked"/>
|
48
|
+
</entry>
|
@@ -0,0 +1,43 @@
|
|
1
|
+
<entry xmlns="http://www.w3.org/2005/Atom" xmlns:activity="http://activitystrea.ms/spec/1.0/" xmlns:service="http://activitystrea.ms/service-provider" xmlns:thr="http://purl.org/syndication/thread/1.0" xmlns:gnip="http://www.gnip.com/schemas/2010">
|
2
|
+
<id>100000601711657_225559834120776</id>
|
3
|
+
<created>2011-05-19T23:13:57+00:00</created>
|
4
|
+
<published>2011-05-19T23:13:57+00:00</published>
|
5
|
+
<updated>2011-05-19T23:13:57+00:00</updated>
|
6
|
+
<title>Shane Smith posted a bookmark to Facebook</title>
|
7
|
+
<category term="BookmarkPosted" label="Bookmark Posted"/>
|
8
|
+
<link rel="alternate" type="html" href="http://www.facebook.com/profile.php?id=100000601711657&v=wall&story_fbid=225559834120776"/>
|
9
|
+
<generator uri="http://www.facebook.com/apps/application.php?id=2344061033">Events</generator>
|
10
|
+
<source>
|
11
|
+
<link rel="self" type="application/json" href="https://graph.facebook.com/search?q=party&type=post&limit=75&access_token=&since=Thu+May+19+19%3A13%3A32+-0400+2011"/>
|
12
|
+
<title>Facebook - Keyword - Search - party</title>
|
13
|
+
<updated>2011-05-19T23-14-05Z</updated>
|
14
|
+
<gnip:rule xmlns:gnip="http://www.gnip.com/schemas/2010">party</gnip:rule>
|
15
|
+
</source>
|
16
|
+
<service:provider>
|
17
|
+
<name>Facebook</name>
|
18
|
+
<uri>www.facebook.com</uri>
|
19
|
+
<icon/>
|
20
|
+
</service:provider>
|
21
|
+
<activity:verb>http://activitystrea.ms/schema/1.0/post</activity:verb>
|
22
|
+
<activity:object>
|
23
|
+
<activity:object-type>http://activitystrea.ms/schema/1.0/bookmark</activity:object-type>
|
24
|
+
<id>100000601711657_225559834120776</id>
|
25
|
+
<title>GRADUATION PARTY!!!</title>
|
26
|
+
<content>Come to my Graduation Party on June 18th!!!</content>
|
27
|
+
<link rel="alternate" type="html" href="http://www.facebook.com/profile.php?id=100000601711657&v=wall&story_fbid=225559834120776"/>
|
28
|
+
<link rel="related" href="http://www.facebook.com/event.php?eid=184487541600108"/>
|
29
|
+
<link rel="preview" href="http://profile.ak.fbcdn.net/hprofile-ak-snc4/187804_184487541600108_5482560_s.jpg"/>
|
30
|
+
</activity:object>
|
31
|
+
<author>
|
32
|
+
<name>Shane Smith</name>
|
33
|
+
<uri>http://www.facebook.com/profile.php?id=100000601711657</uri>
|
34
|
+
</author>
|
35
|
+
<activity:actor>
|
36
|
+
<activity:object-type>http://activitystrea.ms/schema/1.0/person</activity:object-type>
|
37
|
+
<link rel="alternate" type="html" length="0" href="http://www.facebook.com/profile.php?id=100000601711657"/>
|
38
|
+
</activity:actor>
|
39
|
+
<gnip:matching_rules>
|
40
|
+
<gnip:matching_rule rel="source">party</gnip:matching_rule>
|
41
|
+
<gnip:matching_rule rel="source">neat</gnip:matching_rule>
|
42
|
+
</gnip:matching_rules>
|
43
|
+
</entry>
|
@@ -0,0 +1,52 @@
|
|
1
|
+
<entry xmlns="http://www.w3.org/2005/Atom" xmlns:gnip="http://www.gnip.com/schemas/2010">
|
2
|
+
<id>tag:search.twitter.com,2005:71230904948883456</id>
|
3
|
+
<published>2011-05-19T15:09:05+00:00</published>
|
4
|
+
<updated>2011-05-19T15:09:05+00:00</updated>
|
5
|
+
<summary type="html">@mrpotatochipman cool ;)</summary>
|
6
|
+
<link rel="alternate" type="text/html" href="http://twitter.com/_P3PP_CE/statuses/71230904948883456"/>
|
7
|
+
<source>
|
8
|
+
<link rel="self" type="application/json" href="http://stream.twitter.com/1/statuses/filter.json"/>
|
9
|
+
<title>Twitter - Stream - Track</title>
|
10
|
+
<updated>2011-05-19T15:09:08Z</updated>
|
11
|
+
</source>
|
12
|
+
<service:provider xmlns:service="http://activitystrea.ms/service-provider">
|
13
|
+
<name>Twitter</name>
|
14
|
+
<uri>http://www.twitter.com/</uri>
|
15
|
+
<icon/>
|
16
|
+
</service:provider>
|
17
|
+
<title>P3PP posted a note on Twitter</title>
|
18
|
+
<category term="StatusPosted" label="Status Posted"/>
|
19
|
+
<category term="NotePosted" label="Note Posted"/>
|
20
|
+
<activity:verb xmlns:activity="http://activitystrea.ms/spec/1.0/">http://activitystrea.ms/schema/1.0/post</activity:verb>
|
21
|
+
<activity:object xmlns:activity="http://activitystrea.ms/spec/1.0/">
|
22
|
+
<activity:object-type>http://activitystrea.ms/schema/1.0/note</activity:object-type>
|
23
|
+
<id>object:search.twitter.com,2005:71230904948883456</id>
|
24
|
+
<content type="html">@mrpotatochipman cool ;)</content>
|
25
|
+
<link rel="alternate" type="text/html" href="http://twitter.com/_P3PP_CE/statuses/71230904948883456"/>
|
26
|
+
<thr:in-reply-to xmlns:thr="http://purl.org/syndication/thread/1.0" type="text/html" href="http://twitter.com/mrpotatochipman/statuses/71229949155086336"/>
|
27
|
+
</activity:object>
|
28
|
+
<author>
|
29
|
+
<name>P3PP</name>
|
30
|
+
<uri>http://www.twitter.com/_P3PP_CE</uri>
|
31
|
+
</author>
|
32
|
+
<activity:author xmlns:activity="http://activitystrea.ms/spec/1.0/">
|
33
|
+
<activity:object-type>http://activitystrea.ms/schema/1.0/person</activity:object-type>
|
34
|
+
<gnip:friends xmlns:gnip="http://www.gnip.com/schemas/2010" followersCount="911" followingCount="839"/>
|
35
|
+
<link rel="alternate" type="text/html" length="0" href="http://www.twitter.com/_P3PP_CE"/>
|
36
|
+
<link rel="avatar" href="http://a0.twimg.com/profile_images/1352659419/l_15_normal.jpg"/>
|
37
|
+
<id>http://www.twitter.com/_P3PP_CE</id>
|
38
|
+
</activity:author>
|
39
|
+
<activity:actor xmlns:activity="http://activitystrea.ms/spec/1.0/">
|
40
|
+
<activity:object-type>http://activitystrea.ms/schema/1.0/person</activity:object-type>
|
41
|
+
<gnip:friends xmlns:gnip="http://www.gnip.com/schemas/2010" followersCount="911" followingCount="839"/>
|
42
|
+
<gnip:stats xmlns:gnip="http://www.gnip.com/schemas/2010" activityCount="22964" upstreamId="id:twitter.com:125163780"/>
|
43
|
+
<link rel="alternate" type="text/html" length="0" href="http://www.twitter.com/_P3PP_CE"/>
|
44
|
+
<link rel="avatar" href="http://a0.twimg.com/profile_images/1352659419/l_15_normal.jpg"/>
|
45
|
+
<id>http://www.twitter.com/_P3PP_CE</id>
|
46
|
+
<os:location xmlns:os="http://ns.opensocial.org/2008/opensocial">Back N Da LAND,Oh10</os:location>
|
47
|
+
<os:aboutMe xmlns:os="http://ns.opensocial.org/2008/opensocial">Jus A Laid Back Type Of Chick, Who Aint On No B.S Mad Cool. #TeamGemini, #RMF, #BBN, #TFB, #TeamDroid #TeamSingle Reppn Im Owt ;) </os:aboutMe>
|
48
|
+
</activity:actor>
|
49
|
+
<gnip:matching_rules>
|
50
|
+
<gnip:matching_rule rel="inferred" tag="NEAT">cool</gnip:matching_rule>
|
51
|
+
</gnip:matching_rules>
|
52
|
+
</entry>
|
@@ -0,0 +1,79 @@
|
|
1
|
+
require 'spec_helper'
|
2
|
+
|
3
|
+
describe Sergio::HashMethods do
|
4
|
+
context 'value_at_path' do
|
5
|
+
before do
|
6
|
+
@s = Class.new do
|
7
|
+
include Sergio::HashMethods
|
8
|
+
end.new
|
9
|
+
end
|
10
|
+
|
11
|
+
it 'gets the value from passed in hash at point in heirarchy specified by passed in array' do
|
12
|
+
v = @s.value_at_path([['thing'], ['stuff']], {'thing' => {'stuff' => '2'}})
|
13
|
+
v.should == '2'
|
14
|
+
end
|
15
|
+
|
16
|
+
it 'gets the value from passed in hash at point in heirarchy specified by passed in array if array is one element long' do
|
17
|
+
v = @s.value_at_path([['thing']], {'thing' => {'stuff' => '2'}})
|
18
|
+
v.should == {'stuff' => '2'}
|
19
|
+
end
|
20
|
+
|
21
|
+
it 'returns nil if value is not present in hash' do
|
22
|
+
v = @s.value_at_path(['thing', 'stuff'], {'thing' => {'guy' => '2'}})
|
23
|
+
v.should == nil
|
24
|
+
end
|
25
|
+
end
|
26
|
+
|
27
|
+
context 'hash_recursive_merge_to_arrays' do
|
28
|
+
before do
|
29
|
+
@s = Class.new do
|
30
|
+
include Sergio::HashMethods
|
31
|
+
end.new
|
32
|
+
end
|
33
|
+
|
34
|
+
|
35
|
+
it 'merges subhashes together' do
|
36
|
+
h1 = {:thing => {'guy' => 'cool'}}
|
37
|
+
h2 = {:thing => {'guys' => 'cool'}}
|
38
|
+
@s.hash_recursive_merge_to_arrays(h1, h2).should == {:thing => {'guy' => 'cool', 'guys' => 'cool'}}
|
39
|
+
end
|
40
|
+
|
41
|
+
it 'builds an array out of values on intersecting keys' do
|
42
|
+
h1 = {:thing => {'guy' => 'cool'}}
|
43
|
+
h2 = {:thing => {'guy' => 'cool'}}
|
44
|
+
@s.hash_recursive_merge_to_arrays(h1, h2).should == {:thing => {'guy' => ['cool', 'cool']}}
|
45
|
+
end
|
46
|
+
|
47
|
+
it 'appends to array if in value of left intersecting key' do
|
48
|
+
h1 = {:thing => {'guy' => ['cool', 'neat']}}
|
49
|
+
h2 = {:thing => {'guy' => 'cool'}}
|
50
|
+
@s.hash_recursive_merge_to_arrays(h1, h2).should == {:thing => {'guy' => ['cool', 'neat', 'cool']}}
|
51
|
+
end
|
52
|
+
|
53
|
+
it 'appends to array if in value of right intersecting key' do
|
54
|
+
h1 = {:thing => {'guy' => 'cool'}}
|
55
|
+
h2 = {:thing => {'guy' => ['cool', 'neat']}}
|
56
|
+
@s.hash_recursive_merge_to_arrays(h1, h2).should == {:thing => {'guy' => ['cool', 'neat', 'cool']}}
|
57
|
+
end
|
58
|
+
end
|
59
|
+
|
60
|
+
context 'hash_recursive_merge' do
|
61
|
+
before do
|
62
|
+
@s = Class.new do
|
63
|
+
include Sergio::HashMethods
|
64
|
+
end.new
|
65
|
+
end
|
66
|
+
|
67
|
+
it 'merges subhashes together' do
|
68
|
+
h1 = {:thing => {'guy' => 'cool'}}
|
69
|
+
h2 = {:thing => {'guys' => 'cool'}}
|
70
|
+
@s.hash_recursive_merge(h1, h2).should == {:thing => {'guy' => 'cool', 'guys' => 'cool'}}
|
71
|
+
end
|
72
|
+
|
73
|
+
it 'replaces intersecting key values with rightmost hash argument value' do
|
74
|
+
h1 = {:thing => {'guy' => 'cool'}}
|
75
|
+
h2 = {:thing => {'guy' => 'cools'}}
|
76
|
+
@s.hash_recursive_merge(h1, h2).should == {:thing => {'guy' => 'cools'}}
|
77
|
+
end
|
78
|
+
end
|
79
|
+
end
|
@@ -0,0 +1,248 @@
|
|
1
|
+
require 'spec_helper'
|
2
|
+
|
3
|
+
describe Sergio do
|
4
|
+
context 'element' do
|
5
|
+
it 'parses an element' do
|
6
|
+
s = new_sergio do
|
7
|
+
element 'id'
|
8
|
+
end
|
9
|
+
|
10
|
+
@xml = "<id>1</id>"
|
11
|
+
@hash = s.new.parse(@xml)
|
12
|
+
@hash['id'].should == '1'
|
13
|
+
end
|
14
|
+
|
15
|
+
it 'parses duplicate elements into an array' do
|
16
|
+
s = new_sergio do
|
17
|
+
element 'parent' do
|
18
|
+
element 'id'
|
19
|
+
end
|
20
|
+
end
|
21
|
+
|
22
|
+
@xml = "<parent><id>1</id><id>2</id></parent>"
|
23
|
+
@hash = s.new.parse(@xml)
|
24
|
+
@hash['parent']['id'].should == ['1', '2']
|
25
|
+
end
|
26
|
+
|
27
|
+
it 'parses duplicate elements whose callbacks return a hash into an array' do
|
28
|
+
s = new_sergio do
|
29
|
+
element 'parent' do
|
30
|
+
element 'id' do |v|
|
31
|
+
{'v' => v}
|
32
|
+
end
|
33
|
+
end
|
34
|
+
end
|
35
|
+
|
36
|
+
@xml = "<parent><id>1</id><id>2</id></parent>"
|
37
|
+
@hash = s.new.parse(@xml)
|
38
|
+
@hash['parent']['id'].should == [{'v' => '1',}, {'v' => '2'}]
|
39
|
+
end
|
40
|
+
|
41
|
+
it 'parses a nested element' do
|
42
|
+
s = new_sergio do
|
43
|
+
element 'ip' do
|
44
|
+
element 'man'
|
45
|
+
end
|
46
|
+
end
|
47
|
+
|
48
|
+
@xml = "<ip><man>neat</man></ip>"
|
49
|
+
@hash = s.new.parse(@xml)
|
50
|
+
@hash['ip']['man'].should == 'neat'
|
51
|
+
end
|
52
|
+
|
53
|
+
it 'renames an element to second argument passed to #element if argument is provided' do
|
54
|
+
s = new_sergio do
|
55
|
+
element 'man', 'guy'
|
56
|
+
end
|
57
|
+
|
58
|
+
@xml = "<man>neat</man>"
|
59
|
+
@hash = s.new.parse(@xml)
|
60
|
+
@hash['guy'].should == 'neat'
|
61
|
+
end
|
62
|
+
|
63
|
+
it 'passes value into block if block has an arity of 1. And sets element value to block result' do
|
64
|
+
s = new_sergio do
|
65
|
+
element 'man' do |v|
|
66
|
+
v.reverse
|
67
|
+
end
|
68
|
+
end
|
69
|
+
|
70
|
+
@xml = "<man>neat</man>"
|
71
|
+
@hash = s.new.parse(@xml)
|
72
|
+
@hash['man'].should == 'taen'
|
73
|
+
end
|
74
|
+
|
75
|
+
it 'passes value and attributes into block if block has an arity of 2 and sets element value to block result' do
|
76
|
+
s = new_sergio do
|
77
|
+
element 'man' do |v, attrs|
|
78
|
+
{'u' => attrs['it'], 'v' => v.reverse}
|
79
|
+
end
|
80
|
+
end
|
81
|
+
|
82
|
+
@xml = "<man it='u'>neat</man>"
|
83
|
+
@hash = s.new.parse(@xml)
|
84
|
+
@hash['man'].should == {'u' => 'u', 'v' => 'taen'}
|
85
|
+
end
|
86
|
+
|
87
|
+
it 'renames a parent element to second argument passed to #element if argument is provided' do
|
88
|
+
s = new_sergio do
|
89
|
+
element 'parent', 'post' do
|
90
|
+
element 'id'
|
91
|
+
end
|
92
|
+
end
|
93
|
+
|
94
|
+
@xml = "<parent><id>1</id><id>2</id></parent>"
|
95
|
+
@hash = s.new.parse(@xml)
|
96
|
+
@hash['post'].should == {'id' => ['1', '2']}
|
97
|
+
end
|
98
|
+
|
99
|
+
it 'renames a child element to second argument passed to #element if argument is provided' do
|
100
|
+
s = new_sergio do
|
101
|
+
element 'parent', 'post' do
|
102
|
+
element 'id', 'di'
|
103
|
+
end
|
104
|
+
end
|
105
|
+
|
106
|
+
@xml = "<parent><id>1</id><id>2</id></parent>"
|
107
|
+
@hash = s.new.parse(@xml)
|
108
|
+
@hash['post'].should == {'di' => ['1', '2']}
|
109
|
+
end
|
110
|
+
|
111
|
+
it 'matches against attributes using :having argument' do
|
112
|
+
s = new_sergio do
|
113
|
+
element 'parent', 'post' do
|
114
|
+
element 'ya', 'kewl', :attribute => 'location', :having => {:href => 'cool', :poopy => 'true'}
|
115
|
+
end
|
116
|
+
end
|
117
|
+
|
118
|
+
@xml = "<parent><ha href='imahref'>neat</ha><ha href='cool'>rad</ha><ya href='cool' poopy='true' location='my house'>wee</ya></parent>"
|
119
|
+
@hash = s.new.parse(@xml)
|
120
|
+
@hash['post']['kewl'].should == 'my house'
|
121
|
+
end
|
122
|
+
|
123
|
+
it 'forces aggregation of matching results into an array even for one match if passed :as_array => true' do
|
124
|
+
s = new_sergio do
|
125
|
+
element 'parent', 'post' do
|
126
|
+
element 'ya', 'kewl', :attribute => 'location', :having => {:href => 'cool', :poopy => 'true'}, :as_array => true
|
127
|
+
end
|
128
|
+
end
|
129
|
+
|
130
|
+
@xml = "
|
131
|
+
<parent>
|
132
|
+
<ha href='imahref'>neat</ha>
|
133
|
+
<ha href='cool'>rad</ha>
|
134
|
+
<ya href='cool' poopy='true' location='my house'>wee</ya>
|
135
|
+
</parent>"
|
136
|
+
@hash = s.new.parse(@xml)
|
137
|
+
@hash['post']['kewl'].should == ['my house']
|
138
|
+
end
|
139
|
+
|
140
|
+
it 'matches against multiple elements within same parent' do
|
141
|
+
s = new_sergio do
|
142
|
+
element 'parent', 'post' do
|
143
|
+
element 'ha', 'link', :having => {:href => 'cool'} do |val|
|
144
|
+
val.reverse
|
145
|
+
end
|
146
|
+
element 'ya', 'kewl', :attribute => 'location', :having => {:href => 'cool', :poopy => 'true'}
|
147
|
+
end
|
148
|
+
end
|
149
|
+
|
150
|
+
@xml = "<parent>
|
151
|
+
<ha href='imahref'>neat</ha>
|
152
|
+
<ha href='cool'>rad</ha><ya href='cool' poopy='true' location='my house'>wee</ya>
|
153
|
+
</parent>"
|
154
|
+
|
155
|
+
@hash = s.new.parse(@xml)
|
156
|
+
@hash['post']['link'].should == 'dar'
|
157
|
+
@hash['post']['kewl'].should == 'my house'
|
158
|
+
end
|
159
|
+
|
160
|
+
it 'matches against multiple elements of the same type within same parent' do
|
161
|
+
s = new_sergio do
|
162
|
+
element 'p' do
|
163
|
+
element 'a', :attribute => 'href' do |v|
|
164
|
+
v.upcase
|
165
|
+
end
|
166
|
+
element 'a', :attribute => 'cool' do |v|
|
167
|
+
v.upcase
|
168
|
+
end
|
169
|
+
end
|
170
|
+
end
|
171
|
+
|
172
|
+
@xml = "<p><a href='cool'>hi</a><a cool='true'>hy</a></p>"
|
173
|
+
@hash = s.new.parse(@xml)
|
174
|
+
@hash['p']['a'].should == ['COOL','TRUE']
|
175
|
+
end
|
176
|
+
|
177
|
+
it 'uses value of attribute using :attribute argument' do
|
178
|
+
s = new_sergio do
|
179
|
+
element 'a', 'link', :attribute => 'href'
|
180
|
+
end
|
181
|
+
|
182
|
+
@xml = "<a href='neat'>neat</a>"
|
183
|
+
@hash = s.new.parse(@xml)
|
184
|
+
@hash['link'].should == 'neat'
|
185
|
+
end
|
186
|
+
|
187
|
+
it 'accepts an array as the first argument to specify a path to an element' do
|
188
|
+
s = new_sergio do
|
189
|
+
element ['a', 'b'], 'c', :attribute => 'y'
|
190
|
+
end
|
191
|
+
|
192
|
+
@xml = '<a><b y="what"></b></a>'
|
193
|
+
@hash = s.new.parse(@xml)
|
194
|
+
@hash['c'].should == 'what'
|
195
|
+
end
|
196
|
+
|
197
|
+
it 'accepts an array as the second argument to specify a path to an element to merge to' do
|
198
|
+
s = new_sergio do
|
199
|
+
element 'a', ['b', 'c'], :attribute => 'y'
|
200
|
+
end
|
201
|
+
|
202
|
+
@xml = '<a y="what"></a>'
|
203
|
+
@hash = s.new.parse(@xml)
|
204
|
+
@hash['b'].should == {'c' => 'what'}
|
205
|
+
end
|
206
|
+
|
207
|
+
it 'accepts an array for both arguments to specify a path to an element to get value from and to merge to' do
|
208
|
+
s = new_sergio do
|
209
|
+
element 'p' do
|
210
|
+
element 'd'
|
211
|
+
element ['a', 'b'], ['b', 'c'], :attribute => 'y'
|
212
|
+
element 'a'
|
213
|
+
end
|
214
|
+
end
|
215
|
+
|
216
|
+
@xml = '<p><d>a</d><a y="what"><b y="hi"></b></a></p>'
|
217
|
+
@hash = s.new.parse(@xml)
|
218
|
+
@hash['p']['b'].should == {'c' => 'hi'}
|
219
|
+
end
|
220
|
+
|
221
|
+
it 'accepts a class which includes sergio as an argument' do
|
222
|
+
class Pozt
|
223
|
+
include Sergio
|
224
|
+
|
225
|
+
element 'b'
|
226
|
+
end
|
227
|
+
|
228
|
+
s = new_sergio do
|
229
|
+
element 'a', Pozt
|
230
|
+
end
|
231
|
+
|
232
|
+
@xml = '<a y="what"><b>hi</b></a>'
|
233
|
+
@hash = s.new.parse(@xml)
|
234
|
+
@hash['a']['b'].should == {'c' => 'what'}
|
235
|
+
end
|
236
|
+
end
|
237
|
+
|
238
|
+
context 'set_element' do
|
239
|
+
before do
|
240
|
+
@s = new_sergio
|
241
|
+
end
|
242
|
+
|
243
|
+
it 'sets the element at the appropriate point in the hierarchy' do
|
244
|
+
v = @s.new.sergio_parsed_document.set_element(['thing', 'stuff'], '1')
|
245
|
+
v.should == {'thing' => {'stuff' => '1'}}
|
246
|
+
end
|
247
|
+
end
|
248
|
+
end
|
metadata
ADDED
@@ -0,0 +1,108 @@
|
|
1
|
+
--- !ruby/object:Gem::Specification
|
2
|
+
name: sergio
|
3
|
+
version: !ruby/object:Gem::Version
|
4
|
+
prerelease:
|
5
|
+
version: 0.0.1
|
6
|
+
platform: ruby
|
7
|
+
authors:
|
8
|
+
- Max Justus Spransy
|
9
|
+
autorequire:
|
10
|
+
bindir: bin
|
11
|
+
cert_chain: []
|
12
|
+
|
13
|
+
date: 2011-05-25 00:00:00 -05:00
|
14
|
+
default_executable:
|
15
|
+
dependencies:
|
16
|
+
- !ruby/object:Gem::Dependency
|
17
|
+
name: nokogiri
|
18
|
+
prerelease: false
|
19
|
+
requirement: &id001 !ruby/object:Gem::Requirement
|
20
|
+
none: false
|
21
|
+
requirements:
|
22
|
+
- - ">="
|
23
|
+
- !ruby/object:Gem::Version
|
24
|
+
version: "0"
|
25
|
+
type: :runtime
|
26
|
+
version_requirements: *id001
|
27
|
+
- !ruby/object:Gem::Dependency
|
28
|
+
name: rspec
|
29
|
+
prerelease: false
|
30
|
+
requirement: &id002 !ruby/object:Gem::Requirement
|
31
|
+
none: false
|
32
|
+
requirements:
|
33
|
+
- - ~>
|
34
|
+
- !ruby/object:Gem::Version
|
35
|
+
version: 2.5.0
|
36
|
+
type: :development
|
37
|
+
version_requirements: *id002
|
38
|
+
- !ruby/object:Gem::Dependency
|
39
|
+
name: rake
|
40
|
+
prerelease: false
|
41
|
+
requirement: &id003 !ruby/object:Gem::Requirement
|
42
|
+
none: false
|
43
|
+
requirements:
|
44
|
+
- - ">="
|
45
|
+
- !ruby/object:Gem::Version
|
46
|
+
version: "0"
|
47
|
+
type: :development
|
48
|
+
version_requirements: *id003
|
49
|
+
description: "\n Sergio provides a declarative syntax for parsing unruly xml into nice pretty hashes.\n "
|
50
|
+
email:
|
51
|
+
- maxjustus@gmail.com
|
52
|
+
executables: []
|
53
|
+
|
54
|
+
extensions: []
|
55
|
+
|
56
|
+
extra_rdoc_files: []
|
57
|
+
|
58
|
+
files:
|
59
|
+
- Gemfile
|
60
|
+
- Gemfile.lock
|
61
|
+
- Rakefile
|
62
|
+
- lib/sergio.rb
|
63
|
+
- lib/sergio/hash_methods.rb
|
64
|
+
- lib/sergio/sergio_config.rb
|
65
|
+
- lib/sergio/sergio_element.rb
|
66
|
+
- lib/sergio/sergio_parsed_document.rb
|
67
|
+
- lib/sergio/sergio_sax.rb
|
68
|
+
- spec/spec_helper.rb
|
69
|
+
- spec/support/buzz_activity_stream.xml
|
70
|
+
- spec/support/facebook_activity_stream.xml
|
71
|
+
- spec/support/twitter_activity_stream.xml
|
72
|
+
- spec/unit/sergio_hash_methods_spec.rb
|
73
|
+
- spec/unit/sergio_spec.rb
|
74
|
+
has_rdoc: true
|
75
|
+
homepage: https://github.com/maxjustus/Sergio
|
76
|
+
licenses: []
|
77
|
+
|
78
|
+
post_install_message:
|
79
|
+
rdoc_options: []
|
80
|
+
|
81
|
+
require_paths:
|
82
|
+
- lib
|
83
|
+
required_ruby_version: !ruby/object:Gem::Requirement
|
84
|
+
none: false
|
85
|
+
requirements:
|
86
|
+
- - ">="
|
87
|
+
- !ruby/object:Gem::Version
|
88
|
+
version: "0"
|
89
|
+
required_rubygems_version: !ruby/object:Gem::Requirement
|
90
|
+
none: false
|
91
|
+
requirements:
|
92
|
+
- - ">="
|
93
|
+
- !ruby/object:Gem::Version
|
94
|
+
version: "0"
|
95
|
+
requirements: []
|
96
|
+
|
97
|
+
rubyforge_project: sergio
|
98
|
+
rubygems_version: 1.6.2
|
99
|
+
signing_key:
|
100
|
+
specification_version: 3
|
101
|
+
summary: SAXy xml to hash transformation.
|
102
|
+
test_files:
|
103
|
+
- spec/spec_helper.rb
|
104
|
+
- spec/support/buzz_activity_stream.xml
|
105
|
+
- spec/support/facebook_activity_stream.xml
|
106
|
+
- spec/support/twitter_activity_stream.xml
|
107
|
+
- spec/unit/sergio_hash_methods_spec.rb
|
108
|
+
- spec/unit/sergio_spec.rb
|