Get ready for the TextMate “Trundle to Rails 2.0 Bundle”
Have you noticed lately that the Rails TextMate git pull origin two_point_oohbundle (also installed with e - the Text Editor for Windows) isn’t quite as helpful as it once was - guiding and amplifying your rails app development.
Your resourceful/restful rails apps are now filled with named routes, but none of the helpers (link_to, redirect_to, etc) use them. The default view template was still rhtml for a long time (latest svn trunk fixes this to be html.erb, though Marc Jeanson is having a crack at supporting both through a per-project config file).
Or perhaps you were hoping for some snippets for new features of Rails 2.0: assert_difference (asd), assert_select (ass), respond_to (rst).
Or just maybe, you’ve been updating your local bundle to Rails 2.0 already? Please say yes, please say yes. If you are saying “yes”, read at the bottom for how/where to send me the patches. If you are saying “no”, read at the bottom for how/where to start creating patches and sending them to me.
Firstly, DHH didn’t write the Rails bundle
I didn’t know this - I just assumed the guy who created Rails and helped promote TextMate also created the Ruby on Rails TextMate bundle. Instead if was WWF’s “The Rock” himself Duane Johnson (figurines sold separately, and may look different to the real Duane Johnson who writes Rails code instead of prancing around in his underpants with other adult males in confined spaces).
The TextMate SVN logs say that it the bundle was first committed in Jan 2005. Its now Jan 2008. That’s three years of maintaining a suite of code that thousands of people take for granted every day writing Rails code. What a champion.
Like the Australian Cricket team’s wicketkeeper, Adam Gilchrist, he’s also retiring at the top of his game.
New Maintainer… me
Instead of maintaining my own projects, I’ve decided to maintain this one. I love the TextMate bundles. Before I had a Mac, I ported them across to RadRails and many RadRailers entered a golden era of snippet happiness.
My bundle-fu may not be the equivalent of Bruce Lee, but I’m keen to see my favourite Rails tool live on strong.
Bleeding edge bundle
Whilst the TextMate SVN administrators process the maintainer change request (Allan is on holidays somewhere and he took the house keys with him), you might think all is lost!
Never.
The straight-off-the-cow bleeding edge Rails bundle can be accessed via Git. Specifically the branch two_point_ooh.
So, let’s all do away with our pre-existing Rails bundle and live on the edge.
cd ~/"Library/Application Support/TextMate/Bundles/" mv "Ruby on Rails.tmbundle" "RoR.tmbundle.orig" git clone git://github.com/drnic/ruby-on-rails-tmbundle.git "Ruby on Rails.tmbundle" cd "Ruby on Rails.tmbundle" git checkout --track -b two_point_ooh origin/two_point_ooh git pull
Now, back in TextMate, go to menu “Bundles” > “Bundle Editor” > “Reload Bundles”.
You, my friend, are sitting pretty.
Upgrading to when newer blood is on the bleeding edge
Coming back for more?
cd ~/"Library/Application Support/TextMate/Bundles/Ruby on Rails.tmbundle" git checkout two_point_ooh # optional; only if you changed to another branch etc git pull
Now, back in TextMate, go to menu “Bundles” > “Bundle Editor” > “Reload Bundles”.
Bathe yourself in glory. Splash it all over yourself.
Submitting patches
If you go into TextMate, launch the Bundle Editor (Cmd+Alt+Ctrl+B) and change something in the Ruby on Rails bundle, then files will added/changed within your local git clone.
What I mean is, you can very simply submit patches now. [When I say "simply", I mean, "I think this is the best approach but I could be completely wrong here..."] Git is so cool that I don’t know what its doing most of the time; but let’s hang in there as a team.
cd ~/"Library/Application Support/TextMate/Bundles/Ruby on Rails.tmbundle" git add . # to add any new files/snippets/commands you created; TextMate uses one file per snippet git commit -a -m "Useful message about this patch goes here" git diff -p HEAD^ HEAD
[Theoretically] this creates a patch based on your most recent commit. So you can easily create patches for changes even if your repository and the remote/central/bleeding edge repository look different.
If your patch was created over several commits, then replace HEAD^ with HEAD~4, if there were 4 commits.
I think. God I could be wrong about this though.
Just try your best and if it works, let me know and I’ll remove all this “I don’t know what I’m talking about” text and future readers will think I know everything. Web 2.0 rocks.
Send patches to…
Me drnicwilliams@gmail.com [update] or via the new Google Group.
Want to fork the git repo? Use a free account at Gitorious or GitHub.
|
|
| Ruby on Rails: TextMate |
| Visit this group |
Trundle to Rails 2.0 Bundle
Arguably, we just keep patching the bundle forever and bleeding edges will continue to live happily within the blood and muck of said existence.
But, lots of other people want nicely zipped up bundles. No doubt “e - the Text Editor” will want a “released” version so they can clean it of all the ugly things they can’t yet replicate like Macros. And eventually I’ll push it all down to the SVN repo and it will be released with future TextMate upgrades.
So, there will be a “Rails 2.0 compatible” grand opening one day soon. Probably before 14th of March 2008, when I’m giving a “What’s new in Rails 2.0?” talk at QCON London. Showing off the new bundle will be sweet.
That gives us 6 wks to refactor this bundle to make it a shining example of all that is wonderful in Rails 2.0. Every patch helps.
[Please consider Rails 1.2.6 and before developers when conceiving of patches, and please consult your doctor if pain persists.]
Find objects in IRB directly from browser URLs
A long time ago, I tired of going into the irb/console and finding objects/models using the traditional ActiveRecord command Person.find(15) and now I’m sitting pretty: I can paste in URLs to fetch objects.
# No more of this: => Person.find(15) # instead: => people/15
people/15 is something you’ll copy+paste directly from your browser: http://localhost:3000/people/15
Of course, the url is based on your routing + controllers, so the assumption here is that your routes/controllers map to your active record models. That is, your app is smothered in RESTful love and cuddles.
Not following this? Here’s a video:
How to make this work at home
Copy and paste the following into your .irbrc file:
Thanks goes to…
The some original code for this comes via Mike Clark, who had the idea for syntax activity(6). This was good.
I previously had another idea to support the syntax 6.to_activity using the RubyGem to_activerecord. I still like the id.to_class_name structure of this and still use it.
But if I have a perfectly nice looking url sitting in front of me, I can now paste the class_name/id part into irb and I’m off and running.
Happy New Year.
Showing off data on a timeline
I’m still trying to justify my effort writing the MagicCGI code. It let you get an XML or JSON feed for any database, with some basic conditionals, limits etc. The existing demo is for my blog database.
I think this one is kinda cool - showing off all your blog posts/articles on a timeline:
To get the timeline working with the schema output from the Magic CGI, I needed to write my own Timeline EventSource. Currently I don’t do anything fancy with the generated bubbles - I just use the defaults.
The MagicCGI query gets all wp_posts (Wordpress schema) rows, where “post_status=publish” and only returns fields that are relevant (notably ignores the large post_content field holding the blog content):
The other cool thing I did here was to deploy it all with Capistrano (an html page + javascript libs). It even deploys/manages a copy of the Timeline trunk onto the server. This is the first time I’ve deployed a non-Ruby/Rails app using Capistrano, and once I got it set up it becomes much easier to manage than using an FTP app, etc.
How easy? cap1 update (note the dubious use of capistrano 1… I still… haven’t… converted… to 2.0…)
I’m not confident enough that my solution is sexy enough to outline in detail, so if you’re interested in deploying Javascript apps etc with Capistrano, just checkout the code (below) and look at the config/deploy.rb script. (note that I’ve disabled the deprec require statement as it assumes I’m wanted to run some mongrels etc, but deprec is very handy for setting up ssh and other fun stuff at the start).
So, no details here, just a fun example.
If the Timeline tickles your fancy, their website has lots of tutorials, and/or check out my html/javascript code.
svn co http://drnicwilliams.com/svn/blog_timeline/trunk blog_timeline
If the MagicCGI tickles your fancy, its also only on svn at the moment, though its docco should be pretty good. I think.
svn co http://rubyforge.org/var/svn/magicmodels/magic_cgi/trunk magic_cgi
MagicCGI shows OpenID user count
In the last 20 days, 43 people have used OpenID to leave comments. That’s very cool.
Corollary: add OpenID login to your blog.
Even cooler - Dynamic counter
The screen shot comes from the Comments form.
If you look at the comments form in a week, month, year, the counter above have be changed from its original value 43.
No fancy Wordpress plugins (I don’t do PHP)
No Apache tricks. (I don’t know any Apache tricks)
Instead with Javascript/HTML attached to JSON attached to a RubyCGI script attached to my Wordpress database via ActiveRecords and some magic.
I call it MagicCGI. I also call it Frigging Scary.
Try the following:
$ curl -v "http://drnicwilliams.com/cgi-bin/wp_drnicwilliams.cgi?table=wp_openid_identities&action=count&format=json" < Content-Type: txt/json < 43
Or some XML?
$ curl -v "http://drnicwilliams.com/cgi-bin/wp_drnicwilliams.cgi?table=wp_openid_identities&action=count&format=xml" < Content-Type: txt/xml < <?xml version="1.0" encoding="UTF-8"?> <result> <count type="integer">43</count> </result>
Raw data?
$ curl "http://drnicwilliams.com/cgi-bin/wp_drnicwilliams.cgi?table=wp_posts&field=post_title&limit=5&order=post_title"
[{"attributes": {"post_title": "OpenID count"}},
{"attributes": {"post_title": "MagicCGI shows OpenID user count"}},
{"attributes": {"post_title": "map_by_method now works with ActiveRecord associations"}},
{"attributes": {"post_title": "Feedburner"}},
{"attributes": {"post_title": "One year on the InterTubes"}}
]
Getting kinda scary now, I think.
Ooh, just how much magic?
Want a list of available tables to play with?
$ curl "http://drnicwilliams.com/cgi-bin/wp_drnicwilliams.cgi?meta=tables"
[{table_name: 'wp_users'},...]
- add
&format=xmlto XML output; JSON is default - add
&meta=columnsto include the column schema definitions - add
&table_name=wp_postsfor each table you want (instead of all the tables)
E.g. to see the columns for wp_posts and no other table, in XML:
$ curl "http://drnicwilliams.com/cgi-bin/wp_drnicwilliams.cgi?meta=tables&meta=columns&table_name=wp_posts&format=xml"
A list of all urls and internal user_ids for users/OpenID users/registered commenters?
$ curl "http://drnicwilliams.com/cgi-bin/wp_drnicwilliams.cgi?table=wp_users&field=user_url&field=id"
[{"attributes": {"id": "1", "user_url": "http://drnicwilliams.com"}}, ...
So, now we know Dr Nic = user id 1.
Oh oh oh, how about a list of comments for a specific user?
$ curl "http://drnicwilliams.com/cgi-bin/wp_drnicwilliams.cgi?table=wp_comments&user_id=1" ...comments by Dr Nic... or $ curl "http://drnicwilliams.com/cgi-bin/wp_drnicwilliams.cgi?table=wp_comments&user_id=1&action=count" 232
Dr Nic’s commented in his own blog 232 times? Out of how many comments?
$ curl "http://drnicwilliams.com/cgi-bin/wp_drnicwilliams.cgi?table=wp_comments&action=count" 963
Where are the user emails? Where are their passwords?
Hidden! See below.
Callbacks?
JSON URLs support callback=someCallbackMethod and/or variable=someLocalVariable.
$ curl "http://drnicwilliams.com/cgi-bin/wp_drnicwilliams.cgi?table=wp_comments&action=count&callback=someMethod" someMethod(963);
What does wp_drnicwilliams.cgi look like?
Something a little like this…
#!/usr/local/bin/ruby
require 'magic_cgi' # loads the render magic, model magic, and meta-model magic
include Render
require 'magic_cgi/config/wordpress' # connect to DB using Wordpress installation (wp-config.php)
MagicCGI::Config::Wordpress.establish_connection "/path/to/drnicwilliams/web/public"
# The following is defaulted for Wordpress connections:
MagicCGI::Config.hidden_tables |= %w[wp_openid_nonces wp_openid_associations wp_usermeta wp_tla_rss_map wp_tla_data]
MagicCGI::Config.hidden_columns['wp_users'] = %w[user_email user_pass user_activation_key]
MagicCGI::Config.hidden_columns['wp_openid_identities'] = %w[hash]
MagicCGI::Config.hidden_columns['wp_comments'] = %w[comment_author_email comment_author_IP]
MagicCGI::Config.hidden_columns['wp_posts'] = %w[post_password]
render do |params|
data = DbTable.from_params(params)
data ||= begin
table_name = params['table'].first || ‘wp_users’
model_name = ActiveRecord::Base.class_name(table_name)
klass = MagicCGI::MagicModel.create_class(model_name, ActiveRecord::Base)
klass.find_or_count_by_params(params)
end
data
end
Can I write my own CGI scripts for my own DBs?
Sure. Its a library called MagicCGI.
What is MagicCGI?
Coming soon.
map_by_method now works with ActiveRecord associations
I was always annoyed that map_by_method was broken for ActiveRecord has_many associations. 6 mths later I finally fixed it.
That’s the magic of Open Source Software. [/end sarcasm]
So now, the following example works like it should:
$ gem install map_by_method $ console > require 'map_by_method' # stick this in your environment.rb for Rails > user = User.find_by_name "Dr Nic" > user.companies.map_by_name => ['Dr Nic Academy', 'Dr Nic Institute of Being Silly'] > user.companies.map_by_id_and_name => [[1, 'Dr Nic Academy'], [9, 'Dr Nic Institute of Being Silly']]
Recap: why use map_by_method?
Try the following example:
> user.companies.map_by_employees.flatten => list of all employees of user
Versus:
> user.companies.map { |company| company.employees}.flatten
or
> user.companies.map(&:employees).flatten
Or compare:
> user.companies.map_by_id_and_name => [[1, 'Dr Nic Academy'], [9, 'Dr Nic Institute of Being Silly']]
Versus:
> user.companies.map { |company| [company.id, company.name]}
That is, it looks and feels just like ActiveRecord’s #find method, with its find_by_first_name_and_last_name magic.
Summary
No {, }, |, &, or : required. Just clean method names.
Bonus other gem
In the spirit of ActiveRecord hacks, there is to_activerecord:
$ gem install to_activerecord $ console > require 'to_activerecord' # stick this in your environment.rb for Rails > [1,2,3].to_user => [list of User with id's 1,2,3]
To me, this suffix operator reads cleaner than the traditional:
> User.find([1,2,3])
For example, if you want to perform an operation on the list of Users:
> ids = [1,2,3] > ids.to_user.map_by_name => ['Dr Nic', 'Banjo', 'Nancy']
Versus:
> User.find(ids).map_by_name

