Rails TimeRange

1 Comment

Say you want to query your backend objects with a range of times.

One way to do that is to define a TimeRange object which captures the time ranges you are interested in:

TimeRange.rb

class TimeRange
  def self.today
    (Time.now.midnight)..Time.now.end_of_day
  end

  def self.yesterday
    (Time.now.midnight - 1.day)..Time.now.end_of_day
  end

  def self.last_week
    (Time.now.prev_week)..Time.now.end_of_day
  end

  def self.ages_ago
    (Time.now.prev_year)..Time.now.end_of_day
  end
end

And then using these to define your ‘created_at’ parameter:

SomeController.rb

    case params[:date_posted]
    when 'today'
      created_at = TimeRange.today
    when 'yesterday'
      created_at = (Time.now.midnight - 1.day)..Time.now.end_of_day
    when 'last week'
      created_at = (Time.now.prev_week)..Time.now.end_of_day
    when 'ages ago'
      created_at = (Time.now.prev_year)..Time.now.end_of_day
    else
      logger.error "Unknown posted date on listings search #{params[:date_posted]}"
    end

which can then be used in your search:

    @attr = {
            :location => @location,
            :category_id => params[:category_id],
            :created_at => created_at
    }

    @listings = Listing.where(@attr)

Not bad!

See my previous post here for where to place your TimeRange object and how to reference it from another ruby class.

Advertisements

How to reference domain classes in rails

1 Comment

Working with active records in rails is nice because a lot of your domain is directly represented in your model classes.

Sometimes however, you still need to create some domain classes that aren’t tied to a database model, and reference them in your app.

I’m not sure what the best way (or convention) is for doing this, but what’s been working for me is to create a separate directory called domain

and place my domain object(s) in there:

class TimeRange
  def self.today
    (Time.now.midnight)..Time.now.end_of_day
  end

  def self.yesterday
    (Time.now.midnight - 1.day)..Time.now.end_of_day
  end

  def self.last_week
    (Time.now.prev_week)..Time.now.end_of_day
  end

  def self.ages_ago
    (Time.now.prev_year)..Time.now.end_of_day
  end
end

And then referencing them where I need them using the ‘require_relative’ command.

require_relative '../domain/time_range'
require_relative '../domain/constants'

class CitiesController < ApplicationController

How to drop, create, populate, and reset your rails database

Leave a comment

Getting frustrated with all the commands I needed to type I created the following scripts and tools to simplify destroying and recreating my rails database.

Step 1: Create an ‘all’ task.

lib/tasks/db.rake

namespace :db do

  task :all => [:environment, :drop, :create, :migrate, :populate] do
  end

end

The basic commands for recreating and populating your database from the command line often require some variation of:

Rebuild db
$ rake db:drop
$ rake db:create
$ rake db:migrate
$ rake db:fixtures:load RAILS_ENV=test
$ rake db:test:prepare

This rake task does most of this for us. To get data into the database I create a second task shown above ‘populate’.

Step 2: Create a populate task.


require 'faker'

namespace :db do

  desc "Fill database with sample data"
  task :populate => :environment do

    puts "----------------"
    puts "RAILS_ENV is #{RAILS_ENV}"
    puts "----------------"

    puts "----------------"
    puts "--- populate ---"
    puts "----------------"

    make_cities
    make_users
    make_categories
    make_listings
  end

end

def make_cities

  puts "----------------"
  puts "--- cities   ---"
  puts "----------------"

  City.create!(:name => "Calgary")
  City.create!(:name => "San Francisco")
end

def make_users

  puts "----------------"
  puts "---- users  ----"
  puts "----------------"


  admin = User.create!(:name => "aaa",
                       :email => "aaa@aaa.com",
                       :password => "foobar",
                       :city_id => 1)
  admin.toggle!(:admin)
  2.times do |n|
    name = Faker::Name.name
    email = "example-#{n+1}@railstutorial.org"
    password = "password"
    User.create!(:name => name,
                 :email => email,
                 :password => password,
                 :city_id => 1)
  end
end

This task uses the fake gem to create and load test data into which ever environment we pass in via ‘RAILS_ENV’. It won’t work by itself yet, but we can now run this using our ‘all’ task by going:

> rake db.all
> rake db.all RAILS_ENV=test

Step 3: Put it together in a bash script

db.sh

#!/bin/bash
echo "Reseting database"
rake db:all
rake db:all RAILS_ENV=test

