mattapayne / tumblr4rails

A Ruby on Rails plugin for accessing the Tumblr API

This URL has Read+Write access

Matt Payne (author)
Mon Oct 20 22:27:51 -0700 2008
commit  d3a09831f4df2795b26f55ff1a7de7eea7426509
tree    8483076c6a251aa326bbf3a6881446d0c5740783
parent  732829945bac54f09be22e2476d41809a4ff6813
tumblr4rails / spec / regular_posts_json
100644 1 lines (1 sloc) 19.63 kb
1
var tumblr_api_read = {"tumblelog":{"title":"Coding with headphones","description":"","name":"mpayne","timezone":"US\/Eastern","cname":false,"feeds":[{"id":235529,"title":"Twitter \/ mapayne","attribution":null,"error":0,"error_text":"No items are fewer than 2 days old.","url":null,"import-type":"regular-no-title","next-update-in-seconds":4031}]},"posts-start":0,"posts-total":20,"posts-type":"regular","posts":[{"id":41198969,"url":"http:\/\/mpayne.tumblr.com\/post\/41198969","type":"regular","date-gmt":"2008-07-06 15:45:07 GMT","date":"Sun, 06 Jul 2008 11:45:07","bookmarklet":0,"mobile":0,"feed-item":"","from-feed-id":0,"regular-title":"Tumblr4Rails Almost Finished!","regular-body":"It&#8217;s true! Tumblr4Rails is nearly complete. I&#8217;ve been hard at work refactoring it to be the best it can be."},{"id":40969338,"url":"http:\/\/mpayne.tumblr.com\/post\/40969338","type":"regular","date-gmt":"2008-07-04 14:12:29 GMT","date":"Fri, 04 Jul 2008 10:12:29","bookmarklet":0,"mobile":0,"feed-item":"","from-feed-id":0,"regular-title":"Extending Your Rails Application","regular-body":"<p>I played around yesterday for a few hours with a concept that I&#8217;ve recently been thinking about: application extension. I&#8217;ve been thinking about an application for a vertical market. This application would be more or less the same across various clients, but there might be small differences on a per-client basis.<\/p>\n<p>So the question is, how can I write a Rails application where the main application is essentially like a generic framework, but be able to override certain features or add new features. This way, the bulk of the code would only need to be created once. The client-specific features could simply be plugged in.<\/p>\n<p>Well, it turns out that it may be possible. As I see it, there are really only a couple of things that would need to be overridden or extended. Firstly, we have controllers. Controllers provide the functionality for using the application from a procedural, use-case perspective. Then we have models. Models represent the logic, data and behaviour of the application. Finally, we have views. Views provide the user interface and user experience within the application.<\/p>\n<p>It seems to me that that it should be possible to alter dynamically the collection of controllers, models and views making up the basis of the core application.<\/p>\n<p>The mechanism? Well, the ability to re-open Ruby classes of course. In my mind (and I&#8217;m sure that this is not a new concept), this could work using the following approach.<\/p>\n<p>1) The Rails application loads normally.<\/p>\n<p>2) We assume that any &#8216;extensions&#8217; to the application will appear in a folder called &#8216;app_extension&#8217; (or whatever) and that the directory structure of this folder mirrors that of the rails app. Visually, it could look something like this:<\/p>\n<pre><p>application\/<\/p><p> app\/<\/p><p> controllers\/<\/p><p> helpers\/<\/p><p> models\/<\/p><p> views\/<\/p><p> lib\/<\/p><p> app_extension\/<\/p><p> app\/<\/p><p> controllers\/<\/p><p> helpers\/<\/p><p> models\/<\/p><p> views\/<\/p><p> lib\/<\/p><\/pre>\n<p>3) In an initializer (which is run close to the end of the environment loading process, we check to see if this folder exists. If it does, we reopen ActiveSupport::Dependencies and reimplement a method called load_missing_constant. It could look something like the code show below (Note: this may not be the best approach, and I haven&#8217;t tested it beyond verifying that it works. Comments\/suggestions are encouraged).<\/p>\n<pre> def self.enhance_dependencies<br\/> ActiveSupport::Dependencies.class_eval do<br\/> #load up the stuff in the extension dir<br\/> ext_path = File.join(\"#{Rails.root}\", \"app_extension\")<br\/> class_variable_set(:@@extension_dir, Dir[\"#{ext_path}\/**\/*.rb\"].flatten.freeze)<br\/> alias_method :\"old_load_missing_constant\", :\"load_missing_constant\"<br\/> def load_missing_constant(from_mod, const_name)<br\/> extended = false<br\/> obj = old_load_missing_constant(from_mod, const_name)<br\/> unless obj.nil?<br\/> #get the name of the file<br\/> name = qualified_name_for(from_mod, const_name).underscore<br\/> #determine if there's a match in the extension dir<br\/> matches = class_variable_get(:@@extension_dir).select {|file| File.basename(file, \".rb\") == name}<br\/> if matches &amp;&amp; !matches.empty?<br\/> if !loaded.include?(File.expand_path(matches.first))<br\/> require_or_load(matches.first)<br\/> extend_view_path(obj) if controller?(obj)<br\/> extended = true<br\/> end<br\/> end<br\/> end<br\/> unless obj.respond_to?(:extended?)<br\/> mark_extended(obj, extended)<br\/> end<br\/> obj<br\/> end<br\/><br\/> private<br\/><br\/> def mark_extended(klazz, extended=false)<br\/> klazz.class_eval(<br\/> %{<br\/> def self.extended?<br\/> #{extended}<br\/> end<br\/> }<br\/> )<br\/> end<br\/><br\/> def controller?(klazz)<br\/> klazz.ancestors.include?(ActionController::Base)<br\/> end<br\/><br\/> def extend_view_path(klazz)<br\/> return unless klazz.respond_to?(:view_paths)<br\/> #Puts the app extension view path first in the controller's array<br\/> #of view paths so that it will be searched first.<br\/> klazz.view_paths.unshift(File.join(\"#{Rails.root}\/app_extension\", \"app\", \"views\"))<br\/> end<br\/> end<br\/> end<br\/><\/pre>\n<p>This code essentially aliases the original load_missing_constant and defines a new method that loads files within the app_extension directory. When an unloaded constant is encountered, we call the original method so that it is loaded properly, but we also search the files in the app_extension directory to see if there is a version there as well. If there is, we load it too. This effectively re-opens the class and adds new functionality or overrides existing functionality. Additionally, we add a method to the newly loaded class called extended? so that we can know in the regular application if the class has been modified by the extension. Also, if the newly loaded constant is derived from ActionController::Base, we inject the path to the views directory in the app_extension folder. The path must be at the beginning of the view_paths array so that it is searched first, effectively allowing us to override views. If the view exists in the app_extension view path, it will be used. If not, the default view path will be used.<\/p>\n<p>So this gives us a rudimentary means to extend the base application. Is it perfect? I doubt it. For example, the only way to remove functionality would be to undefine a method, etc. Does it address everything? I doubt it. For example, it&#8217;s likely that there may be client-specific migrations that need to be run, etc.<\/p>\n<p>Perhaps in my next post I&#8217;ll address the possibility of hosting client-specific migrations within app_extensions. I want to be able to run rake db:migrate and have them picked up. I&#8217;ll have to spend some time researching the possible approaches to this.<\/p>"},{"id":40607158,"url":"http:\/\/mpayne.tumblr.com\/post\/40607158","type":"regular","date-gmt":"2008-07-01 22:21:22 GMT","date":"Tue, 01 Jul 2008 18:21:22","bookmarklet":0,"mobile":0,"feed-item":"","from-feed-id":0,"regular-title":"<pre> tags in Tumblr","regular-body":"So, initially, I thought that Tumblr did weird stuff with &lt;pre&gt; tags, but it looks like I&#8217;m wrong. It only seems to cause problems in the dashboard. When looking at the public tumblelog, though, the formatting is good."},{"id":40555675,"url":"http:\/\/mpayne.tumblr.com\/post\/40555675","type":"regular","date-gmt":"2008-07-01 14:21:00 GMT","date":"Tue, 01 Jul 2008 10:21:00","bookmarklet":0,"mobile":0,"feed-item":"","from-feed-id":0,"regular-title":"Revision to updating RubyGems","regular-body":"<p>Ignore the post below. While it did upgrade to 1.2.0, unfortunately, nothing worked properly. Now I can&#8217;t seem to install anything and am receiving the following error: (Gem::RemoteFetcher::FetchError).<\/p>\n<p>Screw this - reverting back to 1.1.1 for the time being.<\/p>"},{"id":40554720,"url":"http:\/\/mpayne.tumblr.com\/post\/40554720","type":"regular","date-gmt":"2008-07-01 14:12:56 GMT","date":"Tue, 01 Jul 2008 10:12:56","bookmarklet":0,"mobile":0,"feed-item":"","from-feed-id":0,"regular-title":"Upgrading 10 RubyGems 1.2.0 on Ubuntu Hardy Heron","regular-body":"<p>So doing a simple gem update &#8212;system did not work. Nothing to update. Still at RubyGems 1.1.1<\/p>\n<p>Turns out the solution, after a bit of Googling, is:<\/p>\n<p><b>sudo gem install rubygems-update -v 1.1.1&#160;<\/b><\/p>\n<p>and then do gem update &#8212;system<\/p>\n<p>Hope that helps someone else.<\/p>"},{"id":40331319,"url":"http:\/\/mpayne.tumblr.com\/post\/40331319","type":"regular","date-gmt":"2008-06-29 21:18:41 GMT","date":"Sun, 29 Jun 2008 17:18:41","bookmarklet":0,"mobile":0,"feed-item":"","from-feed-id":0,"regular-title":"Tumblr4Rails","regular-body":"I&#8217;m getting pretty close to being done writing my Tumblr4Rails plugin. So far it looks pretty good and will support Tumblr&#8217;s entire API. Just need to do some refactoring and spec enhancements."},{"id":40102033,"url":"http:\/\/mpayne.tumblr.com\/post\/40102033","type":"regular","date-gmt":"2008-06-27 21:01:26 GMT","date":"Fri, 27 Jun 2008 17:01:26","bookmarklet":0,"mobile":0,"feed-item":"","from-feed-id":0,"regular-title":"JamCruise!","regular-body":"Just booked our cabin for <a target=\"_blank\" href=\"http:\/\/www.jamcruise.com\">JamCruise<\/a>. Tamara and I were thinking about something like Italy for our honeymoon, but then we remembered JamCruise. What could be more fun than sitting in a pool all day, drinking beer and listening to some really good tunes? The cruise takes place January 4 - 9, 2009. Seems a long way off, but I can&#8217;t wait to go. It stops at Belize City and on the Yucatan penninsula. Should be really cool. I still am somewhat shocked that Tamara was into doing it."},{"id":39970352,"url":"http:\/\/mpayne.tumblr.com\/post\/39970352","type":"regular","date-gmt":"2008-06-26 21:03:38 GMT","date":"Thu, 26 Jun 2008 17:03:38","bookmarklet":0,"mobile":0,"feed-item":"","from-feed-id":0,"regular-title":"hash_including()","regular-body":"<p>So I was writing some specs today when I discovered that I needed to do something I haven&#8217;t had the need to do before: validate that a private method called by a public method received the correct parameters. Easy right? Yup should be - if its a parameratized method. In this case, its not - the method takes a hash.<\/p>\n<p>So it&#8217;s not quite as simple as something like:<\/p>\n<pre><p>it \"should call private_method with the correct arguments\" do<\/p><p> some_hash = {:arg1 =&gt; \"blah\", :arg2 =&gt; \"blah\"}<\/p><p> @object.should_receive(:private_method).with(some_hash)<\/p><p> @object.public_method(\"something\", \"something_else\")<\/p><p>end<\/p><\/pre>\n<p>Not as simple because, of course, we can&#8217;t guarantee the ordering of the hash and the test will sometimes (not always) fail.<\/p>\n<p>Enter hash_including()<\/p>\n<p>hash_including allows you to specify the same spec like this:<\/p>\n<pre><p>it \"should call private_method with the correct arguments\" do<\/p><p> some_hash = {:arg1 =&gt; \"blah\", :arg2 =&gt; \"blah\"}<\/p><p> @object.should_receive(:private_method).with(hash_including(some_hash))<\/p><p> @object.public_method(\"something\", \"something_else\")<\/p><p>end<\/p><\/pre>\n<p>Looking at the source code, hash_including compares keys and values from the expected against the target and returns false if there is not match, rather than relying on ==<\/p>\n<p>This also means that you don&#8217;t need to specify the complete hash, but only the important components for that spec.<\/p>"},{"id":39866327,"url":"http:\/\/mpayne.tumblr.com\/post\/39866327","type":"regular","date-gmt":"2008-06-26 03:13:45 GMT","date":"Wed, 25 Jun 2008 23:13:45","bookmarklet":0,"mobile":0,"feed-item":"","from-feed-id":0,"regular-title":"Zappa!","regular-body":"<p>Just got tickets for <a target=\"_blank\" href=\"http:\/\/www.zappaplayszappa.com\/\">Zappa Plays Zappa<\/a><\/p>\n<p>If you like Frank Zappa or are interested in somewhat insane music, check it out. I&#8217;ve seen them once already, and it really blew me away.<\/p>"},{"id":39866161,"url":"http:\/\/mpayne.tumblr.com\/post\/39866161","type":"regular","date-gmt":"2008-06-26 03:11:43 GMT","date":"Wed, 25 Jun 2008 23:11:43","bookmarklet":0,"mobile":0,"feed-item":"","from-feed-id":0,"regular-title":"Moving","regular-body":"<p>Man, am I tired of moving. Saturday, I&#8217;m helping a friend move. I actually don&#8217;t mind that. At least it&#8217;s someone else&#8217;s crap that needs to get moved. Hopefully they&#8217;ve packed. Nothing worse than having to pack someone else&#8217;s stuff.<\/p>\n<p>But then, I need to haul all my stuff and Tamara&#8217;s stuff (mostly hers) from Calgary back to Waterloo. Originally, we thought that we could just ship it, but it turns out that costs some serious dollars. So, no after all, we&#8217;re renting a U-Haul and dragging it all cross country. Really looking forward to that.<\/p>"},{"id":39418843,"url":"http:\/\/mpayne.tumblr.com\/post\/39418843","type":"regular","date-gmt":"2008-06-22 19:11:00 GMT","date":"Sun, 22 Jun 2008 15:11:00","bookmarklet":0,"mobile":0,"feed-item":"","from-feed-id":0,"regular-title":"Sugar is bad","regular-body":"You want to know how much sugar you consume in your coffee on a regular basis? Buy a box of 144 sugar cubes and see how long it lasts. I gotta stop with the sugar."},{"id":39341591,"url":"http:\/\/mpayne.tumblr.com\/post\/39341591","type":"regular","date-gmt":"2008-06-22 02:57:40 GMT","date":"Sat, 21 Jun 2008 22:57:40","bookmarklet":0,"mobile":0,"feed-item":"","from-feed-id":0,"regular-title":"Crepes are good","regular-body":"Just got back from Toronto, where my dad and I picked up his wife, Susan. We went out for a quick pre-birthday (mine) celebration to a crepe place on Queen St. Damn good!"},{"id":39110734,"url":"http:\/\/mpayne.tumblr.com\/post\/39110734","type":"regular","date-gmt":"2008-06-20 04:37:00 GMT","date":"Fri, 20 Jun 2008 00:37:00","bookmarklet":0,"mobile":0,"feed-item":"","from-feed-id":0,"regular-title":"JamBase4Rails Released!","regular-body":"<p>Just put it up on GitHub <a target=\"_blank\" href=\"http:\/\/github.com\/mattapayne\/jambase4rails\/tree\/master\">here<\/a>. I&#8217;d be interested to see if anyone cares!<\/p>\n<p>Still need to test the install\/uninstall stuff, but what the hell.<\/p>","tags":["ruby on rails"]},{"id":38963865,"url":"http:\/\/mpayne.tumblr.com\/post\/38963865","type":"regular","date-gmt":"2008-06-19 02:09:54 GMT","date":"Wed, 18 Jun 2008 22:09:54","bookmarklet":0,"mobile":0,"feed-item":"","from-feed-id":0,"regular-title":"Also working on a Tumblr plugin","regular-body":"I&#8217;ve also been working (slowly) on a Tumblr plugin for Ruby on Rails. I know that a gem already exists out there, but what the hell. I wanted to write one!"},{"id":38963525,"url":"http:\/\/mpayne.tumblr.com\/post\/38963525","type":"regular","date-gmt":"2008-06-19 02:05:16 GMT","date":"Wed, 18 Jun 2008 22:05:16","bookmarklet":0,"mobile":0,"feed-item":"","from-feed-id":0,"regular-title":"Apparently Tumblr has issues with <pre> tags","regular-body":"I tried them in my last post, but the &lt;pre&gt; tag just ended up getting turned into a closed tag (&lt;pre \/&gt;) with the content outside of it. I removed it now."},{"id":38963162,"url":"http:\/\/mpayne.tumblr.com\/post\/38963162","type":"regular","date-gmt":"2008-06-19 01:59:00 GMT","date":"Wed, 18 Jun 2008 21:59:00","bookmarklet":0,"mobile":0,"feed-item":"","from-feed-id":0,"regular-title":"Almost Finished","regular-body":"<p>I&#8217;m almost finished the Ruby on Rails plugin that I&#8217;ve been working on to provide easy access to the <a target=\"_blank\" title=\"JamBase\" href=\"http:\/\/www.jambase.com\">JamBase<\/a> API. So far it looks like this:<\/p>\n<p>#The hash of arguments is optional in all cases<\/p>\n<p>@events = JamBase4Rails::API.search_by_artist(&#8220;A Band&#8221;, {:zip =&gt; &#8220;90210&#8221;, :radius =&gt; 50})<\/p>\n<p>-or-<\/p>\n<p>@events = JamBase4Rails::API.search_by_zipcode(&#8220;90210&#8221;, {:artist =&gt; &#8220;A Band&#8221;})<\/p>\n<p>-or-<\/p>\n<p>@events = JamBase4Rails::API.search({:artist =&gt; &#8220;Something&#8221;, :zip =&gt; &#8220;90210&#8221;, :radius =&gt; 20})<\/p>\n<p>#loop over all found events<\/p>\n<p>@events.each do |event|<\/p>\n<p>puts event.event_date<\/p>\n<p>puts event.event_url<\/p>\n<p>#events have venues (venues also have venue_city, venue_state, venue_zip, etc.)<\/p>\n<p>puts event.venue_name<\/p>\n<p>event.artists each do |artist|<\/p>\n<p>puts artist.artist_name<\/p>\n<p>end<\/p>\n<p>end<\/p>\n<p>So, all in all, pretty cool. Something to note is that JamBase requires all sites using the API to display an attribution. Read <a target=\"_blank\" title=\"JamBase attribution docs\" href=\"http:\/\/developer.jambase.com\/docs\">here<\/a>. I&#8217;ve created a helper that gets mixed in to the views that allows for this:<\/p>\n<p>&lt;%= jambase_text_attribution(text, link_options={}, span_options={}) %&gt;<\/p>\n<p>-or-<\/p>\n<p>&lt;%= jambase_image_attribution(image_options={}, link_options={}) %&gt;<\/p>\n<p>-or-<\/p>\n<p>&lt;%= jambase_favicon(image_options={}) %&gt;<\/p>\n<p>I&#8217;m pretty pleased with the plugin so far. A little more work to do in refinements, but should be available on GitHub soon.<\/p>"},{"id":38841974,"url":"http:\/\/mpayne.tumblr.com\/post\/38841974","type":"regular","date-gmt":"2008-06-18 05:10:56 GMT","date":"Wed, 18 Jun 2008 01:10:56","bookmarklet":0,"mobile":0,"feed-item":"","from-feed-id":0,"regular-title":"Back to working on the JamBase plugin","regular-body":"Fixed the issue with the JamBase API access problem. Turns out this issue was caused by my code ;&lt; Damn."},{"id":38706199,"url":"http:\/\/mpayne.tumblr.com\/post\/38706199","type":"regular","date-gmt":"2008-06-17 05:43:47 GMT","date":"Tue, 17 Jun 2008 01:43:47","bookmarklet":0,"mobile":0,"feed-item":"","from-feed-id":0,"regular-title":"Still waiting on JamBase","regular-body":"I would really like to finish this plugin &#8230;"},{"id":38699473,"url":"http:\/\/mpayne.tumblr.com\/post\/38699473","type":"regular","date-gmt":"2008-06-17 04:20:00 GMT","date":"Tue, 17 Jun 2008 00:20:00","bookmarklet":0,"mobile":0,"feed-item":"","from-feed-id":0,"regular-title":"I wrote some Rails Plugins","regular-body":"Today I wrote (partially) a Ruby on Rails plugin for the <a target=\"_blank\" title=\"JamBase\" href=\"http:\/\/www.jambase.com\">JamBase<\/a> api. Unfortunately, my developer api key has not been activated, so I can&#8217;t continue."},{"id":38776332,"url":"http:\/\/mpayne.tumblr.com\/post\/38776332","type":"regular","date-gmt":"2008-06-17 04:00:00 GMT","date":"Tue, 17 Jun 2008 00:00:00","bookmarklet":0,"mobile":0,"feed-item":"","from-feed-id":0,"regular-title":"This was posted from my Ruby on Rails plugin","regular-body":"This post was generated from the beginnings of my Ruby on Rails Tumblr plugin"}]};