titi 0.0.1

Sign up to get free protection for your applications and to get access to all the features.
@@ -0,0 +1,5 @@
1
+ README.rdoc
2
+ lib/**/*.rb
3
+ bin/*
4
+ features/**/*.feature
5
+ LICENSE
@@ -0,0 +1,21 @@
1
+ ## MAC OS
2
+ .DS_Store
3
+
4
+ ## TEXTMATE
5
+ *.tmproj
6
+ tmtags
7
+
8
+ ## EMACS
9
+ *~
10
+ \#*
11
+ .\#*
12
+
13
+ ## VIM
14
+ *.swp
15
+
16
+ ## PROJECT::GENERAL
17
+ coverage
18
+ rdoc
19
+ pkg
20
+
21
+ ## PROJECT::SPECIFIC
data/LICENSE ADDED
@@ -0,0 +1,20 @@
1
+ Copyright (c) 2009 mrflip
2
+
3
+ Permission is hereby granted, free of charge, to any person obtaining
4
+ a copy of this software and associated documentation files (the
5
+ "Software"), to deal in the Software without restriction, including
6
+ without limitation the rights to use, copy, modify, merge, publish,
7
+ distribute, sublicense, and/or sell copies of the Software, and to
8
+ permit persons to whom the Software is furnished to do so, subject to
9
+ the following conditions:
10
+
11
+ The above copyright notice and this permission notice shall be
12
+ included in all copies or substantial portions of the Software.
13
+
14
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
15
+ EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
16
+ MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
17
+ NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
18
+ LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
19
+ OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
20
+ WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
@@ -0,0 +1,17 @@
1
+ = titi
2
+
3
+ Description goes here.
4
+
5
+ == Note on Patches/Pull Requests
6
+
7
+ * Fork the project.
8
+ * Make your feature addition or bug fix.
9
+ * Add tests for it. This is important so I don't break it in a
10
+ future version unintentionally.
11
+ * Commit, do not mess with rakefile, version, or history.
12
+ (if you want to have your own version, that is fine but bump version in a commit by itself I can ignore when I pull)
13
+ * Send me a pull request. Bonus points for topic branches.
14
+
15
+ == Copyright
16
+
17
+ Copyright (c) 2010 mrflip. See LICENSE for details.
@@ -0,0 +1,73 @@
1
+ require 'rubygems'
2
+ require 'rake'
3
+
4
+ begin
5
+ require 'jeweler'
6
+ Jeweler::Tasks.new do |gem|
7
+ gem.name = "titi"
8
+ gem.summary = %Q{Emit and consume Activity Streams from a wide variety of sources}
9
+ gem.description = %Q{Facade adapting apis to activity streams spec}
10
+ gem.email = "flip@infochimps.org"
11
+ gem.homepage = "http://github.com/mrflip/titi"
12
+ gem.authors = ["mrflip"]
13
+ gem.add_dependency "restclient", ">= 0.0.0"
14
+ gem.add_dependency "wukong", ">= 0.0.0"
15
+ gem.add_dependency "active_support", ">= 0.0.0"
16
+ gem.add_development_dependency "rspec", ">= 1.2.9"
17
+ gem.add_development_dependency "yard", ">= 0"
18
+ # gem is a Gem::Specification... see http://www.rubygems.org/read/chapter/20 for additional settings
19
+ end
20
+ Jeweler::GemcutterTasks.new
21
+ rescue LoadError
22
+ puts "Jeweler (or a dependency) not available. Install it with: gem install jeweler"
23
+ end
24
+
25
+ require 'spec/rake/spectask'
26
+ Spec::Rake::SpecTask.new(:spec) do |spec|
27
+ spec.libs << 'lib' << 'spec'
28
+ spec.spec_files = FileList['spec/**/*_spec.rb']
29
+ end
30
+
31
+ Spec::Rake::SpecTask.new(:rcov) do |spec|
32
+ spec.libs << 'lib' << 'spec'
33
+ spec.pattern = 'spec/**/*_spec.rb'
34
+ spec.rcov = true
35
+ end
36
+
37
+ task :spec => :check_dependencies
38
+
39
+ begin
40
+ require 'reek/adapters/rake_task'
41
+ Reek::RakeTask.new do |t|
42
+ t.fail_on_error = true
43
+ t.verbose = false
44
+ t.source_files = 'lib/**/*.rb'
45
+ end
46
+ rescue LoadError
47
+ task :reek do
48
+ abort "Reek is not available. In order to run reek, you must: sudo gem install reek"
49
+ end
50
+ end
51
+
52
+ begin
53
+ require 'roodi'
54
+ require 'roodi_task'
55
+ RoodiTask.new do |t|
56
+ t.verbose = false
57
+ end
58
+ rescue LoadError
59
+ task :roodi do
60
+ abort "Roodi is not available. In order to run roodi, you must: sudo gem install roodi"
61
+ end
62
+ end
63
+
64
+ task :default => :spec
65
+
66
+ begin
67
+ require 'yard'
68
+ YARD::Rake::YardocTask.new
69
+ rescue LoadError
70
+ task :yardoc do
71
+ abort "YARD is not available. In order to run yardoc, you must: sudo gem install yard"
72
+ end
73
+ end
data/VERSION ADDED
@@ -0,0 +1 @@
1
+ 0.0.1
@@ -0,0 +1,6 @@
1
+ module Titi
2
+ autoload :Matcher, 'titi/matcher'
3
+ autoload :ParseTree, 'titi/parse_tree'
4
+ autoload :Provider, 'titi/provider'
5
+ autoload :Adaptor, 'titi/adaptor'
6
+ end
@@ -0,0 +1,59 @@
1
+ module Titi
2
+ module Adaptor
3
+ # for each key in hsh, sets that attribute to the corresponding value.
4
+ #
5
+ # @example
6
+ # foo
7
+ # # => #<Struct Simian name="Mojo Jojo" status="villain" nemesis=nil species='Chimpanzee'>
8
+ # foo.attributes = { :nemesis => 'Powerpuff girls', :species => 'Pan Troglodytes' }
9
+ # foo
10
+ # # => #<Struct Simian name="Mojo Jojo" status="villain" nemesis='Powerpuff Girls' species='Pan Troglodytes'>
11
+ def attributes= hsh
12
+ hsh.each do |attr, val|
13
+ setter = "#{attr}="
14
+ self.send(setter, val) if respond_to?(setter)
15
+ end
16
+ end
17
+
18
+ # Adopt attributes from given hash, and programatically-set attributes from block
19
+ #
20
+ # @example
21
+ # ActivityStreams::Entry.adapt(
22
+ # :id => status.id,
23
+ # :title => status.text,
24
+ # :content => status.text,
25
+ # :verb => :post
26
+ # ) do |entry|
27
+ # status_time = Time.parse(status.created_at) rescue nil
28
+ # entry.published = status_time
29
+ # entry.author = ActivityStreams::Author.new(:name => status.user.name, :url => status.user.url)
30
+ # entry.object = ActivityStreams::Object.adapt do |activity_obj|
31
+ # activity_obj.id = status.id
32
+ # activity_obj.title = status.text
33
+ # activity_obj.published = status_time
34
+ # activity_obj.updated = status_time
35
+ # activity_obj.author = ActivityStreams::Author.new(:name => status.user.name, :url => status.user.url)
36
+ # end
37
+ # end
38
+ def adapt hsh={}, &block
39
+ self.attributes = hsh
40
+ yield self if block
41
+ end
42
+
43
+ # The standard hack to construct class methods on a class that #include's this model
44
+ module ClassMethods
45
+ # created an object and then adopts from the given hash and block
46
+ def adapt hsh={}, &block
47
+ obj = self.new
48
+ obj.adapt(hsh, &block)
49
+ obj
50
+ end
51
+ end
52
+
53
+ def self.included base
54
+ base.class_eval do
55
+ extend Titi::Adaptor::ClassMethods
56
+ end
57
+ end
58
+ end
59
+ end
@@ -0,0 +1,157 @@
1
+ module Titi
2
+ module Matcher
3
+ class Base
4
+ attr_accessor :selector
5
+ attr_accessor :matcher
6
+ def initialize selector, matcher=nil
7
+ self.selector = selector
8
+ self.matcher = matcher
9
+ end
10
+ def match doc
11
+ raise "Abstract class #{self.class}"
12
+ end
13
+ end
14
+
15
+ class MatchFirstElement < Base
16
+ # return the inner_html of the given element
17
+ #
18
+ # eg.
19
+ # m = MatchFirstElement.new('span#bio/a.homepage')
20
+ # m.match('<span id="bio"><a class="homepage" href="http://foo.bar">My Homepage</a></span>')
21
+ # # => 'My Homepage'
22
+ #
23
+ def match doc
24
+ el = doc.at(selector) or return nil
25
+ matcher ? matcher.match(el) : el.inner_html
26
+ end
27
+ end
28
+
29
+ class MatchArray < Base
30
+ # return the given element
31
+ #
32
+ # eg.
33
+ # m = MatchAttribute.new('span#bio/a.homepage', 'href')
34
+ # m.match('<span id="bio"><a class="homepage" href="http://foo.bar">My Homepage</a></span>')
35
+ # # => 'http://foo.bar'
36
+ #
37
+ def match doc
38
+ subdoc = (doc/selector) or return nil
39
+ if matcher
40
+ subdoc.map{|el| matcher.match(el)}
41
+ else
42
+ subdoc.map{|el| el.inner_html}
43
+ end
44
+ end
45
+ end
46
+
47
+ class MatchAttribute < Base
48
+ attr_accessor :attribute
49
+ def initialize selector, attribute, matcher=nil
50
+ super selector, matcher
51
+ self.attribute = attribute.to_s
52
+ end
53
+ # return the given attribute on the first matching path
54
+ #
55
+ # eg.
56
+ # m = MatchAttribute.new('span#bio/a.homepage', 'href')
57
+ # m.match('<span id="bio"><a class="homepage" href="http://foo.bar">My Homepage</a></span>')
58
+ # # => 'http://foo.bar'
59
+ #
60
+ def match doc
61
+ val = doc.path_attr(selector, attribute)
62
+ matcher ? matcher.match(val) : val
63
+ end
64
+ end
65
+
66
+ #
67
+ # map html elements -- or any HTMLParser tree -- to attributes in a hash.
68
+ # HTMLParser.new([ {
69
+ # :name => 'li/span.fn',
70
+ # :location => 'li/span.adr',
71
+ # :url => HTMLParser.attr('li/a.url[@href]', 'href'),
72
+ # :bio => 'li#bio/span.bio',
73
+ # }
74
+ # ])
75
+ #
76
+ class MatchHash
77
+ attr_accessor :match_hash
78
+ def initialize match_hash
79
+ # Kludge? maybe.
80
+ raise "MatchHash requires a hash of :attributes => matchers." unless match_hash.is_a?(Hash)
81
+ self.match_hash = match_hash
82
+ end
83
+ # Returns a hash mapping each attribute in match_hash
84
+ # to the result of its matcher on the current doc tree
85
+ def match doc
86
+ hsh = { }
87
+ match_hash.each do |attr, m|
88
+ val = m.match(doc)
89
+ case attr
90
+ when Array then hsh.merge!(Hash.zip(attr, val).reject{|k,v| v.nil? }) if val
91
+ else hsh[attr] = val end
92
+ end
93
+ self.class.scrub!(hsh)
94
+ end
95
+ # kill off keys with nil values
96
+ def self.scrub! hsh
97
+ hsh # .reject{|k,v| v.nil? }
98
+ end
99
+ end
100
+
101
+ class MatchProc < MatchFirstElement
102
+ attr_accessor :proc
103
+ attr_accessor :options
104
+ def initialize selector, proc, matcher=nil, options={}
105
+ super selector, matcher
106
+ self.options = options
107
+ self.proc = proc
108
+ end
109
+ def match doc
110
+ val = super doc
111
+ self.proc.call(val) if val
112
+ end
113
+ end
114
+
115
+ class MatchRegexp < Base
116
+ attr_accessor :re
117
+ attr_accessor :options
118
+ def initialize selector, re, matcher=nil, options={}
119
+ super selector, matcher
120
+ self.options = options
121
+ self.re = re
122
+ end
123
+ def match doc
124
+ # apply selector, if any
125
+ el = selector ? doc.contents_of(selector) : doc
126
+ m = re.match(el.to_s)
127
+ val = case
128
+ when m.nil? then nil
129
+ when self.options[:one] then m.captures.first
130
+ else m.captures
131
+ end
132
+ # pass to matcher, if any
133
+ matcher ? matcher.match(val) : val
134
+ end
135
+ end
136
+
137
+ class MatchRegexpRepeatedly < Base
138
+ attr_accessor :re
139
+ def initialize selector, re, matcher=nil
140
+ super selector, matcher
141
+ self.re = re
142
+ end
143
+ def match doc
144
+ # apply selector, if any
145
+ el = selector ? doc.contents_of(selector) : doc
146
+ return unless el
147
+ # get all matches
148
+ val = el.to_s.scan(re)
149
+ # if there's only one capture group, flatten the array
150
+ val = val.flatten if val.first && val.first.length == 1
151
+ # pass to matcher, if any
152
+ matcher ? matcher.match(val) : val
153
+ end
154
+ end
155
+
156
+ end
157
+ end
@@ -0,0 +1,34 @@
1
+ module Titi
2
+ module ParseTree
3
+
4
+ #
5
+ # construct the downstream part of a hash matcher
6
+ #
7
+ def self.build_match_hash spec_hash
8
+ hsh = { }
9
+ spec_hash.each do |attr, spec|
10
+ hsh[attr] = build_parse_tree(spec)
11
+ end
12
+ hsh
13
+ end
14
+
15
+ #
16
+ # recursively build a tree of matchers
17
+ #
18
+ def self.build_parse_tree spec
19
+ case spec
20
+ when nil then nil
21
+ when Matcher then spec
22
+ when Hash then MatchHash.new(build_match_hash(spec))
23
+ when Array then
24
+ return nil if spec.empty?
25
+ raise "Array spec must be a single selector or a selector and another match specification" unless (spec.length <= 2)
26
+ MatchArray.new(spec[0].to_s, build_parse_tree(spec[1]))
27
+ when String then MatchFirstElement.new(spec)
28
+ when Proc then MatchProc.new(nil, spec)
29
+ when Regexp then MatchRegexp.new(nil, spec, nil, :one => true)
30
+ else raise "Don't know how to parse #{spec.inspect}"
31
+ end
32
+ end
33
+ end
34
+ end
@@ -0,0 +1,39 @@
1
+ module Titi
2
+ module Provider
3
+ autoload :ActivityStreams, 'titi/provider/activity_streams'
4
+ #
5
+ autoload :Twitter , 'titi/provider/twitter'
6
+ # autoload :Gowalla , 'titi/provider/gowalla'
7
+ # autoload :Foursquare, 'titi/provider/foursquare'
8
+ # autoload :Tripit , 'titi/provider/tripit'
9
+ # autoload :Facebook , 'titi/provider/facebook'
10
+ # autoload :Myspace , 'titi/provider/myspace'
11
+ # autoload :LinkedIn , 'titi/provider/linked_in'
12
+ # autoload :Digg , 'titi/provider/digg'
13
+ # autoload :Last_fm , 'titi/provider/last_fm'
14
+ # autoload :Flickr , 'titi/provider/flickr'
15
+ # autoload :WordPress , 'titi/provider/word_press'
16
+ end
17
+ end
18
+
19
+ # # from Rob Dolin, a list:
20
+ # {
21
+ # :text_status => %w[ Twitter StatusNet Plurk AIM WlMessenger ],
22
+ # :location => %w[ Gowalla Foursquare Tripit Yelp Qype UrbanSpoon Brightkite Whirl ],
23
+ # :social => %w[ Facebook MySpace LinkedIn Xing Plaxo ],
24
+ # :music => %w[ LastFm Pandora Zune iLike ],
25
+ # :bookmarking => %w[ Digg StumbleUpon Delicious Facebook ],
26
+ # :video => %w[ YouTube DailyMotion Hulu BuddyTv SkyDrive ],
27
+ # :gaming => %w[ Zynga Playdom MsgrGames ],
28
+ # :photos => %w[ Flickr Photobucket MetroFlog Fotolog Picasa WlSkyDrive ],
29
+ # :blogging => %w[ Blogger WordPress TypePad MySpace MoveableType WlSpaces ],
30
+ # }
31
+ #
32
+ # # See also: http://feedproxy.cliqset.com/feed/selection
33
+ # {
34
+ # Social => %w[ AvatarsUnited Bebo BrightKite Friendster FriendFeed GamerDNA Gather hi5 Hyves Identica Jaiku Multiply Netlog Plurk Raptr Skyrock Twitter ],
35
+ # Blogging => %w[ Ameba Baidu Blogger CustomFeed Douban LiveJournal Posterous Tumblr WordPress Xanga ],
36
+ # Bookmarking => %w[ Delicious Digg Diigo GoogleReader Hatena Meneame Mixx Newsvine Propeller Reddit StumbleUpon Twine ],
37
+ # Media => %w[ 12seconds BlipFm BlipTv Buzznet Cinchcast Dailymotion DeviantArt Flickr Fotolog FunnyOrDie Hulu iLike Joost LastFm MetaCafe MobyPicture Pandora Photobucket Photocase Picasa Qik Revver SlideShare Smotri SmugMug VisualizeUs Vimeo YouTube Zooomr ],
38
+ # Reviews => %w[ Blippr Corkd Flixster Goodreads LibraryThing Qype Readernaut Yelp ],
39
+ # }
@@ -0,0 +1,87 @@
1
+ module Titi
2
+
3
+ module Provider
4
+ #
5
+ module ActivityStreams
6
+ # Some methods common to all ActivityStreams classes
7
+ module Common
8
+ def to_hash *args
9
+ hsh = super(*args)
10
+ hsh.each do |attr, val|
11
+ hsh[attr] = val.to_hash if val.respond_to?(:to_hash)
12
+ end
13
+ hsh
14
+ end
15
+
16
+ def to_xml *args
17
+ hsh = self.to_hash
18
+ hsh.to_xml :root => self.class.to_s.underscore.gsub(%r{.*/},'')
19
+ end
20
+ end
21
+
22
+ # An ActivityStream entry
23
+ # http://activitystrea.ms/spec/1.0/atom-activity-01.html#activityentries
24
+ Entry = Struct.new(
25
+ :id, # ???
26
+ :title, # title
27
+ :content, # content
28
+ :link, # link {"href"=>"http://x.myspacecdn.com/modules/common/static/img/photo.gif", "rel"=>"icon", "type"=>"image/gif"}
29
+ :published, # date published
30
+ :updated, # date updated
31
+ #
32
+ :category, # type of entry
33
+ :verb, # action it implies
34
+ :sync, # sync
35
+ :rank, # rank
36
+ #
37
+ :actor, # actor
38
+ :author, # author {"name"=>["misterflip"], "uri"=>["http://profile.myspace.com/index.cfm?fuseaction=user.viewprofile&friendid=24601"]}
39
+ :mood, #
40
+ :source,
41
+ :object, # object
42
+ :target # target
43
+ )
44
+ Entry.class_eval do
45
+ include Titi::Adaptor
46
+ include Titi::Provider::ActivityStreams::Common
47
+ def published= date_time
48
+ self[:published] = DateTime.parse(date_time) rescue nil
49
+ end
50
+ def updated= date_time
51
+ self[:updated] = DateTime.parse(date_time) rescue nil
52
+ end
53
+ end
54
+
55
+ # ActivityStream author
56
+ # http://activitystrea.ms/spec/1.0/atom-activity-01.html#activityactor
57
+ Actor = Struct.new(
58
+ :name,
59
+ :uri
60
+ )
61
+ Actor.class_eval do
62
+ include Titi::Adaptor
63
+ include Titi::Provider::ActivityStreams::Common
64
+ end
65
+ Author = Actor
66
+
67
+ # ActivityStream object
68
+ # http://activitystrea.ms/spec/1.0/atom-activity-01.html#activityobjectelement
69
+ ActivityObject = Struct.new(
70
+ :id,
71
+ :title,
72
+ :content,
73
+ :link,
74
+ :published,
75
+ :updated,
76
+ #
77
+ :author,
78
+ :object_type,
79
+ :vevent
80
+ )
81
+ ActivityObject.class_eval do
82
+ include Titi::Adaptor
83
+ include Titi::Provider::ActivityStreams::Common
84
+ end
85
+ end
86
+ end
87
+ end
@@ -0,0 +1,7 @@
1
+ module Titi::Provider
2
+ module Tripit
3
+
4
+ VERBS = %w[ install connect planning returning about_to_leave made_changes is_planning ].map(&:to_sym)
5
+
6
+ end
7
+ end
@@ -0,0 +1,54 @@
1
+ module Titi::Provider
2
+ module Twitter
3
+ autoload :User, 'titi/provider/twitter/models'
4
+ autoload :Status, 'titi/provider/twitter/models'
5
+
6
+ Status.class_eval do
7
+ # @example
8
+ # <entry>
9
+ # <title>ciberch: thanks to /Jerry @robdolin , @daveman692, @chrismessina, @apparentlymart and @jsmarr for a very produtive activitystrea.ms session</title>
10
+ # <content type="html">ciberch: thanks to /Jerry @robdolin , @daveman692, @chrismessina, @apparentlymart and @jsmarr for a very produtive activitystrea.ms session</content>
11
+ # <id>tag:twitter.com,2007:http://twitter.com/ciberch/statuses/2396395271</id>
12
+ # <published>2009-06-30T00:59:54+00:00</published>
13
+ # <updated>2009-06-30T00:59:54+00:00</updated>
14
+ # <link type="text/html" rel="alternate" href="http://twitter.com/ciberch/statuses/2396395271"/>
15
+ # <link type="image/pjpeg" rel="image" href="http://s3.amazonaws.com/twitter_production/profile_images/81481539/180653_normal.jpg"/>
16
+ # <author>
17
+ # <name>Monica Keller</name>
18
+ # <uri>http://www.myspace.com/ciberch</uri>
19
+ # </author>
20
+ # <activity:verb>http://activitystrea.ms/schema/1.0/post</activity:verb>
21
+ # <activity:object>
22
+ # <id>tag:twitter.com,2007:http://twitter.com/ciberch/statuses/2396395271/object</id>
23
+ # <title>thanks to /Jerry @robdolin , @daveman692, @chrismessina, @apparentlymart and @jsmarr for a very produtive activitystrea.ms session</title>
24
+ # <link type="text/html" rel="alternate" href="http://twitter.com/ciberch/statuses/2396395271"/>
25
+ # <published>2009-06-30T00:59:54+00:00</published>
26
+ # <updated>2009-06-30T00:59:54+00:00</updated>
27
+ # <author>
28
+ # <name>Monica Keller</name>
29
+ # <uri>http://www.myspace.com/ciberch</uri>
30
+ # </author>
31
+ # </activity:object>
32
+ # </entry>
33
+ #
34
+ def to_activity_stream_entry
35
+ ActivityStreams::Entry.adapt(
36
+ :id => %Q{tag:twitter.com,2007:http://twitter.com/#{user.screen_name}/statuses/#{id}},
37
+ :title => text,
38
+ :content => text,
39
+ :published => created_at,
40
+ :verb => :post
41
+ ) do |entry|
42
+ entry.author = ActivityStreams::Author.new(user.name, user.url)
43
+ entry.object = ActivityStreams::ActivityObject.adapt do |activity_obj|
44
+ activity_obj.id = id
45
+ activity_obj.title = text
46
+ activity_obj.published = created_at
47
+ activity_obj.updated = created_at
48
+ activity_obj.author = entry.author
49
+ end
50
+ end
51
+ end
52
+ end
53
+ end
54
+ end
@@ -0,0 +1,11 @@
1
+ require 'restclient'
2
+ require 'json'
3
+ require 'titi'
4
+ require 'wukong'
5
+ require 'active_support'
6
+
7
+ # Dir['./**/*.rb'].each{|req| load req }^
8
+ # $raw_json_str = RestClient.get 'http://twitter.com/users/show/mrflip.json'
9
+ $raw_json_str = %q{{"created_at":"Mon Mar 19 21:08:24 +0000 2007","profile_sidebar_fill_color":"ffffff","description":"Founder, http://infochimps.org - Building tools to Organize, Explore and Comprehend massive data sources","verified":false,"time_zone":"Central Time (US & Canada)","status":{"in_reply_to_status_id":null,"created_at":"Sat Apr 17 05:47:28 +0000 2010","favorited":false,"in_reply_to_user_id":null,"source":"<a href=\"http://www.atebits.com/\" rel=\"nofollow\">Tweetie</a>","id":12327261220,"in_reply_to_screen_name":null,"truncated":false,"text":"Hacking Activity Streams yay"},"following":null,"profile_sidebar_border_color":"f0edd8","url":"http://infochimps.org","profile_image_url":"http://a3.twimg.com/profile_images/377919497/FlipCircle-2009-900-trans_normal.png","notifications":null,"followers_count":1061,"profile_background_color":"BCC0C8","location":"iPhone: 30.316122,-97.733817","screen_name":"mrflip","profile_background_image_url":"http://a3.twimg.com/profile_background_images/2348065/2005Mar-AustinTypeTour-075_-_Rappers_Delight_Raindrop.jpg","friends_count":754,"statuses_count":1744,"profile_text_color":"000000","protected":false,"profile_background_tile":false,"favourites_count":98,"name":"Philip Flip Kromer","contributors_enabled":false,"profile_link_color":"0000ff","id":1554031,"lang":"en","geo_enabled":true,"utc_offset":-21600}}
10
+ $raw_user = JSON.load($raw_json_str.to_s)
11
+ $user = Titi::Provider::Twitter::User.from_hash $raw_user
@@ -0,0 +1,102 @@
1
+ module Titi::Provider
2
+ module Twitter
3
+
4
+ #
5
+ #
6
+ # {
7
+ # "id" => 1554031,
8
+ # "screen_name" => "mrflip",
9
+ # "protected" => false,
10
+ # "followers_count" => 1060,
11
+ # "friends_count" => 754,
12
+ # "statuses_count" => 1744,
13
+ # "favourites_count" => 98,
14
+ # "created_at" => "Mon Mar 19 21:08:24 +0000 2007",
15
+ # #
16
+ # "name" => "Philip Flip Kromer",
17
+ # "url" => "http://infochimps.org",
18
+ # "location" => "iPhone: 30.316122,-97.733817",
19
+ # "description" => "Founder, http://infochimps.org - Building tools to Organize, Explore and Comprehend massive data sources",
20
+ # "time_zone" => "Central Time (US & Canada)",
21
+ # "utc_offset" => -21600,
22
+ # "lang" => "en",
23
+ # #
24
+ # "profile_background_color" => "BCC0C8",
25
+ # "profile_text_color" => "000000",
26
+ # "profile_link_color" => "0000ff",
27
+ # "profile_sidebar_border_color" => "f0edd8",
28
+ # "profile_sidebar_fill_color" => "ffffff",
29
+ # "profile_background_tile" => false,
30
+ # "profile_background_image_url" => "http://a3.twimg.com/profile_background_images/2348065/2005Mar-AustinTypeTour-075_-_Rappers_Delight_Raindrop.jpg",
31
+ # "profile_image_url" => "http://a3.twimg.com/profile_images/377919497/FlipCircle-2009-900-trans_normal.png",
32
+ # #
33
+ # "geo_enabled" => true,
34
+ # "contributors_enabled" => false,
35
+ # "following" => nil,
36
+ # "notifications" => nil,
37
+ # "verified" => false,
38
+ # #
39
+ # "status" => #<Twitter::Status>
40
+ # }
41
+ #
42
+ User = Struct.new(
43
+ :id,
44
+ :screen_name, :protected, :followers_count, :friends_count, :statuses_count, :favourites_count, :created_at,
45
+ :name, :url, :location, :description, :time_zone, :utc_offset,
46
+ :profile_background_color, :profile_text_color, :profile_link_color,
47
+ :profile_sidebar_border_color, :profile_sidebar_fill_color,
48
+ :profile_background_tile, :profile_background_image_url, :profile_image_url
49
+ )
50
+ User.class_eval do
51
+ include Titi::Provider
52
+ include Titi::Adaptor
53
+ # def status= new_status
54
+ # new_status = Twitter::Status.from_hash(new_status) unless new_status.is_a?(Twitter::Status)
55
+ # self[:status] = new_status
56
+ # end
57
+
58
+ def created_at= date_time
59
+ p [:created_at, date_time]
60
+ unless date_time.is_a?(DateTime)
61
+ dt = DateTime.parse(date_time) rescue nil
62
+ p dt
63
+ end
64
+ self[:created_at] = dt
65
+ end
66
+ end
67
+
68
+ # {
69
+ # "id" => 12327261220,
70
+ # "created_at" => "Sat Apr 17 05:47:28 +0000 2010",
71
+ # "user" => #<Twitter::User>
72
+ # "text" => "Hacking Activity Streams yay",
73
+ # "favorited" => false,
74
+ # "truncated" => false,
75
+ # "in_reply_to_user_id" => nil,
76
+ # "in_reply_to_status_id" => nil
77
+ # "in_reply_to_screen_name" => nil,
78
+ # "source" => "<a href='http://www.atebits.com/' rel='nofollow'>Tweetie</a>",
79
+ # }
80
+ Status = Struct.new(
81
+ :id,
82
+ :created_at,
83
+ :user,
84
+ :favorited,
85
+ :truncated,
86
+ :in_reply_to_user_id,
87
+ :in_reply_to_status_id,
88
+ :text,
89
+ :source,
90
+ :in_reply_to_screen_name
91
+ )
92
+ Status.class_eval do
93
+ include Titi::Provider
94
+ include Titi::Adaptor
95
+
96
+ def user= new_user
97
+ new_user = Twitter::User.from_hash(new_user.to_hash) unless new_user.is_a?(Twitter::User)
98
+ self[:user] = new_user
99
+ end
100
+ end
101
+ end
102
+ end
@@ -0,0 +1,12 @@
1
+ jeweler \
2
+ --summary 'Emit and consume Activity Streams from a wide variety of sources' \
3
+ --description 'Facade adapting apis to activity streams spec' \
4
+ --rspec --reek --roodi --yard \
5
+ --gemcutter --create-repo --user-name=mrflip --user-email=flip@infochimps.org --github-username=mrflip \
6
+ titi
7
+
8
+
9
+ bloom filter:
10
+ n ln(p) n log10(1/p)
11
+ - -------- = --------------- = 0.9 n log10(1/p) ...
12
+ (ln 2)^2 (ln 2)^2 ln(10)
@@ -0,0 +1 @@
1
+ --color
@@ -0,0 +1,9 @@
1
+ $LOAD_PATH.unshift(File.dirname(__FILE__))
2
+ $LOAD_PATH.unshift(File.join(File.dirname(__FILE__), '..', 'lib'))
3
+ require 'titi'
4
+ require 'spec'
5
+ require 'spec/autorun'
6
+
7
+ Spec::Runner.configure do |config|
8
+
9
+ end
@@ -0,0 +1,7 @@
1
+ require File.expand_path(File.dirname(__FILE__) + '/spec_helper')
2
+
3
+ describe "Titi" do
4
+ it "fails" do
5
+ fail "hey buddy, you should probably rename this file and start specing for real"
6
+ end
7
+ end
@@ -0,0 +1,78 @@
1
+ # Generated by jeweler
2
+ # DO NOT EDIT THIS FILE DIRECTLY
3
+ # Instead, edit Jeweler::Tasks in Rakefile, and run the gemspec command
4
+ # -*- encoding: utf-8 -*-
5
+
6
+ Gem::Specification.new do |s|
7
+ s.name = %q{titi}
8
+ s.version = "0.0.1"
9
+
10
+ s.required_rubygems_version = Gem::Requirement.new(">= 0") if s.respond_to? :required_rubygems_version=
11
+ s.authors = ["mrflip"]
12
+ s.date = %q{2010-04-17}
13
+ s.description = %q{Facade adapting apis to activity streams spec}
14
+ s.email = %q{flip@infochimps.org}
15
+ s.extra_rdoc_files = [
16
+ "LICENSE",
17
+ "README.textile"
18
+ ]
19
+ s.files = [
20
+ ".document",
21
+ ".gitignore",
22
+ "LICENSE",
23
+ "README.textile",
24
+ "Rakefile",
25
+ "VERSION",
26
+ "lib/titi.rb",
27
+ "lib/titi/adaptor.rb",
28
+ "lib/titi/matcher.rb",
29
+ "lib/titi/parse_tree.rb",
30
+ "lib/titi/provider.rb",
31
+ "lib/titi/provider/README.textile",
32
+ "lib/titi/provider/activity_streams.rb",
33
+ "lib/titi/provider/tripit.rb",
34
+ "lib/titi/provider/twitter.rb",
35
+ "lib/titi/provider/twitter/example.rb",
36
+ "lib/titi/provider/twitter/models.rb",
37
+ "notes/jeweler-gen.sh",
38
+ "spec/spec.opts",
39
+ "spec/spec_helper.rb",
40
+ "spec/titi_spec.rb",
41
+ "titi.gemspec"
42
+ ]
43
+ s.homepage = %q{http://github.com/mrflip/titi}
44
+ s.rdoc_options = ["--charset=UTF-8"]
45
+ s.require_paths = ["lib"]
46
+ s.rubygems_version = %q{1.3.6}
47
+ s.summary = %q{Emit and consume Activity Streams from a wide variety of sources}
48
+ s.test_files = [
49
+ "spec/spec_helper.rb",
50
+ "spec/titi_spec.rb"
51
+ ]
52
+
53
+ if s.respond_to? :specification_version then
54
+ current_version = Gem::Specification::CURRENT_SPECIFICATION_VERSION
55
+ s.specification_version = 3
56
+
57
+ if Gem::Version.new(Gem::RubyGemsVersion) >= Gem::Version.new('1.2.0') then
58
+ s.add_runtime_dependency(%q<restclient>, [">= 0.0.0"])
59
+ s.add_runtime_dependency(%q<wukong>, [">= 0.0.0"])
60
+ s.add_runtime_dependency(%q<active_support>, [">= 0.0.0"])
61
+ s.add_development_dependency(%q<rspec>, [">= 1.2.9"])
62
+ s.add_development_dependency(%q<yard>, [">= 0"])
63
+ else
64
+ s.add_dependency(%q<restclient>, [">= 0.0.0"])
65
+ s.add_dependency(%q<wukong>, [">= 0.0.0"])
66
+ s.add_dependency(%q<active_support>, [">= 0.0.0"])
67
+ s.add_dependency(%q<rspec>, [">= 1.2.9"])
68
+ s.add_dependency(%q<yard>, [">= 0"])
69
+ end
70
+ else
71
+ s.add_dependency(%q<restclient>, [">= 0.0.0"])
72
+ s.add_dependency(%q<wukong>, [">= 0.0.0"])
73
+ s.add_dependency(%q<active_support>, [">= 0.0.0"])
74
+ s.add_dependency(%q<rspec>, [">= 1.2.9"])
75
+ s.add_dependency(%q<yard>, [">= 0"])
76
+ end
77
+ end
78
+
metadata ADDED
@@ -0,0 +1,152 @@
1
+ --- !ruby/object:Gem::Specification
2
+ name: titi
3
+ version: !ruby/object:Gem::Version
4
+ prerelease: false
5
+ segments:
6
+ - 0
7
+ - 0
8
+ - 1
9
+ version: 0.0.1
10
+ platform: ruby
11
+ authors:
12
+ - mrflip
13
+ autorequire:
14
+ bindir: bin
15
+ cert_chain: []
16
+
17
+ date: 2010-04-17 00:00:00 -07:00
18
+ default_executable:
19
+ dependencies:
20
+ - !ruby/object:Gem::Dependency
21
+ name: restclient
22
+ prerelease: false
23
+ requirement: &id001 !ruby/object:Gem::Requirement
24
+ requirements:
25
+ - - ">="
26
+ - !ruby/object:Gem::Version
27
+ segments:
28
+ - 0
29
+ - 0
30
+ - 0
31
+ version: 0.0.0
32
+ type: :runtime
33
+ version_requirements: *id001
34
+ - !ruby/object:Gem::Dependency
35
+ name: wukong
36
+ prerelease: false
37
+ requirement: &id002 !ruby/object:Gem::Requirement
38
+ requirements:
39
+ - - ">="
40
+ - !ruby/object:Gem::Version
41
+ segments:
42
+ - 0
43
+ - 0
44
+ - 0
45
+ version: 0.0.0
46
+ type: :runtime
47
+ version_requirements: *id002
48
+ - !ruby/object:Gem::Dependency
49
+ name: active_support
50
+ prerelease: false
51
+ requirement: &id003 !ruby/object:Gem::Requirement
52
+ requirements:
53
+ - - ">="
54
+ - !ruby/object:Gem::Version
55
+ segments:
56
+ - 0
57
+ - 0
58
+ - 0
59
+ version: 0.0.0
60
+ type: :runtime
61
+ version_requirements: *id003
62
+ - !ruby/object:Gem::Dependency
63
+ name: rspec
64
+ prerelease: false
65
+ requirement: &id004 !ruby/object:Gem::Requirement
66
+ requirements:
67
+ - - ">="
68
+ - !ruby/object:Gem::Version
69
+ segments:
70
+ - 1
71
+ - 2
72
+ - 9
73
+ version: 1.2.9
74
+ type: :development
75
+ version_requirements: *id004
76
+ - !ruby/object:Gem::Dependency
77
+ name: yard
78
+ prerelease: false
79
+ requirement: &id005 !ruby/object:Gem::Requirement
80
+ requirements:
81
+ - - ">="
82
+ - !ruby/object:Gem::Version
83
+ segments:
84
+ - 0
85
+ version: "0"
86
+ type: :development
87
+ version_requirements: *id005
88
+ description: Facade adapting apis to activity streams spec
89
+ email: flip@infochimps.org
90
+ executables: []
91
+
92
+ extensions: []
93
+
94
+ extra_rdoc_files:
95
+ - LICENSE
96
+ - README.textile
97
+ files:
98
+ - .document
99
+ - .gitignore
100
+ - LICENSE
101
+ - README.textile
102
+ - Rakefile
103
+ - VERSION
104
+ - lib/titi.rb
105
+ - lib/titi/adaptor.rb
106
+ - lib/titi/matcher.rb
107
+ - lib/titi/parse_tree.rb
108
+ - lib/titi/provider.rb
109
+ - lib/titi/provider/README.textile
110
+ - lib/titi/provider/activity_streams.rb
111
+ - lib/titi/provider/tripit.rb
112
+ - lib/titi/provider/twitter.rb
113
+ - lib/titi/provider/twitter/example.rb
114
+ - lib/titi/provider/twitter/models.rb
115
+ - notes/jeweler-gen.sh
116
+ - spec/spec.opts
117
+ - spec/spec_helper.rb
118
+ - spec/titi_spec.rb
119
+ - titi.gemspec
120
+ has_rdoc: true
121
+ homepage: http://github.com/mrflip/titi
122
+ licenses: []
123
+
124
+ post_install_message:
125
+ rdoc_options:
126
+ - --charset=UTF-8
127
+ require_paths:
128
+ - lib
129
+ required_ruby_version: !ruby/object:Gem::Requirement
130
+ requirements:
131
+ - - ">="
132
+ - !ruby/object:Gem::Version
133
+ segments:
134
+ - 0
135
+ version: "0"
136
+ required_rubygems_version: !ruby/object:Gem::Requirement
137
+ requirements:
138
+ - - ">="
139
+ - !ruby/object:Gem::Version
140
+ segments:
141
+ - 0
142
+ version: "0"
143
+ requirements: []
144
+
145
+ rubyforge_project:
146
+ rubygems_version: 1.3.6
147
+ signing_key:
148
+ specification_version: 3
149
+ summary: Emit and consume Activity Streams from a wide variety of sources
150
+ test_files:
151
+ - spec/spec_helper.rb
152
+ - spec/titi_spec.rb