Now when ever I would to scrap and start all over, I rollback my git repository, and rebuild my db.

Voila. Clean slate and I can what ever it was I was trying to do again.

How to setup Jasmine Rails31 directory structure

Leave a comment

Following are instructions on how to setup/configure jasmine for use with rails31. It’s mostly straight forward, just one gotcha with regards to how to tell jasmine where your source files are.

1. Add jasmine gem

http://asciicasts.com/episodes/261-testing-javascript-with-jasmine has a nice summary of how to setup and install the rails gem for jasmine.

Gemfile

gem 'jasmine', :group => [:development, :test]

> bundle install
> rails g jasmine:install
> rake jasmine

2. Create your test.

spec/javascripts/listings_model_spec.js

describe("ListingModelSpec", function() {

  beforeEach(function() {
    setFixtures('<input type="hidden" id="city_name" value="Calgary" />');
  });

  it("gets the city name", function() {
    var model = new ListingsModel();
  });

});

Here I am writing a test that verifies I can read a value from a hidden field. To do that, jasmine will need jquery, and another useful helper library called jasmine-jquery (for that setFixture method).

Download and place them both in the /spec/javascripts/helpers dir.

4. Tell jasmine where to find your javascript files.

jasmine knows where to find your src files via it’s jasmine.yml file.

Assuming you want your javascript files to live under apps (where rails31 like them)

Edit the jasmine.yml file to look something like this:

/spec/javascripts/support/jasmine.yml

# src_files
#
# Return an array of filepaths relative to src_dir to include before jasmine specs.
# Default: []
#
# EXAMPLE:
#
# src_files:
#   - lib/source1.js
#   - lib/source2.js
#   - dist/**/*.js
#
src_files:
  - public/javascripts/prototype.js
...
  - app/assets/javascripts/**/*.js

See that last line at he bottom? That’s the one you need to point to the rails3 javascript directory. The other files above it points to where rails used to share it’s javascript files but everything is now stored under apps so just point there.

With that you should now be able to run your own javascript tests by firing up the jasmine server:

> rake jasmine

Going to localhost:8888 and seeing your output there.

More resources

http://railscasts.com/episodes/261-testing-javascript-with-jasmine

How to do an jQuery ajax call from a dropdown using Rails

6 Comments

Here is a simple example I got going that refreshes a text box based on the user changing the selection from a drop down box.

1. Setup jQuery

Download or add a link to the jQuery CDN as shown below.

application.html.erb

<!DOCTYPE html>
<html>
<head>
  <title>Blog</title>
  <%= stylesheet_link_tag    "application" %>
  <%= javascript_include_tag "application" %>
  <%= javascript_include_tag "http://ajax.googleapis.com/ajax/libs/jquery/1.6.4/jquery.min.js" %>
  <%= csrf_meta_tags %>
</head>
<body>

<%= yield %>

</body>
</html>

2. Create a view.

This view will populate a textbox with a ‘hello’ message whenever it’s selection has changed.

/views/foo/bar.html.erb

<div class="field">
  <label>Category</label><br />

  <select name="category[id]" id="category_id">
    <option value="1">art</option>
    <option value="2">bikes</option>
  </select>
</div>

<input type="text" size="57" id="category_textbox">

3. Create a controller.

Our controller doesn’t do much at the moment.

class FooController < ApplicationController
  def bar

  end
end

But to get it to work we need to add one route to our route table.

routes.rb

match ':controller(/:action(/:id(.:format)))'

Once we do that we should see this:

3. Add the ajax.

To detect the change in the drop down, we will use jQuery to register for the select ‘change’ event. Once caught, we will then do an ajax GET to the backend to retrieve our JSON response.

/views/foo/bar.html.erb

 <script type="text/javascript">
jQuery(function($) {
  $("#category_id").change(function() {
    var state = $('select#category_id :selected').val();
    if(state == "") state="0";
    jQuery.getJSON('/foo/bar/' + state, function(data){
        $("#category_textbox").val(data.message);
    })
    return false;
  });
})
</script>

<div class="field">
  <label>Category</label><br />

  <select name="category[id]" id="category_id">
    <option value="1">art</option>
    <option value="2">bikes</option>
  </select>
</div>

<input type="text" size="57" id="category_textbox">

