deliruby 0.1.0

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 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