Posts Tagged: Ruby on Rails


9
Jul 10

Mass-assigning protected attributes in Rails

Rails has a great feature called protected attributes which you can set like this:

class User < ActiveRecord:Base
  attr_protected :account_id
end

Or you can use the inverted versions (which I prefer) where you set which attributes are accessible, like this:

attr_accessible :name, :email

What this does is that it tells Rails which attributes can be used in mass-assignment like this:

User.create(:name => "Lasse", :email => "lassebunk@gmail.com")

or what’s used more:

User.create(params[:user])

Then some malicious hacker cannot use mass-assignment to manually set the account_id.

If you try to assign to protected attributes you’ll get the following warning:

WARNING: Can't mass-assign these protected attributes: account_id

But the problem is: What if I’m no hacker, but I should have unlimited access, for example as an administrator? Then I can’t mass-assign to any protected attributes. Then I’d need to do something like this:

u = User.new(params[:user])
u.account_id = params[:user][:account_id]

Not very pretty.

What I’ve done is that I’ve created an extension to some of the methods taking mass-assignment and added an optional parameter called guard_protected_attributes which has a default value of true.

Example:

# config/initializers/unprotected_attributes.rb
class ActiveRecord::Base
  def update_attributes_with_unprotected(attributes, guard_protected_attributes = true)
    send :attributes=, attributes, guard_protected_attributes
    save
  end
  alias_method_chain :update_attributes, :unprotected
end

We can do this because the native ActiveRecord::Base#attributes= can also be called with guard_unprotected_attributes.

Now you can call update_attributes like this:

user = User.find(3)
user.update_attributes(params[:user], false)

and it will also assign the protected attributes.

I have made extensions to create, new, update_attributes, and update_attributes!.

The ugly part, however, is that to make it work, I had to copy the functions in their entirety which could potentially mean that if you use it on a Rails version other than exactly 2.3.8 it might not work. Sorry for that.

You can download the extensions here.


5
Mar 10

Outputting the flash to views and layouts

Here is a simple way of outputting the first message in the flash (because I didn’t want it to output both flash[:error] and flash[:notice] at the same time).

# in the controller
flash[:notice] = "Thanks for signing up!"

.. and…

# in the view
<% flash.detect do |type, text| %>
	<div id="flash" class="<%= type.to_s %>">
		<%= text %>
	</div>
<% end %>

The detect method will return the first key/value pair of the flash where the block doesn’t return false. However, that’s not what we’re using it for – we’re using it to make sure we only get the first element.

Hope this is useful.


25
Feb 10

Using Sinatra to serve smaller (than Rails) applications

So, maybe you’ve been coding Rails for a while and wondered – “do I really need this full stack framework to serve even small applications?”. Well, no you don’t :-)

Check out Norbauer’s DNS Tools (and on Github). It’s a small set of easy to use DNS tools. So small it would be overkill to serve it using Rails.

Norbauer’s using Sinatra to serve the app.

Start by installing the gem:

$ sudo gem install sinatra

After that it’s as simple as this:

# myapp.rb
require 'rubygems'
require 'sinatra'

get '/' do
  "Welcome to my homepage!"
end

get '/contact' do
  "My contact info is..."
end

At your terminal:

$ ruby myapp.rb
== Sinatra/0.9.4 has taken the stage on 4567 for development with backup from Mongrel

Now you can view it in your browser at http://localhost:4567 and http://localhost:4567/contact. Simple.

This is great for hosting apps from your command line (for example check out Taps) but what if you wanted to serve it like you would a Rails app?

Well, then you’d be using Rack.

Create a file named config.ru in the same folder as myapp.rb:

require 'myapp'
run Sinatra::Application

Now you can deploy your application as you would if it was written in Rails (thanks to config.ru).
If you want to check this setup at your localhost, run the following:

$ sudo gem install rack # make sure you have Rack installed
$ rackup # run the Rack application using config.ru

It’s as easy as that! Great for hosting small applications.

Also see:


18
Feb 10