Note: This isn’t my preferred method of embedding javascript and I read that if I used the jQuery live method to register for events I should be able to extract this code into it’s own javascript file but I wasn’t able to get it going. Would love it if someone could show me what I am doing wrong – more information here (http://www.petermac.com/rails-3-jquery-and-multi-select-dependencies/).

4. Send back the response.

Now we just need to configure our controller to send back a json response, as well as handle the original html response.

class FooController < ApplicationController
  def bar
    @msg = { "success" => "true", "message" => "hello"}

    respond_to do |format|
      format.html
      format.json { render json: @msg }
    end
  end
end

Once it’s all working, your show see something like this using Google Developer Tools:

It’s not perfect, but this example at least shows how to hook up jQuery and make an ajax call to a rails backend. I will update this once I learn more about the rails Asset Pipeline and why I wasn’t able to put the javascript into it’s own file.

Rails drop downs

6 Comments

Rails helpers for drop downs were initially confusing to me. I didn’t get how to deal with all the options and variations.

Once I got a few examples going however, they didn’t see that bad. Here’s what I have learned.

collection_select

<%= collection_select(:category, :id, Category.all, :id, :name) %>

produces

<select id="category_id" name="category[id]">
   <option value="1">art</option>
   <option value="2">books</option>
   <option value="3">computers</option>
</select>

This one is handy when you know what your resource is (in this case categories) and you just want to populate it into a drop down.

collection_select(object, method, collection, value_method, text_method, options = {}, html_options = {})

object – is basically the rails resource you are going to get the id and value from for each of your drop down options

method – is the way rails gets an instance of your object. It calls this ‘method’ and whatever object is returned is the instance it uses to populate the drop down options.

collection – is the resource you are going to use to populate the drop down. You can either call it here (Category.all) or have it populated in a controller and have it set as an instance variable (i.e. @categories = Category.all in your controller).

value_method & text_method – are the methods called on your object instance to populate the value and text elements of the drop down.

If you wanted to set some options you can also:

collection_select(:category, :id, Category.all, :id, :name,  {:include_blank => "Please select"}, {:class=>'my-custom-class'})

How do I extract selected drop down value on the backend?

One handy way to see what values are being passed to your controller is to raise an exception and inspect the parameters:

class MyController  < ApplicationController
    def index
      raise params.inspect
      ...
    end
end

When you do this for the above example you see the parameters come to us looking like this:

So we have a category hash with includes the name/value pair id => 1.

To extract that in our view we’d go:

      id = params[:category][:id]

Which you could check by going:

      raise id.inspect

What if my drop down is part of a form post?

Same thing as above. Only this time your drop down is going to be tied to the resource you are posting as.

<%= form_for @listing do |f| %>
  <div class="field">
    <%= f.label :category %><br />
    <%= f.collection_select(:category_id, @categories, :id, :name, :include_blank => "Please select") %>
  </div>
  <div class="actions">
    <%= f.submit "Submit" %>
  </div>
<% end %>

Here that f.collection_select puts the parameters into a listing hash which looks something like this:

Same thing, only now the select drop down value is associated with the posting (listing) hash which you can extract as”

params[:listing][category_id]

For more information on rails drop downs checkout:

collection_select api
http://guides.rubyonrails.org/form_helpers.html#making-select-boxes-with-ease

Non-resource routes in rails

4 Comments

When you define a new resource in rails, you get a slew of routes automatically generated for you, defined by a resource definition in your routes file.

routes.rb

 resources :photos

Now this is great for general resources. But what if you need a URL, that can’t be defined by the conventional rails route.

For example what if you need to display listings (a resource) by cities (another resource).

city.rb

class City < ActiveRecord::Base
  has_many  :listings
end

Listing.rb

class Listing < ActiveRecord::Base
  belongs_to  :city
end

How would you generate the following URL to display listings by city:

/cities/1/listings

Non-resource routes to the rescue. You can create a route that will hook these up as follows:

routes.rb

  match '/cities(/:id(/listings))',  :to => 'cities#listings'

Now whenever rails sees a request of this type, it will forward it as follows:

:controller => citities
:id => params[:id]
:action -> listings

Which ends up in:

cities_controller.rb

class CitiesController < ApplicationController
  def listings
    @listings = Listing.where(:city_id => params[:id])
  end
end

For more on non-resource rails routes checkout the excellent documentation at the Ruby On Rails Guide.

Older Entries

%d bloggers like this: