deliruby 0.1.0

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 lfborjas
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,50 @@
1
+ = deliruby
2
+
3
+ Deliruby is a simple ruby wrapper for the {delicious rss feeds}[http://www.delicious.com/help/json] (not the {API}[http://www.delicious.com/help/api]), so you can use that info for your projects without dealing with xml or json.
4
+
5
+ It uses {httparty}[http://github.com/jnunemaker/httparty] and does caching with the {httparty_icebox}[http://gist.github.com/209605] for extra code readability!
6
+
7
+ ==Installation
8
+ Easy as pi, just run
9
+ sudo gem install deliruby
10
+
11
+ ==Usage
12
+
13
+ The module Deliruby has two classes defined: +Bookmarks+ and +PublicInfo+
14
+
15
+ ===Bookmarks
16
+ Every method returns an array of instances of the class DeliciousBookmark, which has a +to_hash+ method for your
17
+ convenience. If any of the attributes wasn't found in the feed, it's set to a sensible default (+""+ for strings, +[]+ for arrays and +nil+ for the published date).
18
+
19
+ require 'deliruby'
20
+ #all methods return the same kind of data
21
+ Deliruby::Bookmarks.popular.each do |bookmark|
22
+ puts "#{bookmark.creator} marked #{bookmark.url} with the tags #{bookmark.url} on #{bookmark.date.to_s}"
23
+ end
24
+
25
+ ===PublicInfo
26
+
27
+ Every method returns either an array, an array of hashes or a hash:
28
+
29
+ [alerts] An array for the system alerts for delicious
30
+ [userinfo] A hash of the public user info (bookmark count, network members and network fans)
31
+ [tags] An array of hashes for every tag associated with the number of times it has been used by a user
32
+ [network] An array of hashes with the username and profile of every user in the specified user network
33
+ [network_fans] An array of hashes with the username and profile of every *fan* in the specified user network
34
+ [urlinfo] The summary information for a url: a hash with the md5 hash, link, title, times bookmarked and top tags applied
35
+
36
+ It tries to return sensible data: empty arrays or hashes if nothing is returned. In some cases, though, it might raise an exception of delicious responds with a 4** or 5** HTTP status code.
37
+
38
+ == Note on Patches/Pull Requests
39
+
40
+ * Fork the project.
41
+ * Make your feature addition or bug fix.
42
+ * Add tests for it. This is important so I don't break it in a
43
+ future version unintentionally.
44
+ * Commit, do not mess with rakefile, version, or history.
45
+ (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)
46
+ * Send me a pull request. Bonus points for topic branches.
47
+
48
+ == Copyright
49
+
50
+ Copyright (c) 2010 lfborjas. See LICENSE for details.
@@ -0,0 +1,54 @@
1
+ require 'rubygems'
2
+ require 'rake'
3
+
4
+ begin
5
+ require 'jeweler'
6
+ Jeweler::Tasks.new do |gem|
7
+ gem.name = "deliruby"
8
+ gem.summary = %Q{Ruby wrapper for the delicious.com public rss feeds}
9
+ gem.description = %Q{Ruby wrapper for the delicious.com public rss feeds}
10
+ gem.email = "me@lfborjas.com"
11
+ gem.homepage = "http://github.com/lfborjas/deliruby"
12
+ gem.authors = ["lfborjas"]
13
+ gem.add_development_dependency "thoughtbot-shoulda", ">= 0"
14
+ gem.add_dependency "httparty", "0.6.1"
15
+ # gem is a Gem::Specification... see http://www.rubygems.org/read/chapter/20 for additional settings
16
+ end
17
+ Jeweler::GemcutterTasks.new
18
+ rescue LoadError
19
+ puts "Jeweler (or a dependency) not available. Install it with: gem install jeweler"
20
+ end
21
+
22
+ require 'rake/testtask'
23
+ Rake::TestTask.new(:test) do |test|
24
+ test.libs << 'lib' << 'test'
25
+ test.pattern = 'test/**/test_*.rb'
26
+ test.verbose = true
27
+ end
28
+
29
+ begin
30
+ require 'rcov/rcovtask'
31
+ Rcov::RcovTask.new do |test|
32
+ test.libs << 'test'
33
+ test.pattern = 'test/**/test_*.rb'
34
+ test.verbose = true
35
+ end
36
+ rescue LoadError
37
+ task :rcov do
38
+ abort "RCov is not available. In order to run rcov, you must: sudo gem install spicycode-rcov"
39
+ end
40
+ end
41
+
42
+ task :test => :check_dependencies
43
+
44
+ task :default => :test
45
+
46
+ require 'rake/rdoctask'
47
+ Rake::RDocTask.new do |rdoc|
48
+ version = File.exist?('VERSION') ? File.read('VERSION') : ""
49
+
50
+ rdoc.rdoc_dir = 'rdoc'
51
+ rdoc.title = "deliruby #{version}"
52
+ rdoc.rdoc_files.include('README*')
53
+ rdoc.rdoc_files.include('lib/**/*.rb')
54
+ end
data/VERSION ADDED
@@ -0,0 +1 @@
1
+ 0.1.0
@@ -0,0 +1,220 @@
1
+ require 'httparty'
2
+ require 'httparty_icebox'
3
+ require 'date'
4
+ require 'digest/md5'
5
+ module Deliruby
6
+ #Abstract representation of a delicious bookmark item
7
+ class DeliciousBookmark
8
+ #The bookmarked url
9
+ attr_accessor :url
10
+ #The tags applied to it in this instance
11
+ attr_accessor :tags
12
+ #The title of this bookmark
13
+ attr_accessor :title
14
+ #The date when the bookmark was made
15
+ attr_accessor :published_on
16
+ #Who bookmarked it
17
+ attr_accessor :creator
18
+
19
+ def initialize(url, title="", published_on=nil, creator="", tags=[])
20
+ @url = url
21
+ @tags = tags || []
22
+ @title = title || ""
23
+ @published_on = published_on ? DateTime.strptime(published_on, "%a, %d %b %Y %H:%M:%S %Z") : nil
24
+ @creator = creator || ""
25
+ end
26
+
27
+ #convert this instance into a hash, based on
28
+ #{this snippet}[http://pullmonkey.com/2008/01/06/convert-a-ruby-hash-into-a-class-object/comment-page-1/]
29
+ def to_hash
30
+ hsh = {}
31
+ self.instance_variables.each do |var|
32
+ hsh[var.gsub("@","")] = self.instance_variable_get(var)
33
+ end
34
+ end
35
+ end
36
+ #Provides instance methods to access the public feeds of delicious.com, returning an array of Bookmark instances
37
+ class Bookmarks
38
+ include HTTParty
39
+ format :xml
40
+ base_uri "http://feeds.delicious.com/v2/xml"
41
+ include HTTParty::Icebox
42
+ #cache :store => 'memory', :timeout => 300
43
+ cache :store => 'file', :timeout => 300, :location => '/tmp/'
44
+
45
+ #Base method for all the calls to the delicious api
46
+ #Params:
47
+ #+url+:: the sub-url to access
48
+ def self.get_feed(url="/")
49
+ res = get(url)
50
+ raise "Communication error with delicious server: #{res.response['status']}" if res.response['status'] =~ /^[45]/
51
+ bookmarks = []
52
+ return [] unless res['rss']['channel'].has_key?('item')
53
+ res['rss']['channel']['item'].each do |item|
54
+ bookmarks.push DeliciousBookmark.new(item['link'], item['title'], item['pubDate'], item['dc:creator'],
55
+ item['category']) rescue next
56
+ end
57
+ return bookmarks
58
+ end
59
+
60
+ #Get the recent bookmarks on delicious
61
+ def self.recent
62
+ return self.get_feed('/recent')
63
+ end
64
+
65
+ #Get the recent bookmarks tagged with a combination of tags
66
+ #Params:
67
+ #+tags+:: an Array of tags; notice that the search is for bookmarks that include them all, not just a subset
68
+ def self.by_tags(tags=[])
69
+ return self.get_feed("/tag/#{tags.join('+')}")
70
+ end
71
+
72
+ #Get the most recent popular bookmarks (optionally) by tag
73
+ #Params:
74
+ #+tag+:: the tag to get popular bookmarks for; if not provided, just gets the globally popular bookmarks
75
+ def self.popular(tag="")
76
+ return self.get_feed("/popular#{"/"+tag if tag}")
77
+ end
78
+
79
+ #Get the recent bookmarks of a user, by tags
80
+ #Params:
81
+ #+user+:: the delicious username of the queried user
82
+ #+tags+:: an Array of tags, if not provided, just gets the recent bookmarks of the user
83
+ def self.for_user(user, tags=[])
84
+ return self.get_feed("/#{user}/#{tags.join('+')}")
85
+ end
86
+
87
+ #Get the recent bookmarks of a user's subscriptions
88
+ #Params:
89
+ #+user+:: the delicious username of the user
90
+ def self.subscripted(user)
91
+ return self.get_feed("/subscriptions/#{user}")
92
+ end
93
+
94
+ #Get the recent bookmarks of a user's network, by tags
95
+ #Params:
96
+ #+user+:: the delicious username
97
+ #+tags+:: an optional array of tags to filter by
98
+ def self.network(user, tags=[])
99
+ return self.get_feed("/network/#{user}/#{tags.join('+')}")
100
+ end
101
+
102
+ #Get the recent bookmarks for a specific url
103
+ #Params:
104
+ #+url+:: the url for which to get recent bookmarks
105
+ def self.for_url(url)
106
+ return self.get_feed("/url/#{Digest::MD5.hexdigest(url)}")
107
+ end
108
+ class << self
109
+ alias :hotlist :get_feed
110
+ alias :tagged :by_tags
111
+ alias :for :for_user
112
+ alias :for_network :network
113
+ alias :for_subscriptions :subscripted
114
+ end
115
+ end #of class Bookmarks
116
+
117
+ #access the public information that's not necesarily bookmarks
118
+ #because the information varies, it returns hashes or arrays of hashes instead of a class instance
119
+ class PublicInfo
120
+ include HTTParty
121
+ format :xml
122
+ base_uri "http://feeds.delicious.com/v2/xml"
123
+ include HTTParty::Icebox
124
+ #cache :store => 'memory', :timeout => 300
125
+ cache :store => 'file', :timeout => 300, :location => '/tmp/'
126
+
127
+ #Get a the site alerts
128
+ #Params
129
+ #+url+:: the url to retrieve
130
+ def self.alerts
131
+ res = get("/alerts")
132
+ raise "Communication error with delicious server: #{res.response['status']}" if res.response['status'] =~ /^[45]/
133
+ return [] unless res['rss']['channel'].has_key?('item')
134
+ alerts = []
135
+ res['rss']['channel']['item'].each do |item|
136
+ alerts.push item rescue next
137
+ end
138
+ return alerts
139
+ end
140
+
141
+ #Get public info for a user: the number of bookmarks, network members and network fans
142
+ #Params
143
+ #+user+:: the user
144
+ def self.user_info(user)
145
+ res = get("/userinfo/#{user}")
146
+ raise "Communication error with delicious server: #{res.response['status']}" if res.response['status'] =~ /^[45]/
147
+ return [] unless res['rss']['channel'].has_key?('item')
148
+ info = {}
149
+ res['rss']['channel']['item'].each do |item|
150
+ info[item['title']] = item['description'] rescue next
151
+ end
152
+ return info
153
+ end
154
+
155
+ #Get the top public tags of a user and how many items are bookmarked with them
156
+ #and also get the related tags if a filtering array of tags is provided
157
+ #Params:
158
+ #+user+:: the user to peruse
159
+ #
160
+ def self.tags(user, tags=[])
161
+ res = get("/tags/#{user}/#{tags.join('+')}")
162
+ raise "Communication error with delicious server: #{res.response['status']}" if res.response['status'] =~ /^[45]/
163
+ return [] unless res['rss']['channel'].has_key?('item')
164
+ info = []
165
+ res['rss']['channel']['item'].each do |item|
166
+ info.push({item['title']=>item['description']}) rescue next
167
+ end
168
+ return info
169
+ end
170
+
171
+ #Get a list of the network members for a user
172
+ #Params:
173
+ #+user+:: the user to peruse
174
+ def self.network(user)
175
+ res = get("/networkmembers/#{user}")
176
+ raise "Communication error with delicious server: #{res.response['status']}" if res.response['status'] =~ /^[45]/
177
+ return [] unless res['rss']['channel'].has_key?('item')
178
+ members = []
179
+ res['rss']['channel']['item'].each do |item|
180
+ members.push({:user => item['title'], :profile=>item['link']}) rescue next
181
+ end
182
+ return members
183
+ end
184
+
185
+ #Get a list of the network fans for a user
186
+ #Params:
187
+ #+user+:: the user to peruse
188
+ def self.network_fans(user)
189
+ res = get("/networkfans/#{user}")
190
+ raise "Communication error with delicious server: #{res.response['status']}" if res.response['status'] =~ /^[45]/
191
+ return [] unless res['rss']['channel'].has_key?('item')
192
+ members = []
193
+ res['rss']['channel']['item'].each do |item|
194
+ members.push({:user => item['title'], :profile=>item['link']}) rescue next
195
+ end
196
+ return members
197
+ end
198
+
199
+ #Return summary information for a given url
200
+ def self.url(url)
201
+ #for some reason, the cache screws this one up
202
+ get_without_caching("/urlinfo/#{Digest::MD5.hexdigest(url)}",:format => :json,
203
+ :base_uri => self.default_options[:base_uri].gsub('xml', 'json')).parsed_response[0]
204
+ end
205
+
206
+ class << self
207
+ alias :for_user :user_info
208
+ alias :userinfo :user_info
209
+ alias :tags_for_user :tags
210
+ alias :tags_for :tags
211
+ alias :network_for :network
212
+ alias :network_for_user :network
213
+ alias :network_fans_for :network_fans
214
+ alias :network_fans_for_user :network_fans
215
+ alias :for_url :url
216
+ alias :urlinfo :url
217
+ alias :url_info :url
218
+ end
219
+ end
220
+ end
@@ -0,0 +1,263 @@
1
+ # = Icebox : Caching for HTTParty
2
+ #
3
+ # Cache responses in HTTParty models [http://github.com/jnunemaker/httparty]
4
+ #
5
+ # === Usage
6
+ #
7
+ # class Foo
8
+ # include HTTParty
9
+ # include HTTParty::Icebox
10
+ # cache :store => 'file', :timeout => 600, :location => MY_APP_ROOT.join('tmp', 'cache')
11
+ # end
12
+ #
13
+ # Modeled after Martyn Loughran's APICache [http://github.com/newbamboo/api_cache]
14
+ # and Ruby On Rails's caching [http://api.rubyonrails.org/classes/ActiveSupport/Cache.html]
15
+ #
16
+ # Author: Karel Minarik [www.karmi.cz]
17
+ #
18
+ # === Notes
19
+ #
20
+ # Thanks to Amit Chakradeo for pointing out response objects have to be stored marhalled on FS
21
+ # Thanks to Marlin Forbes for pointing out the query parameters have to be included in the cache key
22
+ #
23
+ #
24
+
25
+ require 'logger'
26
+ require 'ftools'
27
+ require 'tmpdir'
28
+ require 'pathname'
29
+ require 'digest/md5'
30
+
31
+ module HTTParty #:nodoc:
32
+ # == Caching for HTTParty
33
+ # See documentation in HTTParty::Icebox::ClassMethods.cache
34
+ #
35
+ module Icebox
36
+
37
+ module ClassMethods
38
+
39
+ # Enable caching and set cache options
40
+ # Returns memoized cache object
41
+ #
42
+ # Following options are available, default values are in []:
43
+ #
44
+ # +store+:: Storage mechanism for cached data (memory, filesystem, your own) [memory]
45
+ # +timeout+:: Cache expiration in seconds [60]
46
+ # +logger+:: Path to logfile or logger instance [nil, silent]
47
+ #
48
+ # Any additional options are passed to the Cache constructor
49
+ #
50
+ # Usage:
51
+ #
52
+ # # Enable caching in HTTParty, in memory, for 1 minute
53
+ # cache # Use default values
54
+ #
55
+ # # Enable caching in HTTParty, on filesystem (/tmp), for 10 minutes
56
+ # cache :store => 'file', :timeout => 600, :location => '/tmp/'
57
+ #
58
+ # # Use your own cache store (see +AbstractStore+ class below)
59
+ # cache :store => 'memcached', :timeout => 600, :server => '192.168.1.1:1001'
60
+ #
61
+ def cache(options={})
62
+ options[:store] ||= 'memory'
63
+ options[:timeout] ||= 60
64
+ logger = options[:logger]
65
+ @cache ||= Cache.new( options.delete(:store), options )
66
+ end
67
+
68
+ end
69
+
70
+ # When included, extend class with +cache+ method
71
+ # and redefine +get+ method to use cache
72
+ #
73
+ def self.included(receiver) #:nodoc:
74
+ receiver.extend ClassMethods
75
+ receiver.class_eval do
76
+
77
+ # Get reponse from network
78
+ #
79
+ # TODO: Why alias :new :old is not working here? Returns NoMethodError
80
+ #
81
+ def self.get_without_caching(path, options={})
82
+ perform_request Net::HTTP::Get, path, options
83
+ end
84
+
85
+ # Get response from cache, if available
86
+ #
87
+ def self.get_with_caching(path, options={})
88
+ key = path
89
+ key << options[:query].to_s if defined? options[:query]
90
+ if cache.exists?(key) and not cache.stale?(key)
91
+ Cache.logger.debug "CACHE -- GET #{path}#{options[:query]}"
92
+ return cache.get(key)
93
+ else
94
+ Cache.logger.debug "/!\\ NETWORK -- GET #{path}#{options[:query]}"
95
+ response = get_without_caching(path, options)
96
+ cache.set(key, response) if response.code == 200
97
+ return response
98
+ end
99
+ end
100
+
101
+ # Redefine original HTTParty +get+ method to use cache
102
+ #
103
+ def self.get(path, options={})
104
+ self.get_with_caching(path, options={})
105
+ end
106
+
107
+ end
108
+ end
109
+
110
+ # === Cache container
111
+ #
112
+ # Pass a store name ('memory', etc) to new
113
+ #
114
+ class Cache
115
+ attr_accessor :store
116
+
117
+ def initialize(store, options={})
118
+ self.class.logger = options[:logger]
119
+ @store = self.class.lookup_store(store).new(options)
120
+ end
121
+
122
+ def get(key); @store.get encode(key) unless stale?(key); end
123
+ def set(key, value); @store.set encode(key), value; end
124
+ def exists?(key); @store.exists? encode(key); end
125
+ def stale?(key); @store.stale? encode(key); end
126
+
127
+ def self.logger; @logger || default_logger; end
128
+ def self.default_logger; logger = ::Logger.new(STDERR); end
129
+
130
+ # Pass a filename (String), IO object, Logger instance or +nil+ to silence the logger
131
+ def self.logger=(device); @logger = device.kind_of?(::Logger) ? device : ::Logger.new(device); end
132
+
133
+ private
134
+
135
+ # Return store class based on passed name
136
+ def self.lookup_store(name)
137
+ store_name = "#{name.capitalize}Store"
138
+ return Store::const_get(store_name)
139
+ rescue NameError => e
140
+ raise Store::StoreNotFound, "The cache store '#{store_name}' was not found. Did you loaded any such class?"
141
+ end
142
+
143
+ def encode(key); Digest::MD5.hexdigest(key); end
144
+ end
145
+
146
+
147
+ # === Cache stores
148
+ #
149
+ module Store
150
+
151
+ class StoreNotFound < StandardError; end #:nodoc:
152
+
153
+ # ==== Abstract Store
154
+ # Inherit your store from this class
155
+ # *IMPORTANT*: Do not forget to call +super+ in your +initialize+ method!
156
+ #
157
+ class AbstractStore
158
+ def initialize(options={})
159
+ raise ArgumentError, "You need to set the :timeout parameter" unless options[:timeout]
160
+ @timeout = options[:timeout]
161
+ message = "Cache: Using #{self.class.to_s.split('::').last}"
162
+ message << " in location: #{options[:location]}" if options[:location]
163
+ message << " with timeout #{options[:timeout]} sec"
164
+ Cache.logger.info message unless options[:logger].nil?
165
+ return self
166
+ end
167
+ %w{set get exists? stale?}.each do |method_name|
168
+ define_method(method_name) { raise NoMethodError, "Please implement method #{method_name} in your store class" }
169
+ end
170
+ end
171
+
172
+ # ==== Store objects in memory
173
+ # See HTTParty::Icebox::ClassMethods.cache
174
+ #
175
+ class MemoryStore < AbstractStore
176
+ def initialize(options={})
177
+ super; @store = {}; self
178
+ end
179
+ def set(key, value)
180
+ Cache.logger.info("Cache: set (#{key})")
181
+ @store[key] = [Time.now, value]; true
182
+ end
183
+ def get(key)
184
+ data = @store[key][1]
185
+ Cache.logger.info("Cache: #{data.nil? ? "miss" : "hit"} (#{key})")
186
+ data
187
+ end
188
+ def exists?(key)
189
+ !@store[key].nil?
190
+ end
191
+ def stale?(key)
192
+ return true unless exists?(key)
193
+ Time.now - created(key) > @timeout
194
+ end
195
+ private
196
+ def created(key)
197
+ @store[key][0]
198
+ end
199
+ end
200
+
201
+ # ==== Store objects on the filesystem
202
+ # See HTTParty::Icebox::ClassMethods.cache
203
+ #
204
+ class FileStore < AbstractStore
205
+ def initialize(options={})
206
+ super
207
+ options[:location] ||= Dir::tmpdir
208
+ @path = Pathname.new( options[:location] )
209
+ FileUtils.mkdir_p( @path )
210
+ self
211
+ end
212
+ def set(key, value)
213
+ Cache.logger.info("Cache: set (#{key})")
214
+ File.open( @path.join(key), 'w' ) { |file| file << Marshal.dump(value) }
215
+ true
216
+ end
217
+ def get(key)
218
+ data = Marshal.load(File.read( @path.join(key)))
219
+ Cache.logger.info("Cache: #{data.nil? ? "miss" : "hit"} (#{key})")
220
+ data
221
+ end
222
+ def exists?(key)
223
+ File.exists?( @path.join(key) )
224
+ end
225
+ def stale?(key)
226
+ return true unless exists?(key)
227
+ Time.now - created(key) > @timeout
228
+ end
229
+ private
230
+ def created(key)
231
+ File.mtime( @path.join(key) )
232
+ end
233
+ end
234
+ end
235
+
236
+ end
237
+ end
238
+
239
+
240
+ # Major parts of this code are based on architecture of ApiCache.
241
+ # Copyright (c) 2008 Martyn Loughran
242
+ #
243
+ # Other parts are inspired by the ActiveSupport::Cache in Ruby On Rails.
244
+ # Copyright (c) 2005-2009 David Heinemeier Hansson
245
+ #
246
+ # Permission is hereby granted, free of charge, to any person obtaining
247
+ # a copy of this software and associated documentation files (the
248
+ # "Software"), to deal in the Software without restriction, including
249
+ # without limitation the rights to use, copy, modify, merge, publish,
250
+ # distribute, sublicense, and/or sell copies of the Software, and to
251
+ # permit persons to whom the Software is furnished to do so, subject to
252
+ # the following conditions:
253
+ #
254
+ # The above copyright notice and this permission notice shall be
255
+ # included in all copies or substantial portions of the Software.
256
+ #
257
+ # THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
258
+ # EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
259
+ # MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
260
+ # NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
261
+ # LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
262
+ # OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
263
+ # WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
@@ -0,0 +1,10 @@
1
+ require 'rubygems'
2
+ require 'test/unit'
3
+ require 'shoulda'
4
+
5
+ $LOAD_PATH.unshift(File.join(File.dirname(__FILE__), '..', 'lib'))
6
+ $LOAD_PATH.unshift(File.dirname(__FILE__))
7
+ require 'deliruby'
8
+
9
+ class Test::Unit::TestCase
10
+ end
@@ -0,0 +1,7 @@
1
+ require 'helper'
2
+
3
+ class TestDeliruby < Test::Unit::TestCase
4
+ should "probably rename this file and start testing for real" do
5
+ flunk "hey buddy, you should probably rename this file and start testing for real"
6
+ end
7
+ end
metadata ADDED
@@ -0,0 +1,107 @@
1
+ --- !ruby/object:Gem::Specification
2
+ name: deliruby
3
+ version: !ruby/object:Gem::Version
4
+ hash: 27
5
+ prerelease: false
6
+ segments:
7
+ - 0
8
+ - 1
9
+ - 0
10
+ version: 0.1.0
11
+ platform: ruby
12
+ authors:
13
+ - lfborjas
14
+ autorequire:
15
+ bindir: bin
16
+ cert_chain: []
17
+
18
+ date: 2010-08-29 00:00:00 -06:00
19
+ default_executable:
20
+ dependencies:
21
+ - !ruby/object:Gem::Dependency
22
+ name: thoughtbot-shoulda
23
+ prerelease: false
24
+ requirement: &id001 !ruby/object:Gem::Requirement
25
+ none: false
26
+ requirements:
27
+ - - ">="
28
+ - !ruby/object:Gem::Version
29
+ hash: 3
30
+ segments:
31
+ - 0
32
+ version: "0"
33
+ type: :development
34
+ version_requirements: *id001
35
+ - !ruby/object:Gem::Dependency
36
+ name: httparty
37
+ prerelease: false
38
+ requirement: &id002 !ruby/object:Gem::Requirement
39
+ none: false
40
+ requirements:
41
+ - - "="
42
+ - !ruby/object:Gem::Version
43
+ hash: 5
44
+ segments:
45
+ - 0
46
+ - 6
47
+ - 1
48
+ version: 0.6.1
49
+ type: :runtime
50
+ version_requirements: *id002
51
+ description: Ruby wrapper for the delicious.com public rss feeds
52
+ email: me@lfborjas.com
53
+ executables: []
54
+
55
+ extensions: []
56
+
57
+ extra_rdoc_files:
58
+ - LICENSE
59
+ - README.rdoc
60
+ files:
61
+ - .document
62
+ - .gitignore
63
+ - LICENSE
64
+ - README.rdoc
65
+ - Rakefile
66
+ - VERSION
67
+ - lib/deliruby.rb
68
+ - lib/httparty_icebox.rb
69
+ - test/helper.rb
70
+ - test/test_deliruby.rb
71
+ has_rdoc: true
72
+ homepage: http://github.com/lfborjas/deliruby
73
+ licenses: []
74
+
75
+ post_install_message:
76
+ rdoc_options:
77
+ - --charset=UTF-8
78
+ require_paths:
79
+ - lib
80
+ required_ruby_version: !ruby/object:Gem::Requirement
81
+ none: false
82
+ requirements:
83
+ - - ">="
84
+ - !ruby/object:Gem::Version
85
+ hash: 3
86
+ segments:
87
+ - 0
88
+ version: "0"
89
+ required_rubygems_version: !ruby/object:Gem::Requirement
90
+ none: false
91
+ requirements:
92
+ - - ">="
93
+ - !ruby/object:Gem::Version
94
+ hash: 3
95
+ segments:
96
+ - 0
97
+ version: "0"
98
+ requirements: []
99
+
100
+ rubyforge_project:
101
+ rubygems_version: 1.3.7
102
+ signing_key:
103
+ specification_version: 3
104
+ summary: Ruby wrapper for the delicious.com public rss feeds
105
+ test_files:
106
+ - test/helper.rb
107
+ - test/test_deliruby.rb