rails 4.2 and bootstrap 3.x modal dialog

Creating a modal dialog in rails with bootstrap is pretty easy yet I’ve seen numerous incarnations that seem overly complex. Since it’s the NHL playoffs lets consider showing the details of a player. Here’s a simple example:

controller
app/controllers/players/players_controller.rb

def player_modal
  @player = Player.find_by(id: params[:id])
end

route
config/routes.rb

match '/player_modal/:id', to: 'players#player_modal', via: 'get', as: 'player_modal'

view
app/views/players/players_modal.js.erb

   $('body').append("<%= escape_javascript(render 'players/players_modal', player: @player) %>");
   $('#player-modal-dialog').modal();

The code below clears out modal for subsequent calls

    // must destroy the modal when it is hidden.
    // stackoverflow.com/questions/12286332/twitter-bootstrap-remote-modal-shows-same-content-everytime
    $(document).on("hidden.bs.modal", function (e) {
        $('body').removeClass('modal-open');
        $('.modal-backdrop').remove();
        $('#player-modal-dialog').remove();
    });

modal
app/views/players/_players_modal.html.erb

<!-- standard modal code.... -->
<div class="modal-body">
  name: <%= player.name %>
  positions: <%=player.positions %>
</div>

link

 <%= link_to ({:players, :action => :player_modal,
:id => player.id}, :remote => true) do %>
  <i class='fa fa-something'></i>
<% end %>

bootstrap – responsive utilities

I’ve been working on a side project, targetedhunt.com, to visualize hunting statistics and recently pushed it to heroku.

Although I’m using bootstrap I haven’t paid much attention to what the app looked like on phones or tablets. Honestly, I’ve just been too busy with the basics.  Now that I’m showing the app to people I decided it’s time to take advantage of bootstrap’s responsive utilities.

First off, I’m using Chrome’s build-in device emulator to test what the app looks like on various devices.  The device emulator is available by clicking the “Toggle device mode” icon on the upper left of the console.

When I looked at the app on the iphone the data table had too many columns to display. This told me i need to remove and/or combine some columns.

chrome-iphone6-search-page-01

You can also see the emulator tells us where the page’s content ends with the dark gray “shadow” that goes beyond the border of the device’s dimensions.

So now I needed to decided what columns to get rid of. Since you can only search on one species I removed that column but adding hidden-xs css class to the header and data columns.

Most hunters care about success rates and the number of bulls killed so I kept those columns and combined the cows and calves columns. For the cows and calves columns I again used hidden-xs css class. To only show the combined “cow & calf” column we need to hide the column when the device is larger than a phone and hide it when the device is a phone. To accomplish this I used visible-xs-block css class.

&lt;td class=&quot;visible-xs-block&quot;&gt;&lt;%= h.try(:calves)+h.try(:cows) %&gt;&lt;/td&gt;

As you can see this got me pretty close but the table is still a bit wide.

chrome-iphone-search-cowsncalves-toobig

I could have messed with the font size and spacing but this is a side project and I didn’t have enough time. Instead, I removed the “cows & calves” columns.

chrome-iphone6-search-fits

rails – generic error dialogs with bootbox.js

We started adding some ajax foo to one rails app I’m working on and I was looking for an simple way to add modal dialogs when an error occurred. Luckily, @makeusabrew created bootbox.js and the bootbox-rails gem.

First the standard install instructions from the gem page
Update your Gemfile

    gem 'bootbox-rails', '~&gt;0.1.0'

Now you need to edit your app/assets/javascripts/application.js file and add the following line:

  //= require boot box

Now a generic javascript function to handle ajax errors

function error_modal_ajax(jqXHR, textStatus, errorThrown){
	
    switch(jqXHR.status) {
    case 401:
    	dialog_message = "textStatus<br />";
    	dialog_message = "Your session has timed out. Please login again.";
    	dialog_label = "Login";
	    bootbox.dialog(dialog_message, {
			"label" : dialog_label,
			"class" : "success", // or primary, or danger, or nothing at all
			"callback": function() {
				window.location = "/";
			}
		});
        break;
    default:
    	dialog_message = "textStatus<br />";
    	dialog_message += "This is embarassing, an error occured. If this happens again, please contact support for assistance.";
    	bootbox.dialog(dialog_message);
	}   
}

Now, update all your error callbacks in ajax calls