Using Paperclip and jpegcam to get pictures from your webcam into Rails

Here’s how to get pictures from your webcam into Rails using the Paperclip plugin and jpegcam.

What we will do is create a Photo model and a Photos controller with actions new, upload and create to take care of the image creation.

Start by creating a new Rails application:

$ rails webcam_app
$ cd webcam_app

Create the Photo model:

$ script/generate model Photo description:string
$ rake db:migrate

Create the Photos controller:

$ script/generate controller Photos

Edit config/routes.rb to contain a photos resource:

map.resources :photos, :only => [:index, :show, :new, :create], :new => { :upload => :post }

Download jpegcam and put webcam.js in your public/javascripts folder and webcam.swf and shutter.mp3 in your public folder.

Create the layout file app/views/layouts/application.html.erb and insert the following HTML:

<html>
  <head>
    <%= javascript_include_tag :defaults %>
    <%= javascript_include_tag 'webcam' %>
  </head>
  <body>
	  <%= yield %>
  </body>
</html>

Next, create app/views/photos/new.html.erb and make a div for the webcam contents:

<div id="webcam"></div>

And the actual webcam javascript:

<script type="text/javascript">
  webcam.set_swf_url('/webcam.swf');
  webcam.set_api_url('<%= upload_new_photo_path %>');
  webcam.set_quality(90);
  webcam.set_shutter_sound(true, '/shutter.mp3');
  $('webcam').innerHTML = webcam.get_html(640, 480);
</script>

That will run the actual webcam so you can see yourself :) But for taking a picture, we’ll need to add the following button:

<input type="button" value="Take picture" onclick="webcam.snap();" />

Now when you click the button, the webcam image will be posted to /photos/new/upload. Try it out:

$ script/server

and go to http://localhost:3000/photos/new.

Let’s add some code for handling the upload.

The webcam.swf just uploads a bunch of jpeg data so we’ll need to get a hold of the raw post data. That’s done with Rails’ request.raw_post. In your PhotosController:

def upload
  File.open(upload_path, 'w') do |f|
    f.write request.raw_post
  end
  render :text => "ok"
end

private

def upload_path # is used in upload and create
  file_name = session[:session_id].to_s + '.jpg'
  File.join(RAILS_ROOT, 'public', 'uploads', file_name)
end

Remember to create the public/uploads folder.

Now, back to app/views/photos/new.html.erb. Create the following at the bottom of the view:

<% form_for Photo.new, :html => { :style => "display: none;" } do |f| %>
  <p>
    <%= f.label :description %><br />
    <%= f.text_field :description %>
  </p>
  <p>
    <%= f.submit "Save the photo" %>
    or <%= link_to "Take another", new_photo_path %>
  </p>
<% end %>

In your javascript just below the webcam div, insert the following function:

function upload_complete(msg) {
  if (msg == 'ok') {
    $('new_photo').show();
    $('photo_description').focus();
  } else {
    alert('An error occured');
    webcam.reset();
  }
}

And add the following webcam code:

webcam.set_hook('onComplete', 'upload_complete');

Now when you take a picture you will get a new photo form for entering a description. If something goes wrong, the webcam will be reset so you can try again. This might also be a good time to check your logs.

Now we have the upload (see for yourself in your public/uploads folder) but we still need to add Paperclip to the model.
Start by installing the plugin:

$ script/plugin install git://github.com/thoughtbot/paperclip.git

In the Photo model:

class Photo < ActiveRecord::Base
  has_attached_file :image, :styles => { :medium => "300x300>", :thumb => "100x100>" }
end

For this to work, we need to add some necessary Paperclip columns to our Photo model:

$ script/generate paperclip photo image
$ rake db:migrate

Now we can show photos like this:

<%= image_tag photo.image.url(:thumb) %>

We just need to save the photos first. Add the following to your PhotosController somewhere above the private keyword:

def create
  @photo = Photo.new(params[:photo])
  @photo.image = File.new(upload_path)
  @photo.save

  redirect_to @photo
end

def show
  @photo = Photo.find(params[:id])
