titi 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.
@@ -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