$.ajax(REST_URL, {
  type: 'GET',
  dataType: 'json',
  cache: false,
  error: function(jqXHR, textStatus, errorThrown) {
    error_modal_ajax(jqXHR, textStatus, errorThrown);
  },
  success: function(data, textStatus, jqXHR) {
   ...		          
  }

You can see when a 401(Unauthorized) code is returned, a “login” button appears and your directed to the home page which displays a login form. The next thing I want to do is add a login form in the modal dialog.

Hope this helps.

rails: switching between connections

In the app I’m working on we are allowing certain roles the ability to create reports. Keeping things simple, the user basically enters some sql which, in-turn, populates worksheets in and Excel file. Of course this sounds dangerous since a user could update or even delete data.

To mitigate this risk we created a separate postgres user, report, which is used to execute the sql statements and only has grants for select. We updated our chef scripts to create this user with proper grants, we won’t show that here.

This is how the rails code was changed:

Update database.yml

I added an entry for each environment which used the read-only database user.

development_report:
adapter: postgis
encoding: unicode
database: bike
pool: 5
username: report
password: 12345678
timeout: 5000
host: localhost
schema_search_path: "public,postgis"
#just for test and dev
script_dir: /opt/local/share/contrib/postgis-1.5

Update code executing sql report

Notice, I wrapped the code executing the queries in a begin/ensure block because I wanted to make sure the connection was always switched back. Also, I’m not catching the exception because the caller is handling it.

def generate_report(report, filters)
  begin
    report_con_pool = ActiveRecord::Base.establish_connection("#{Rails.env}_report")
    report_con = report_con_pool.connection</code>

    result = report_con.execute(sqlTemplate)
    # ITERATE THROUGH RESULTS

  ensure
    # SWITCH BACK TO DEFAULT CONNECTION
    ActiveRecord::Base.establish_connection("#{Rails.env}")
  end # BEGIN
end

Rails – action mailer, devise per environment

Recently, I was configuring ActionMailer with devise and was surprised about the config.action_mailer.default_url_options configuration setting and wanted to document it.

default_url_options
This configuration parameter is found in each environmental configuration file located in ./config/environments.

I initially thought this told ActionMailer what to use for sending mail. I was wrong, this parameter is used to resolve named routes, such as devise’s edit_password_url.

smtp_settings & sendmail_settings
If you need to configure where to send the mail look into these configuration parameters.

Who is sending your emails?

If your like me, you probably want your app to send emails from different email addresses for each environment. Since this is a devise specific setting you’ll need to update config.mailer_sender in config/initializers/devise.rb

  config.mailer_sender = 'dev@example.com'
  if Rails.env.test?
    config.mailer_sender = 'test@example.com'
  elsif Rails.env.production?
    config.mailer_sender = 'prod@example.com'
  end

REFERENCES

rails – postgis and your models

We’re going to look at how to store a PostGIS, install PostgreSQL and PostGIS,  point using ActiveRecord and activerecord-postgis-adapter gem.

I followed the Geo-Rails Part 2 by @danielazuma post to create a model with a point attribute.  The tutorial then shows how easy it is to perform GIS queries but doesn’t get into the details of CRUD functionality through the web-site, this is where I’ll pick-up.

I’ll cover some of the tutorial here to get you started. if you want to see the GIS queries checkout Geo-Rails Part 2 by @danielazuma, his other post are great also, thanks Daniel!

Before we get started one thing I found confusing in Daniel’s tutorial is he calls the point attribute lat_long when in fact to insert data into the column the format is “POINT( LONGITUDE, LATITUDE). For my tutorial I’m calling the attribute location and the user will enter long, lat to make it consistent.

Create a migration specifying a location attribute as type point…

rails g migration port name:string location:point

Change the migration to make the location column geographic

class CreatePorts < ActiveRecord::Migration   def change    create_table :port do |t|      t.string :name      t.point :location, :geographic => true
     t.timestamps
   end
  end
end

Update the Model…

Let’s look at the Port model, there are several things we changed/updated.

class Port < ActiveRecord::Base   include PointsHelper   set_rgeo_factory_for_column(:location, RGeo::Geographic.spherical_factory(:srid => 4326))

  validates :location, format: { with: VALID_LONG_LAT_DIRECTION, message: I18n.t("validation.point") }
  def location
    point_to_s(self[:location])
  end
  def location=(s)
    # Parse "s" or do whatever you need to with it
    # then set your real attribute.
    self[:location] = create_point(s)
  end

Validation needs to be added for the specific format(s) my customer wanted, i.e. 104.9847° W, 29.7392° N. The Port.location attribute ends up being a RGeo::Geographic::SphericalPointImpl object.  I stored the validation format regex in ./config/initializers/constants.rb as a constant VALID_LONG_LAT_DIRECTION.

VALID_LONG_LAT_DIRECTION = /\A(\d*.\d*)(\xC2\xB0)(\s*)(\w),(\s*)(\d*.\d*)(\xC2\xB0)(\s*)(\w)\z/

Also notice I’m internationalizing the message using I18n.t(“validation.point”), the message is located in ./config/locales/en.yml. I’m keeping all my validation messages under validation:

validation:
  point: "should be longitude, latitude with direction, i.e. 39.7392° N, 104.9847° W"

Location Getter & Setter

The location attribure is actually a RGeo::Geographic::SphericalPointImpl object, because of this we need to perform translations to and from an rgeo point implementation and the formatted string.

To translate between a point object and string I overrode location getter and setter. So I can use these translation methods in other models I created a PointsHelper, it’s included in first line of the model.  The PointsHelper methods are s_to_point and point_to_s. Implementation of these methods is left as an exercise for the reader (I know I hate when people do that, just spoon feed me dammit!…)

Views
Since we overrode the location getter and setter only the form partial, _form, needs to be updated.

The form partial for location looks like this…

<div class="field">
  <%= f.label "location" %>
  <%= f.text_field :location, :value => @port.location %>
</div>

The change here is  :value => @port.location. If we don’t add :value the  getter is not called and the web page displays the value as seen  in the database, 0101000020E6100000C2172653053F5A40053411363CBD3D40.

So now we have a way to enter and view GIS points with in the rails framework. In the near future I’m sure I’ll need to plot these points on a map.

rails – custom date_select generator

On my project we wanted the ability to regenerate views ‘at will’. We weren’t able to achieve the ‘at will’ goal but we did successfully create a erb view generator using bootstrap classes. The purpose was to avoid having the update views as they are generated. I need to write a post about those details…

The next step was to create a better user experience for selecting a date.There were a few problems with the date_select widget

In lib/templates/erb/scaffold/_form.html.erb we added some logic to detect a date_select attribute and generate html for the bootstrap-datepicker gem

<%- if attribute.field_type == :date_select -%>
<div id="<%= singular_table_name %>_<%= attribute.name %>_calendar" >
  <div class="input-append date datepicker" data-date-format="yyyy-mm-dd"><%%= f.text_field :<%= attribute.name %>, 'data-behaviour' => 'datepicker' %></div>
</div>
<%- else -%>
  <%%= f.<%= attribute.field_type %> :<%= attribute.name %> %>
<%- end -%>

The code generator above creates the following html

<div id="bike_bought_calendar" >
  <div class="input-append date datepicker" data-date-format="yyyy-mm-dd">
    <%= f.text_field :bought, 'data-behaviour' => 'datepicker', style: input_style %>
     <span class="add-on"><i class="icon-calendar"></i></span>
   </div>
</div>

Next, in whatever model, in this case bike, you are using the date picker gem you need to add some javascript.

# Place all the behaviors and hooks related to the matching controller here.
# All this logic will automatically be available in application.js.
# You can use CoffeeScript in this file: http://coffeescript.org/
jQuery ->
  $('#bike_bought_calendar .input-append.date').datepicker
    todayBtn: "linked",
    autoclose: true,
    todayHighlight: true

The resulting calendar widget looks like this

BootstrapDatepickerRails-01
REFERENCES

RAILS – RSPEC Shared Example Groups

In my last post I talked about creating a mixin for validating precision of model attributes.

This post will cover writing an #rspec ‘shared example group’ that allow you to concisely write validation tests. To make this generic I wanted to pass in the precision and scale of the attribute being tested

I created spec/support/shared_validate_precision.rb to place the generic tests in and it looks like this.

shared_examples_for "validate_precision" do |property, precision, scale|
  it "can be nil" do
    record.send(:"#{property}=", nil)
    record.should be_valid
    record.errors.should_not have_key(:"#{property}")
  end
  .....
end

The test looks like this

require 'spec_helper'
describe Turbine do
  describe "validation" do

    let(:record) { FactoryGirl.build(:turbine) }

    describe "capacity" do
      it_behaves_like "validate_precision", :capacity, 20, 2
    end
...
end

A few things to notice

  • record: this is an instance of the model the test creates using #FactoryGirl
  • property: the attribute you want to test validation on
  • precision & scale: self documenting 🙂
  • record.send(:”#{property}=”, nil): sets the attribute of the model to nil
  • record.should be_valid: calls model.valid?
  • record.errors.should_not have_key(:”#{property}”): makes sure there are no errors for the model’s property

One thing I did not get a change to do was create an alias for the “shared example”. See more about aliases in #bitfluxx post  & #relish

REFERENCES

RAILS – Validating Precision

Let’s say you have an attribute that was defined as decimal{10,2} in your migration, how do you validate that?

I tried using regex but it became lengthy and cumbersome to handle every case or precision versus scale. This is because the number of digits on the left side of the decimal is dependent on the number on the left. The number 1234.5 has a precision 5 and scale of 1 while the number 123.45 has a precision 5 and scale 2. Both are valid.

Here’s and explanation from PostgreSql

The scale of a numeric is the count of decimal digits in the fractional part, to the right of the decimal point. The precision of a numeric is the total count of significant digits in the whole number, that is, the number of digits to both sides of the decimal point. So the number 23.5141 has a precision of 6 and a scale of 4. Integers can be considered to have a scale of zero.

I started searching for how to implement custom validations in rails and settled upon creating a mixin. Chris’ article was immeasurably helpful. I chose a mixin because I needed to use the validator across models.

Here are the highlights

Create config/initializers/validators.rb

This is where the mixin is created and it looks like this

ActiveRecord::Base.class_eval do
  
  def self.validates_precision(*attr_names)
  ...
  end
end

I wanted to pass in precision and scale so I added configuration parameters

ActiveRecord::Base.class_eval do
  
  # Set the default configuration
  configuration = { :precision => 20, :scale => 2 }  

  # Update defaults with any supplied configuration values
  configuration.update(attr_names.extract_options!)

  def self.validates_precision(*attr_names)
  ...
  end
end

The other thing I did was add internationalization using I18n.t(“validation.larger_than_max”…

Finally, here are the before and after of what the models look like

BEFORE

class Turbine < ActiveRecord::Base
  # VALIDATIONS
  validates :capacity, numericality: true, :allow_nil => true
  validates_format_of :capacity, :allow_nil => true, numericality: true, :with => /\A[\d]+\.?[\d]{0,2}\Z/, :message => I18n.t("validation.decimal_precision_two")
  
  validates_format_of :rotor_diameter, :allow_nil => true, :with => /\A[\d]+\.?[\d]{0,2}\Z/, :message => I18n.t("validation.decimal_precision_two")
  validates :rotor_diameter, numericality: true, :allow_nil => true
  
  validates_format_of :rotor_mass, :allow_nil => true, :with => /\A[\d]+\.?[\d]{0,2}\Z/, :message => I18n.t("validation.decimal_precision_two")
  validates :rotor_mass, numericality: true, :allow_nil => true

end

AFTER

class Turbine < ActiveRecord::Base
  # VALIDATIONS
  %w[capacity rotor_mass rotor_diameter].each do |key|
    validates_precision key, { :precision => 20, :scale => 2 }
    validates key, numericality: true, :allow_nil => true
  end
end

Yeh, I could have made it more generic by putting the validations in a method but i’m new to Ruby and was pushing my time limits on validating precision. I made a lot of progress and learned about more rails capabilities with the help of some great blog posts.

References

  • Rails: Writing DRY Custom Validators by @cblunt
  • Edge Rails Guide to Validations
  • Rails – Changing Your Most Recent Migration

    Yesterday, a Saturday, morning I was playing around with the authorization model presented in the rails tutorial. It’s pretty simple, admin flag on the Users model, but demonstrates what was intended.

    Anyways, I lost track of time, was late for a mtn bike ride and wasn’t able to find the proper rake command for getting db/schema.rb to update when your trying to update an already applied migration.  I posted because I learned how to set default values, RAILS – Migrations with Default Values, and ran into the schema.rb update problem.

    Luckily, a good friend and RoR expert, @jacobsenscott, saw my lame post and set me straight.

    Rails Changing Your Most Recent Migration

    Some great info above, especially the “read the docs stupid” tweet.

    Basically, “if you change your most recent migration you can do rake db:migrate:redo.”

    Thanks to Scott I got my mtn bike ride in and resolved this burning question…