end

def index
  @photos = Photo.all
end

In app/views/photos/show.html.erb:

<h1>Photo</h1>
<p><%= image_tag @photo.image.url(:medium) %></p>
<p>
  <strong>Description:</strong><br />
  <%=h @photo.description %>
</p>
<p>
  <%= link_to "Take a new picture", new_photo_path %>
  | <%= link_to "See all pictures", photos_path %>
</p>

And last but not least in app/views/photos/index.html.erb:

<h1>All photos</h1>
<p>
  <% @photos.each do |photo| %>
    <%= link_to image_tag(photo.image.url(:thumb)), photo %>
  <% end %>
</p>
<p>
  <%= link_to "Take a new picture", new_photo_path %>
</p>

Now start your script/server if it isn’t already, go to http://localhost:3000/photos – aaannnd.. it works!


24
Sep 09

Three months on the Mac stack

It’s actually four months now – since May 1st – but I’ve been wanting to write this article for a month. Hence the – somewhat misleading – title.

I bought my MacBook on May 1th after viewing a screencast where a guy sets up a blog in 15 minutes using Ruby on Rails. I immediately said to my stepdad: “I’m going to buy a Mac and learn Ruby on Rails!”

And so I did – the day after, I bought a Mac.

I’ve always been a dedicated user of Microsoft’s products. First C64, then Amiga, then Mac, then PC. I liked the way everything was tested and came from one place, unlike open source. I’ve always been saying stuff like “I like to pay for my software because then I’m be sure about the quality.” – but in reality, everyone who uses Windows and other Microsoft products know that this isn’t always the case.

So I bought the Mac, and the first thing that surprised me, was how much of day-to-day work was done in the Terminal, or command line. When I installed Ruby on Rails, it was via command line; when I installed plugins, it was via command line. Evererything command line. :-)

Over the next few days I began to get a hang of it. I bought a couple of domain names and signed up for a slice at Slicehost – because I had read some job description that said that you should’ve at least tried to use a whole day of setting a slice.

Coming from Windows, Linux is a whole other deal to setup. I used a lot of the Slicehost articles that guides you through the whole process from setup and security to getting your slice to run as a webserver.

In the beginning I was a little nervous about all the command lines, if they would really work and so one. But the more you try it, the more calm you get. It just works! And lucky me there was a lot of helpful articles about Unix and Linux commands out there (just search for the command on Google).

Since starting out on the Mac, I’ve learned a multitude of things:

In short I’ve learned so much about the open source world that just wasn’t that easy on the Microsoft platform.

I still use Microsoft Windows and other products, but now it’s through VMware Fusion on the Mac.

I’m happy about the Mac because OS X is very unobtrusive, fast operating system and what it does, it does very good. But at the same time, I also want a netbook that’s easier to carry, so I may end up running both systems for different purposes (unless I’m just installing Ubuntu on the netbook too ;-)).

Hope you found this post interesting – I wrote it to tell about a beautiful (yak, I know) progress from Windows to Mac and Linux. Thumbs up if this has made you want to try it too. And please tell me in the comments. :-)


18
Jul 09

Deploying gems using Capistrano

Thought I’d share this because I had a lot of trouble figuring out how and where to run the “rake gems:install” when deploying with Capistrano. I wanted it to automatically deploy the gems when I call “cap deploy:migrations” – this is done by installing the gems right after update_code, like this:

# config/deploy.rb
after "deploy:update_code", "gems:install"

namespace :gems do
  desc "Install gems"
  task :install, :roles => :app do
    run "cd #{current_release} && #{sudo} rake gems:install"
  end
end

Some of the examples I’ve seen use “cd #{current_path}” but I don’t think this would work as the “current” symlink hasn’t been updated at the time the “update_code” task is run. That’s why I use “current_release”.

Also others use “rake RAILS_ENV=production gems:install” but I can’t see why you should have to use RAILS_ENV here. If I’m wrong, please correct me.

Some of the links I found when trying to get this to work:


Fork me on GitHub