How to find a random ActiveRecord object
July 23rd, 2010Station.find(:first, :offset => (rand Station.count))
Station.find(:first, :offset => (rand Station.count))
I don’t know why the code snippets out there for turning ruby strings into Title Case are so complicated. Here’s one I hacked out based off of Justin French’s string extensions.
It’s probably slower than the others, or missing some exceptions, but it’s good enough for my current purpose.
class String def titlecase downcase.split.map {|w| capitalization_exceptions.include?(w) ? w : w.capitalize}.join(" ").upfirst end def upfirst self[0,1].capitalize + self[1,length-1] end private def capitalization_exceptions [ 'of','a','the','and','an','or','nor','but','if','then','else','when','up','at','from','by','on', 'off','for','in','out','over','to' ] end end
>> Dec[4] - 14.weeks => Sat, 28 Aug 2010
This took a little while to figure out, and existing documentation wasn’t super helpful for me, so here are some notes.
First off, you create new postgres users with the createuser command, but every time I ran it I was getting an error like this:
createuser: could not connect to database postgres: could not connect to server: No such file or directory Is the server running locally and accepting connections on Unix domain socket "/var/run/postgresql/.s.PGSQL.5432"?
What got me over that hump was installing postgresql-8.3 instead of 8.4, which is the default you get on Ubuntu when you install the postgres package. This is what I wanted anyway, since Heroku (my host) uses 8.3:
sudo /etc/init.d/postgresql-8.4 stop sudo apt-get remove postgresql sudo apt-get install postgresql-8.3
If you want to check if your postgresql server is running, run ps ax | grep post. You should get something like this:
4417 ? S 0:00 /usr/lib/postgresql/8.3/bin/postgres -D /var/lib/postgresql/8.3/main -c config_file=/etc/postgresql/8.3/main/postgresql.conf 4420 ? Ss 0:00 postgres: writer process 4421 ? Ss 0:00 postgres: wal writer process 4422 ? Ss 0:00 postgres: autovacuum launcher process 4423 ? Ss 0:00 postgres: stats collector process 4482 pts/4 D+ 0:00 grep post
Now you can create your user:
sudo su postgres createuser -A -d --pwprompt the_username
And then you should be able to create your databases with rake db:create.
If you are getting errors like this:
rake db:create (in /path/to/your/app) FATAL: Ident authentication failed for user "your_username" /usr/lib/ruby/gems/1.8/gems/activerecord-2.3.5/lib/active_record/connection_adapters/postgresql_adapter.rb:968:in `initialize' /usr/lib/ruby/gems/1.8/gems/activerecord-2.3.5/lib/active_record/connection_adapters/postgresql_adapter.rb:968:in `connect' ...
… it is because Postgres expects there will be a UNIX user that corresponds to each postgres user. When it talks about “Ident” it’s talking about the UNIX authentication system. You could just create a UNIX user with the same credentials as your postgres user, but I took a different route.
As suggested by Felix Sun, I edited my pg_hba.conf (which on Ubuntu is in /etc/postgresql/8.3/main). I didn’t do exactly what he did though, I just added another line as follows:
local all postgres ident sameuser local all all md5
As far as I understand, that just means that any user connecting locally on any database is OK as long as they authenticate with a password.
I restarted postgres (/etc/init.d/postgresql-8.3 restart) and ran my rake db:create; rake db:migrate and I was in business.
I don’t know how many people subscribe to this blog, since it’s basically just a dump of random snippets of code. But if you do and you’re curious what I’ve been referring whenever I’ve mentioned “my site” over the last year, I just launched it a couple of weeks ago, so you all can see it.
It’s called SproutRobot, and it tells you when to plant your vegetable garden and sends you seeds! It’s still quite a mess, but it does something useful, and it will hopefully only get better. At some point you just have to take the plunge.
Look forward to future posts about optimizing activerecord-driven sites for speed, I’m sure. ![]()
This is pretty cool. Say you have a model with a has_many relation, and you want to edit the model and all it’s children on one page. Rails can take care of all of this, automatically, with the standard controller. You just need this in your parent model:
class User < ActiveRecord::Base has_many :posts accepts_nested_attributes_for :posts end
And you need a form like this:
<% form_for(@user) do |f| %>
<%= f.label_for :blog_name %>
<%= f.text_field :blog_name %>
<% f.fields_for :posts do |p| %>
<%= p.label_for :title %>
<%= p.text_field :title %>
...
<% end %>
<%= f.submit %>
<% end %>And that’s it! You can use all the craziness that the form helpers do (grouping radio buttons, converting to values booleans, etc) and it all works out.
It’s exciting. I feel like I’m finally getting into the really good stuff with Rails.
I’ve been using .png images on my site, which are great for putting on top of gradient backgrounds. But unfortunately IE6 just displays a flat background for them.
Probably the best thing to do would be to switch to transparent .gif images with carefully chosen background colors that will blend reasonably on the anticipated background color. But that’s a lot of work.
Instead, I’m just setting the background color in the .pngs to something close to the gradient background. You can still see the box, but whatever. If you’re using IE6 you obviously don’t care if the web sites you visit look funky.
The way to do this is just to create an extra layer in The GIMP below your other layers, fill it with the background color you want in IE6, and then set the transparency to 0%. Won’t affect modern browsers.
I’m embarking on the painful journey of making my site passable usable in Internet Explorer. Not fun.
First bizarre bug: sometimes text is simple not displayed, until you select it and then deselect it. And then it’s there. Nice work, Microsoft.
I was able to fix it by changing the nesting of my divs and form elements. I changed this:
<div> <form> ... </form> </div>
… to this:
<form> <div> ... </div> </form>
Lots to look forward to.
Failed search terms: ie6 text hidden/invisible unless I select it, deselect
This one took way longer than I would’ve expected. I just wanted to replace those ugly “Internal Server Error” pages with nice ones that include my feedback form.
The easy way to do this in Rails is just to replace the files in /public (404.html, etc) with nicer ones. But I wanted to use my existing templates and forms and such, so I needed to dig a little deeper.
There’s a ton of stuff all over the place on the web about this, and I tried a bunch of stuff to no avail. But what ended up working well for me is basically doing what Tanel Suurhans suggests.
The one thing that is messed up about those instructions is that you need to set config.action_controller.consider_all_requests_local to FALSE in your development.rb for testing, and then back to true when you’re done messing around with the error pages. Tanel has it backwards I think.
The other thing is, if you’re using Exceptional instead of HopToad, your render_error method needs to look more like this:
def render_error(exception) log_error(exception) Exceptional::handle(exception) render :template => "/error/500.html.erb", :status => 500 end
In my application, I wanted to redirect my users from my domain to https://secure.mydomain.com for checkout, so their credit card info would be transmitted securely.
Unfortunately, if I just do a simple redirect, the user gets logged out, because Authlogic (and maybe the cookies themselves?) only work on a per-domain basis.
There are various ways to do this, including having Authlogic authenticate for your whole domain, and not just the one subdomain, but the solution I chose is to use Authlogic’s single use tokens. This just means, I get a token that can sign the user in one time, and I pass the user with that token to my secure domain.
Here’s the relevant bits of my controller:
class CheckoutController < ApplicationController before_filter :require_user, :except => [:with_authentication] def index if RAILS_ENV == 'production' && !request.ssl? domain = "https://secure.mydomain.com" token = Authlogic::Random.friendly_token current_user.update_attributes(:single_access_token => token) redirect_to "#{domain}/checkout/with_authentication?user_credentials=#{token}" else end def with_authentication user = User.find_by_single_access_token(params[:user_credentials]) session = UserSession.new(user) session.save redirect_to '/checkout' end end
I also had to add the single_use_token field to my users table:
script/generate migration add_single_access_token_to_users single_access_token:string rake db:migrate
What happens now is if a user goes to /checkout and they’re not using SSL, they get redirected to something like http://secure.mydomain.com/checkout/with_authentication?user_credentials=fh3f83ahfa92ha, which authenticates them and creates a new session on the secure subdomain. Then it redirects them to /checkout on that domain.
The one thing that wasn’t very well explained on The Google is how you get the single use token in the first place. The docs explain how to use it, but now how to create it. As you see above, you just have to generate a random one with Authlogic::Random.friendly_token and then stick it in your user model.
Seems to work ok.
One of the other issues is that this totally screws up your Cucumber features. Cucumber basically expects you to stay on one domain. There are some good suggestions on the Rspec mailing list for getting around this, but I ultimately chose the hacky, ugly solution of just selectively redirecting when we’re in the production environment. It didn’t seem worth it to do it the right way… especially given that the “right” ways aren’t all that much prettier.
Keywords: login to multiple subdomains, cucumber ssl redirect, https