If you've got a boat load of legacy fixtures laying around (and who doesn't?) it can be a pain now that Rails handles the ID of objects so you don't have to.
On the train back from holiday I wrote a snippet of code to migrate them:
RUBY:
-
def Util.real_yml(klass,col = 'name')
-
y = YAML.load_file(File.join(RAILS_ROOT,'test','fixtures',"#{klass.to_s.tableize}.yml"))
-
real_names = y.inject([]) { |names,(key,val)| names <<y[key][col] if y[key][col] ;names }
-
real_objs = klass.find :all, :conditions => [ "#{col} IN (?)",real_names ], :order => "#{col} asc"
-
yml = ""
-
real_objs.each do |real_obj|
-
yml += "#{real_obj.name.downcase.gsub(' ','_').gsub('-','')}:\n"
-
real_obj.attributes.each do |k,v|
-
if k =~ /_id$/ && real_obj.respond_to?(k.gsub(/_id$/,'').to_sym) && ! v.nil?
-
n_klass = k.gsub(/_id$/,'').camelize.constantize.find(v) rescue nil
-
key = k.gsub(/_id$/,'')
-
val = n_klass.name.downcase.gsub(' ','_').gsub('-','')
-
yml += " #{key}: #{val}\n"
-
else
-
yml += " #{k}: #{v}\n" unless k.to_s == 'id'
-
end
-
end
-
end
-
yml
-
end
The use is:
RUBY:
-
require 'util'
-
print Util.real_yml(Corporation)
Copy/paste into corporations.yml
So far it's working great!
Lately the rails-core mail list has declined in quality. Spammers have moved in and operate unfettered. Reporting offenders to Google does nothing.
In fact, in order to isolate my personal mail from spam and to keep dspam from getting very confused I've moved the rails-core list to a google mail account. It's a shame but I'm more interested in keeping my other mail free from spam and the dspam quarantine free from ham. Isolation of rails-core until Google can get the spammers off their lists will accomplish the goal.
Yesterday we launched Mini Match, an application me and my colleagues from work wrote for Cartoon Network.
Late last year we opened a beta that was, unfortunately, short lived. Friday (May 30, 2008) we opened the system up on a much improved codebase for a few hours and had all systems "green". Based on the positive success from Friday we opened it up yesterday.
It opened slowly at first with just a small advertisement on the Cartoon Network Games' Page, and then a larger one on the same page and then we made the Cartoon Network home page with a small advertisement again. Today, I reckon, a larger advertisement will be put on the front page and we'll really start to see traffic!
Some details on the application:
- Flash/Flex/AS/Whatever front-end GUI (really, it's one of them)
- Java-based persistance server
- Rails-based funnel into the database with a bit of logic.
The Rails part is RESTful (for the most part) and is the "glue" of the application, to quote someone from IRC.
Today should be a fun day!
This coming Monday I'm flying off to Atlanta, Georgia for the week. Not keen on going as Atlanta is like descending into the armpit of summer. I'm hoping that the weather co-operates and I don't melt.
I'm flying down there for work, we're beta launching a product for a customer (that's based in Atlanta - go figure) and they (the customer) have requested a contingent of us from work be on site for the release. So it's me and two others from the UK.
We're down on Monday then flying back home on Friday evening - I'm hoping to be in bed by midnight (landing 22:05 - 30 minute taxi home, getting bags and immigration and such). A SysAdmin that works for our customer suggested Zuma sushi bar to me for sushi. I like sushi a lot and hope to stop in for some glorious sushi.
Had my first bug report today about the to_i method I made on the String class breaking Rails migrations because leading zeroes weren't being treated nicely in my method: "001".to_i # => 1 and fails the test 1.to_s == "001" and so a String was being returned fromto_i. Crap.
I don't think that I need the method anyways (I'm going to spend part of today to make sure with more tests!) so I've removed it and tagged Release 80 and uploaded the gem to RubyForge. It should be available to gem update or gem install shortly.
This release also has a larger test coverage and some other unnecessary methods were pruned. Check it out!
First the problem:
I'm writing a load test framework at work and I need to consume JSON webservice and pass on the output to another request. But I don't always know what the data is that I need to pass on but I do know the basic "path" ("hpath" -- "hash path") to get to the data as the JSON data is uniform.
Now the method:
RUBY:
-
# Picks out data from a (JSON) decoded hash based on the @passon hash,
-
# which looks like this:
-
# { "id" => "packet.products[0].attributes.id",
-
# "quantity" => "packet.products[0].attributes.quantity"
-
# }
-
# The "id" and "quantity" are the new keys for the return data;
-
# packet.products[0].attributes.id will look at the value of the id key
-
# in the attributes hash in the 0th element of the products array in the
-
# packet hash.
-
def pick_out_passon(hash_data)
-
return {} unless @passon
-
returnhash = {}
-
nh = hash_data.dup
-
@passon.each do |newkey,part_str|
-
parts = part_str.split(".").reverse
-
while (part = parts.pop) do
-
m = part.match(/\[(\d+)\]/)
-
index = nil
-
if m
-
index = m[1].to_i
-
part.gsub!(/\[#{m[0]}\]/,'')
-
end
-
nh = nh.values_at(part).first
-
nh = nh.at(index) if index
-
end
-
returnhash.merge!({ newkey=> nh})
-
nh = hash_data.dup
-
end
-
returnhash
-
end
What's it do? It does magic!
I'll step through it...
Looks for @passon instance variable and doesn't do anything useful unless it exists.
Duplicate the input hash because the process done is destructive to it and we may need to reuse it.
For each new hash key and "hpath" pair from @passon split up the hpath into its parts and reverse it so Array#pop will work in the while loop to get the next first part to try.
Since each part of the hpath can examine an Array by index it has to be checked for and the index removed from the part (and saved).
Next, investigate the copy of the hash_data, nh by the computed key; if the value in the hash was an Array use the index to get the desired value. Then compute the next part!
Once we're out of parts stuff the new key and data into the returnhash and keep going til there's no more @passon pairs.
And thus some fun code was written.
Reve release 61 is full of changes and new features. Highlighting them is Reve now has a Trac and its home page here on my blog will eventually be migrated over to the Trac including code samples and more comprehensive documentation.
A brief list of what's changed is:
- Corporations now nest under the Reve::API#alliances call and their ID can be passed to Reve::API#corporation_sheet to get more info from about the Corporation.
- Added an init.rb so people can install Reve as a plugin to a Rails application (Saves having to put it in lib and constantly update it. See RailsPlugin on the trac for details. Thanks to James Harrison for the idea.
- Reading of XML from an arbitrary source for all API calls. Including a file on disk or any other website. Useful for integrating with a caching proxy.
- Saving of XML to disk after a successfully completed API call. Useful to report bugs and to cache things locally. See the previous point!
The RDOC for Reve has been updated as well.
Download:
In reference to the post about My First Patch to the Rails trac I mentioned in the edit of the post that I emailed the maintainer. There's been no response, unfortunately.
So I'll post the patch here so it'll get a bit more visibility and hopefully Oracle users can find it helpful in speeding up rake db:migrate on Oracle systems with a lot of indicies.
Patch to speed up Rails’s OracleAdapter#indexes method
With Rails there's a useful set of callback methods that can be used within model classes. They include after_create, before_validation and the one I want to talk about after_initialize.
A lot of cool things can be done with after_initialize, especially if one uses the database as a means to store meta-data about a model. Use the after_initialize method to transform your models with Ruby after they're fethced from the database. Consider the following User model definition:
RUBY:
-
class User <ActiveRecord::Base
-
has_many :memberships, :order => "memberships.group_id, memberships.expires_at desc", :group => "group_id"
-
end
It's pretty clear that with the :group => "group_id" that I want a unique set of Membership objects. That works well when I've already got a User and do user.memberships but not at all when I do User.find(:first, :include => :memberships); likely because of the way the join is set up and grouping on that may not be possible.
I still wanted to use eager loading so I thought that it would be an OK sacrifice to fetch all the Membership objects even if I wanted to pare it down after I initialize the User object (with eager loading). I defined:
RUBY:
-
class User <ActiveRecord::Base
-
-
def after_initialize
-
do stuff with @memberships instance varible
-
end
-
end
Except it doesn't work. Quickly I found the following from active_record/base.rb (click link for source): right where it does the after_initialize callback! But it wasn't working for me. I needed to dig further.
Further research indicated that before the creation of the @memberships instance variable that would hold the collection of Membership objects the after_initialize callback was being fired off in the User model. Using after_initialize would not work due to the design of ActiveRecord. I'm still searching for an elegant way to work the way I want. I'll post again when I find it!
I've submitted my first patch to the Rails trac today to speed up the OracleAdapter#indexes method (used in migrations). A client at work uses Oracle and in the production environment this method was taking upwards of 45 seconds to run per table!One of their DBAs gave me the SQL there, so I can't take the credit, I just submitted it.
I hope it's accepted. Incidentally, it works and has saved me a lot of time with migrations.
Edit: Yeah, turns out that since turning these adapters into gems patches for them shouldn't be done through Rails trac. How irritating, but whatever. I've sent the patch do the current maintainer for review.