HipHop for PHP is not PHP
February 3rd, 2010
Charles Nutter, when talking about Ruby implementation performance, said it best:
Listen, people: Ruby is hard to implement. Oh, it may look easy at a
glance, and you can probably get 70, 80, or even 90% of the way pretty
quickly. But there's some crazy stuff in that last 10% or 5% that totally
blindsides you if you're not looking for it. An early Ruby implementation
has not run that last mile of Ruby implementation, and it takes almost as
much work to get there as it does to run the first 90%.
That goes for pretty much all dynamic languages, including PHP. HipHop is fast by not implementing PHP. It's an implementation of a subset of PHP useful to Facebook. I'm not saying it's not awesome what Facebook has done, I'm just saying don't call it PHP.
Based only on the one limitation they've mentioned publicly, lack of eval(), here's a short list of PHP OSS that currently won't work correctly on HipHop:
- PEAR
- CodeIgniter
- WordPress
- Joomla
- Kohana
I wouldn't be surprised if there were additional features they've removed from PHP in HipHop, their blog post announcing it certainly leaves that possibility open.
So call it what it is, a fast PHP-like language.
The Ruby Trojan Horse: A Look Back at 2009
January 8th, 2010
I’ve been here at Tycoon for well over a year now. I remember thinking out loud about whether or not I should take this job. On that post is a thoughtful reply from the CEO which, while encouraging me to follow my heart, played a large role in my decision to join the team here.
There were some rough patches but, it’s worked out well. They have a tremendous team here and I’m proud to be a part of it. That’s not what this post is about though.
The PHP shop
They were a PHP-exclusive shop when I started. They were more than open to using Ruby though, in fact they seemed to be looking forward to it. However, after a few months of planning and discussion, when it came time to pull the trigger the same old arguments came out. Ignoring what was actually said, a lot of it really came down to conservatism and fear of the unknown. We only had one Ruby developer (me) as opposed to several PHP devs and no one wanted to effectively bet the next several months of the business’ productivity on the new guy.
This is entirely understandable and while I like to think of what could have been, it was the right decision for this company. We split up the work in a way that I still got to do most of my work in Ruby while the rest is PHP. Not a bad deal and it’s worked quite well.
Making inroads
Hey Rich, what do you use to deploy your code?
From that point forward our PHP application was deployed via Capistrano. Very little trouble, it just worked.
Next up, we needed a pretty simple stand alone background worker daemon. A bit of Ruby and the daemons and beanstalk-client gems and we had a worker that’s served us quite well for some time.
The gates swing open
I noticed the migrations in the PHP app were simply SQL files that each developer had to remember to pipe into mysql. So, I wrote a little script, migrate.php, that ran those files for you and kept a schema_versions table maintained with what migrations it had run. This worked great but, honestly, I was a little taken aback by the response:
Can’t we just use Rails’ migrations?
Absolutely. So, I hacked up the smallest possible rails app that still supported migrations and Lou Kosak used some his new found Ruby ability to pare that down quite a bit further by monkey patching Rails.
Now our PHP/CodeIgniter application has it’s database schema managed by Rails migrations and everyone seems happy. We’ve discussed creating a gem for this but, I expect this will be quite a bit easier in the more modular Rails 3.
Next up, we switched from Subversion to Git like the good Rails CodeIgniter application we are. One problem, we used svn:externals pretty heavily and Git submodules aren’t a direct equivalent and feel a bit funky. Lou had an answer in the form of a couple of Rake tasks.
The next big step, that we’re still getting working, is testing. This too, will use Ruby and there’s no wringing of hands.
Every developer here seems genuinely excited about Ruby now. I’m excited to see them learning what I was, admittedly poorly, trying to explain all those months ago. I’m looking forward to a Ruby filled 2010 here at Tycoon.
CI-Events: A Code Igniter event library
January 2nd, 2010
At Tycoon Research we have a significant investment in both Code Igniter and Ruby on Rails. Frequently the differences in methodologies between the two is striking.
We ended up writing quite a few libraries to make CI less painful to develop with while fitting into the CI way of doing things. The CI-Events library is one of those libraries.
Why do you need an event library?
We were running into situations where several parts of our system needed to deal with certain things happening such as user registration.
In that case we were ending up with a Users#create method calling out to many, many other models and libraries or, even worse, handling things unrelated to a User. Over time this can get quite sloppy, bug ridden and difficult to maintain.
An event library allowed for proper separation of concerns resulting in simpler, easier to read code.
Why CI-Events?
We had four main goals:
- Don’t introduce a lot of additional overhead.
- Flexible enough to handle multiple usage styles.
- Fit well into the CodeIgniter way of doing things.
- Considering the indirection involved, attempt to keep things readable.
Admittedly, two of the four are subjective but, with the criteria in mind the other event libraries we looked at failed on one of more of these points. Most significantly, there was no single way of knowing what events were handled by what code.
Another, more minor issue, was the introduction of a new kind of handler file into CI. We felt that was unnecessary.
Usage
The library stores it’s event configuration in a configuration file: config/events.php. The empty config/events.php in the repo contains detailed documentation but, here’s some examples:
For each event there’s an array of handlers. A handler consists of the CI object type, the name of the object, the name of the method and an optional forth argument that may be anything you like.
This approach gives you a few things:
- One point of reference for all the events in your system.
- Delegation of event handling to existing libraries and models where appropriate.
- Reuse of a single handler by modifying the handler’s behavior based on the forth argument.
Sending events is very simple:
The first argument is simply the matching event from your config/events.php. The second argument, like the forth argument in the config file, is entirely optional and may be whatever you’d like it to be. We’ve largely stuck to passing associative arrays through the second argument and been very happy with the result.
Finally, the actual handler:
It’s important to note that all three arguments are optional on the handler methods. If you accept no arguments, it’ll call your method without any arguments. Accept one and you’ll get just the second argument passed to the ->send call. The second argument, when present, will receive the forth argument from the config file. The third and final argument will be the name of the event such as profile.created.
This library has served us very well, in production, for a few months now. Feel free to use it and contribute back if you feel it’s missing something.
Update
I’ve pushed a very simple demonstration application to github. You can find it at http://github.com/rich/ci-events-demo-app. The handler library is simply called Thing and you can see it in system/application/libraries/Thing.php. The event is sent by the default welcome controller. The output looks like this:
Escaping the hell of PHP testing using Ruby
January 1st, 2010
First, some background. Feel free to skip to the next header if you just want the details.
At work, we’re basically a 50/50 environment split between Ruby and PHP. In the last several months there’s been an effort to write more tests for the PHP side of things. We’ve been investigating various PHP testing tools and came to an inescapable conclusion: testing a web app using PHP is not pleasant (I’m being nice here).
I come from the point of view that if writing tests is unpleasant, no one will want to do it. The rest of the team is in agreement so, we had to come up with a different approach.
For some time I was honestly stumped trying to find that silver bullet solution. My answer came at RubyConf talking to Stephen Caudill and Corey Haines. Put simply, doing comprehensive integration testing was better than no testing and I should use Ruby to do that testing.
That was a revelation to me. I hadn’t considered just doing integration test but, during my indecision, we weren’t doing any meaningful testing. So, getting integration testing going and figuring the other layers later, sounded like a pragmatic approach.
Why a new library?
Yes, you can configure webrat to just hit a local webserver and that was my initial approach. This ended up requiring a fair bit of thought and configuration though. This was making testing painful again. I wanted to get to the point that people could checkout the code and not do much more than a handful of commands followed by rake spec.
No webserver or it’s configuration. No complicated webrat or rspec configuration. No test database management nightmares. No nothing. Just simple Rails-style testing.
gem install messiah
Messiah is my attempt at just that. It’s still a very young project and doesn’t hit all those goals yet.
It does however make for a decent first run experience even right now. Messiah itself primarily runs tests that hit php using itself so, it makes for a good demo:
You can see the actual spec file here and the php code it’s executing here.
The only real configuration, in spec_helper.rb, is pretty trivial:
“How’s it work?” or “I love Rack”
The most important part of messiah is a relatively simple idea. I wrote a Rack application that acts as a wrapper for CGI applications. So, it can actually test any CGI app not just PHP apps.
Besides the Rack app, it’s support code for configuring the environment for you at runtime. In the long run, that will be the more important part of messiah.
Where to next?
There are two big things I’m planning on adding in the next several days. First, a set of generators (thanks templater) for adding RSpec, Cucumber and others (preconfigured for messiah of course) to a codebase.
Second, I want to have first class database support. Coming up with a workable solution was surprisingly challenging. You need to consider connecting to the right database, keeping it in a sane state and easy way to work with known data.
My current approach based on database_cleaner, Dr. Nic’s Magic Models and Factory Girl seems to be working out nicely. This is not in the repo yet but, I do hope to push it soon.
How about everyone else out there? Any ideas?
acts_as_revisable 1.1.1 - now with STI love and more
September 19th, 2009
I’ve been buried in launch prep for work and really needed a break. Not from coding, just from work. So, I went through and pulled in several changes from the friendly strangers on GitHub and corrected the issues listed in all the GitHub tickets.
Single table inheritance
acts_as_revisable was previously non-functional on STI models. Now it works nicely. I do suspect there may be a few more issues lurking with this but, in my testing I didn’t run across anything.
Note: It does require explicit use of the acts_as_(revisable|revision) calls in all the subclasses.
Thanks Diego Scataglini for the report.
validates_uniqueness_of
This issue was reported in detail by Chris Blunt. Basically, for the revisable and revision models I use Rails’ default_scope functionality. validates_uniqueness_of ignores the default scope.
acts_as_revisable now patches validates_uniqueness_of by default while allowing you to disable this functionality with a simple flag.
Thanks Chris.
Miscellaneous bug fixes
There were a bunch of little bug fixes from a few users on GitHub. These include:
- proper time zone support
- added and better specs describing revision number behavior
- corrected a bug with revision numbering
- support #find_revision(:last)
Thank you moklett, martinh and aguynamedryan.
Exploring PostgreSQL 8.4 - Recursive Queries
July 20th, 2009
This topic has honestly been covered really well by the various PostgreSQL-covered bloggers. I’m simply hoping to expose my handful of friends who read this blog to some of the new beauty of 8.4.
Recursive queries in PostgreSQL are part of Common Table Expressions or CTEs. They’re kinda like views defined on demand as part of a query.
Jumping right into code
I think the perfect example for recursive queries is a tree structure such as categories. So that’s what we’ll build:
Now, if we want to select all categories that are children of the ‘three’ category, we can with one recursive query:
Let’s go through that line by line.
- Line 1: we create a recursive CTE called all_categories
- Line 2: we define the first query executed which selects the initial data
- Line 3: for a recursive query, PostgreSQL requires a UNION in the CTE. This UNIONs the initial data with the data collected through recursion.
- Line 4: defines the query that actually does the recursion. This query joins the categories table with the all_categories CTE.
- Line 5: query the CTE we just defined.
And the results of the single query:
id | parent_id | name
—-+———–+——-
7 | 3 | no
8 | 3 | yes
9 | 3 | maybe
10 | 8 | good
11 | 8 | bad
12 | 8 | evil
(6 rows)
I generally find this far more palatable than the various other solutions I’ve seen that involved either mountains of queries, mangling the schema or both.
Quick Hack: Nokogiri backed Hash-ish class
July 20th, 2009
Many months ago I was working on some code that needed to handle small bits of XML. For the sake of expediency I went with using Rails’ Hash.from_xml method. This worked great for quite a while.
Eventually, the size of the XML I was handling got quite a bit bigger. Now, I’m dealing with several megabytes of XML in each file. With this much data Hash.from_xml is painfully slow. Maxing out the CPU for several minutes slow.
So, I spent a few minutes on Friday afternoon and hacked together a class that acts like the data structure that Hash.from_xml returns. However, instead of building the entire data structure up front, it has some Hash and Array methods that call Nokogiri methods on demand. That said, even parsing the entire document is an order of magnitude faster.
user system total real
Hash.from_xml 663.820000 3.220000 667.040000 (675.076590)
NokoHash 22.410000 0.190000 22.600000 ( 22.771999)
I also threw in some #method_missing magic to allow for method-call style access of the structure instead of just hash/key access.
Warning
This code is only tested as far as my specific needs for this specific system. I have precisely zero confidence that this will work smoothly for you. I just found this to be an interesting bit of code, I hope you do too.
Simple association versioning with acts_as_revisable
May 12th, 2009
One of the reasons AAR was created was due to the hoops that we had to jump through to effectively version across associations while taking into account our business logic. We ended up with hundreds of lines of very complicated spaghetti code. It wasn't pretty.
Here I'm going to demonstrate how you can very simply version two models together.
We'll provide for Posts which can have multiple Links. Any time a Post is created or modified, a revision of it and it's associated links at the time will be stored. Adding a link to a Post will also trigger a revision.
Changing a link will not trigger a revision of the Post. This is trivial to add and should be considered a task for you (if you want to play with AAR).
Model walk-through
You can grab the complete code for this any time at GitHub but, I'm going to walk through the models now.
post.rb
class Post < ActiveRecord::Base
acts_as_revisable
has_many :links, :before_add => :revise!
after_revise :revise_links!
private
def revise_links!
self.links.each(&:revise!)
end
end
So first, we're defining our Post, making it revisable and associating it to it's links. We've also added two callbacks.
The first, on the links association, causes the post to be revised before a new link is added. This allows us to grab the state of the links before it changes. So we'll end up with an accurate snapshot of the post.
The second callback is the after_revise callback which simply loops through the links, forcing a revision on them. This allows us to peg a particular post revision with a particular link revision.
Notice there's really no magic here, we're just using the tools acts_as_revisable gives us.
link.rb
class Link < ActiveRecord::Base
acts_as_revisable
belongs_to :post
end
Simply declaring our Link, making it revisable and associating it with a post. Nothing special here.
post_revision.rb
class PostRevision < ActiveRecord::Base
acts_as_revision
has_many :links, :class_name => "LinkRevision", :foreign_key => "post_id"
end
Now we're explicitly defining our PostRevision class and letting AAR know. Defining the association requires a few more options just to override some of ActiveRecord's defaults. Notice PostRevisions will always be associated with LinkRevisions.
link_revision.rb
class LinkRevision < ActiveRecord::Base
acts_as_revision
belongs_to :post, :class_name => "PostRevision"
before_create :reassociate_with_post, :if => :post_in_revision?
private
def reassociate_with_post
self.post = self.current_revision.post.find_revision(:previous)
end
def post_in_revision?
self.current_revision.post.in_revision?
end
end
Here's the most complicated part and even this isn't so bad. We're explicitly defining our LinkRevision and it's association.
Next, we have a before_create callback. Keep in mind, this is the revision model so, before_create in this case is roughly equivalent to before_revise on the Link model. I've defined it this way for the sake of clarity. The work in this callback really concerns the revisions not the revisable. So, while it could be defined in either model, it makes the most sense when reading the code for it to be here.
The reassociate_with_post method being called by the callback is relatively easy to understand if you keep the big picture in mind. A post's links are being revised after the post so, this callback is called after the post has been revised. So, we're simply grabbing the last revision of this post and associating the LinkRevision with that.
Let's see it in action
>> p = Post.create(:title => "Simple association versioning with acts_as_revisable")
=> #<Post id: 55, title: "Simple association versioning with acts_as_revisabl...">
>> p.links.create(:url => "http://github.com/rich/aar-demo-1/tree/master")
=> #<Link id: 92, url: "http://github.com/rich/aar-demo-1/tree/master">
>> p.revisions.size
=> 1
>> p.revisions.first.links
=> []
>> p.links
=> [#<Link id: 92, url: "http://github.com/rich/aar-demo-1/tree/master">]
>> p.title = "That was easy"
=> "That was easy"
>> p.save
=> true
>> p.revisions.map {|r| r.links.size}
=> [1, 0]
>> p.revisions.first.links.first
=> #<LinkRevision id: 93, url: "http://github.com/rich/aar-demo-1/tree/master">
>> p.links.create(:url => "http://github.com/rich")
=> #<Link id: 95, url: "http://github.com/rich">
>> p.revisions.map {|r| r.links.size}
=> [1, 1, 0]
>> p.links.size
=> 2
>> p.links.create(:url => "http://github.com/technoweenie/acts_as_versioned/tree/master")
=> #<Link id: 98, url: "http://github.com/technoweenie/acts_as_versioned/tr...">
>> p.links.size
=> 3
>> p.revisions.map {|r| r.links.size}
=> [2, 1, 1, 0]
I have a couple ideas in progress for a followup post but, I'd love some requests if there's interest.
Remember, you can grab the code on GitHub.
Hoptoad. Goes great with PHP too.
April 9th, 2009
Over the years of Rails work I’ve been using a few services that I consider indispensable now. Hoptoad is one of those tools. It beats the hell out of pretty much every cobbled together error notifier I’ve seen. I can’t see launching an application without it.
The Horror
Sometimes, I need to work with PHP. It’s not pleasant but, there it is. One of the things I did to make this a bit more palatable is hack together a Hoptoad notifier for PHP. Recently, with some help from Lou Kosak, I’ve cleaned the code up a bit and think it might be useful in other projects now.
Grab it
You can find the code at http://github.com/rich/php-hoptoad-notifier. Just drop the Hoptoad.php file somewhere in your include_path and include the following code somewhere early in your application:
require_once('Hoptoad.php');
Hoptoad::installHandlers("YOUR_HOPTOAD_API_KEY");
Flexible ActiveRecord Versioning with ActsAsRevisable
April 2nd, 2009
I’ve been working on this on and off for the better part of the last year. Life has intruded many times but, I’ve finally got it to a point that I’m comfortable releasing as a 1.0.
This plugin grew out of Stephen Caudill and my use of Rick Olsen’s ActsAsVersioned. The application was centered around versioning of our user’s resources. Early on, AAV worked great for us but, as our requirements got more complicated we found ourselves hacking AAV more and more.
Eventually Stephen came to the conclusion that we needed to break from AAV and roll our own. So he laid out the requirements and I built it. I’m going to talk about all of AAR’s features through a series of posts but, I thought I’d quickly highlight some of it’s unique features now.
Pervasive Callbacks
AAR was created to help build applications that need precise control of the life-cycle of it’s data. In pursuit of that, there is a full suite of callbacks:
- revisable models
- before_revise
- after_revise
- before_revert
- after_revert
- before_changeset
- after_changeset
- after_branch_created
- before_revise_on_destroy
- after_revise_on_destroy
- revision models
- before_restore
- after_restore
- both revisable and revision models
- before_branch
- after_branch
AAR callbacks act just like ActiveRecord callbacks. They accept all the same arguments and they can prevent the action being taken just like AR callbacks.
Branching and Changesets
Branching is a well understood concept, no need to go in depth on this, here’s the relevant code:
@branch = @post.branch
@branch.branch_source == @post # => true
Changesets are a bit more interesting. In non-trivial code you may end up a block of code that may perform many actions that each cause a revision to be saved. If you wish to group those as a single revision, you can:
@post.changeset do
@post.title = "A post"
# save would normally trigger a revision
@post.save
# update_attribute triggers a save triggering a revision (normally)
@post.updated_attribute(:title, "Another Title")
# revise! normally forces a revision to be created
@post.revise!
end
Deletes can be stored as a revision
This is pretty simple, #destroy no longer destroys the record. It flags it as deleted. Deleted records can then be accessed through the revision class’ #deleted named_scope.
class Post < ActiveRecord::Base
acts_as_revisable :on_delete => :revise
end
@post.destroy
PostRevision.deleted # => [@post]
Explicit is better than implicit
ActsAsRevisable will not, by default, define the revision class for you. The most common configuration is as follows:
class Post < ActiveRecord::Base
acts_as_revisable
end
class PostRevision < ActiveRecord::Base
acts_as_revision
end
If you want to call your revision class something else, you can:
class Post < ActiveRecord::Base
acts_as_revisable :revision_class_name => 'OldPost'
end
class OldPost < ActiveRecord::Base
acts_as_revision :revisable_class_name => 'Post'
end
If you absolutely must have AAR generate the class for you:
class Post < ActiveRecord::Base
acts_as_revisable :generate_revision_class => true
end
All data for a model is stored in one table
There isn’t a separate versions table. This comes from the belief that all Posts (or whatever your model happens to be) are Posts whether or not they’re the current version.
This also makes it much easier to find records whether or not they’re the current version or not:
Post.find(:all, :conditions => {...}, :with_revisions => true)
Wrapping up, requirements and installing
There’s a lot of flexibility here. In the application AAR was built for we were using it to version entire trees of objects together, not just individual records. I plan on digging into this and more in future posts.
AAR 1.0 requires Rails 2.3. You can take a look at the code on GitHub.
sudo gem install rich-acts_as_revisable --source=http://gems.github.com
Once the gem is installed you’ll want to activate it in your Rails app by adding the following line to config/environment.rb:
config.gem "rich-acts_as_revisable", :lib => "acts_as_revisable", :source => "http://gems.github.com"
Chrome is Google's Big Stick
September 2nd, 2008
I’ve been playing with Google Chrome all day. I’ve used it exclusively for about eight hours now. I’m definitely impressed. It’s fast, simple and stable. The UI changes show obvious inspiration from all of the major browsers with solid usability improvements.
The concept and implementation of distinct processes for each plugin and page causes an inappropriate stirring in my loins.
I’m impressed.
I’ve seen those details covered ad-nauseam though and probably better than I ever could. Here’s a decent rundown.
Wrapping Myself in Tinfoil
I don’t think Google wants or expects to gain significant market share with Chrome. I think they’re now trying to drive innovation in the browser world by competing rather than simply consuming.
They’ve been providing carrots to the browser makers in the form of the various Google apps for years. I think they’ve been disappointed at the relatively slow and uninspired developments of IE, Firefox, Opera and Safari.
I believe this is Google’s stick. They’re going to smack the other browser developers with Chrome to drive them in the direction that they believe is most beneficial to Google.
I really don’t think I’m way off in tin-foil hat territory here but I am curious what others think.
Tough Decisions
August 28th, 2008
Clarifications
Reading what I wrote below I see where my mind was just a few days ago. I wasn’t entirely wrong to talk about where my mind was but, I did trivialize the job offer quite a bit in an effort to get people to backup the idea of consulting.
That was kinda stupid of me to do though. It actually *is* an interesting job with a company in a field I’ve wanted to work in for a long time. It’s a solid group of passionate people and anyone that knows me knows seeing passion for what you’re doing is a big deal for me. These people don’t just have passion though, they seem to have the talent to implement their vision.
So, I’m leaving the post as is for posterity’s sake but, as you read, keep in mind I don’t even agree with a fair bit of what I wrote myself.
I’ve been an out of work Ruby developer for about three weeks now. When I got word that my paychecks would stop coming I immediately reached out to some friends and colleagues for a few contracts and this is what I’ve been doing the last few weeks.
Three Weeks
In those three weeks I’ve been working on some truly interesting projects and learning a ton. I’ve been working with other people’s code that borders on pure poetry and some code that appears to be an implementation of “Brainfuck on Ruby on Rails”.
In just three weeks I’ve worked in multiple languages with several technologies I hadn’t touched before. I’ve even found and used a respectable PHP ORM.
More importantly, I’ve gotten a crash course in the contracting business from some smart people that have been doing this for years. I’m also playing “Do The Hustle” on a perpetual loop, thanks Obie.
Doing this for just three weeks of course means I’m still absolutely horrible at the sales process but, it’s a start.
No Plan
So far, I’m loving the contracting thing and I want desperately to make my living doing this for a while. The problem is, I didn’t plan this.
I planned on sticking with JamLab and seeing that through. I planned on a successful launch and supporting a truly awesome product. I planned on getting a steady paycheck and health insurance.
I have all the usual responsibilities of someone my age. I have a wife, a daughter, a mortgage, etc. All these people and things generally want to eat and/or get paid on a pretty regular basis.
Had I planned this and had all the research done and proof that I could handle my responsibilities consistently I wouldn’t be so nervous. I’m fine now but what about tomorrow? The day after?
Is there really that much work for a good developer but horrible sales guy?
My Dilemma
I got a job offer today. Not a great one and not a horrible one. I would still get to use Ruby, which is great, and the work looks interesting. The job does strike me as having the potential of being one of those perpetual crunch time environments though. Really, this post isn’t about this job though, others will come, it’s more about full-time v. contractor.
It would pay the bills though and, I’m assuming, on a pretty regular basis. I just honestly don’t know what to do.
Three weeks ago it was easy, I was a cog in a wheel and didn’t know what I was missing out on. Now I’ve got a taste and I do like it.
So all ten of you that read my blog, tell me what to do. Help me map out my future. Direct me on how to balance my happiness.
Or just post lolcat images, that’s fine too.
Don't use proxy_target in AR association extensions
August 21st, 2008
This is as much a note to my future self as a warning post to others doomed to make the same mistake I did. I was creating a few methods to extend a has_many association with. Based on the current Rails docs I used the proxy_target accessor to get at the records of the association.
The only problem with that is proxy_target is not populated unless the association had previously been traversed through the same model instance. After I tossed in a line of code into the method that explicitly loaded the association I decided this was a bug in Rails.
It wasn’t a bug
I started thinking about a patch but decided this couldn’t possibly have been missed that long. So instead I googled quite a bit more and eventually found “Should proxy_owner be eager loaded?”. This accurately described my issue.
The answer
Put simply, proxy_target is implied in the scope of association extensions. Here’s some of my own example code that demonstrates:
Also, if you go off the beaten path in the Rails docs a bit you’ll find this behavior is documented in ActiveRecord::Associations::AssociationProxy. This should probably be mentioned in ActiveRecord::Associations::ClassMethods where proxy_target is discussed. Maybe I’ll make my first doc patch tomorrow.
Feel free to use the comments to ridicule me.
Some Shoulda Plugin Love
August 18th, 2008
I’ve been using Shoulda for some new projects and while it’s a learning experience I’m really pleased with the results. One thing I noticed right away is that I was testing for the presence and functionality of several Rails plugins I chose that are being used on many models.
I ended up writing custom Shoulda macros for three of the plugins: ActsAsTaggableOn, ActsAsCommentable and SaltySlugs. Below are the macros and an example of their usage in a test.
Shoulda Macros
Example Usage
$ ruby -Itest test/unit/album_test.rb
Loaded suite test/unit/album_test
Started
…………………
Finished in 0.109251 seconds.
21 tests, 32 assertions, 0 failures, 0 errors
Be a Developer. Be Flexible. Be Yourself.
August 1st, 2008
Jay Fields originally got me thinking and writing the first draft of this with his “Developers needed; Hackers need not apply” post. By his definition, I’m a long time hacker. I’m in recovery now and feel like I’m far more of a developer at this point.
Confession time…
For too long I worked in very small companies where I was the only developer. A few times I was the only one with any technical experience and yeah, that was in technology startups. This led to them leaning too much on me for the overall direction of the products which in turn led me to think I’m qualified to make those decisions on my own.
If you’re a developer, that’s a path destined for failure. You’ll likely make things more complicated than they need to be which will take longer to develop. Talk to everyone you can about the project and really listen. This sounds absolutely ridiculous and basic when typed out but, some people can’t even make that step, I know I couldn’t.
Recovery
Only through time and frustration did I realize there was something wrong with my thinking. I largely credit getting more involved with the development community as a whole with my real recovery. I think I was too isolated to come to the realization on my own.
Meeting other developers who I truly respected was the real turning point. Stephen Caudill, Chris Saylor, Bruno Miranda and Ryan Leavengood to name a few. These are all incredibly talented developers with varied experiences who’ve taught me the humility all true developers need to be successful.
The Ruby community as a whole deserves credit as well. These ideals are better expressed by this community than any other I’ve seen.
To be a good developer is to be humble. To realize you alone will not make a project successful. Sheer force of will doesn’t translate into a usable interface or a solid brand.
The part where I’m a bit of a contrarian
Shortly after the “Developers” post Jay followed up with “Civics not Cadillacs.” He’s absolutely on point with this post as well but, this one is more of a high-wire act.
Let me really abuse his car metaphor a bit.
What happens when you’re told the airbags and seat-belts should be considered a version 2 feature? How about when you’re asked to propose a stopping mechanism other than brakes with “I saw a promising solution on the Flintstones once” tossed in for good measure.
I know Jay wasn’t suggesting to abandon development best practices at the drop of the hat. I do think that it needs to be clear that some things a good developer just won’t sacrifice.
It’s our jobs as developers to balance the business needs and proper development. That balancing act needs to include extensive collaboration with the rest of the business.
They probably hate you already
Anyone that knows me in person knows that I’m very opinionated. I do tend to tone it down online though. I’ve written several blog posts and chose to scrap them because I worried I would offend someone. Many times it was someone I respected.
I did this because text and my questionable writing skills just don’t convey the tongue-in-cheek nature with which I want to deliver some of my thoughts.
Then I read “Losing Twitter Followers is a Good Thing” by Steve Bristol. Yet again, it’s another simple and obvious concept; just be yourself. I wish I knew why I need other people to blog about common sense for me to finally get it.
As a developer you’re paid for your opinions. Don’t be afraid to share them. Just have the common sense to temper them with the realities of the business while maintaining your integrity.
Simple enough.