Oomoo

August 20, 2008

How Do I ‘Refresh’ Individual Form Fields?

Filed under: ajax, controller, forms, model, rails, view — oomoo @ 11:13 pm

Recently, I wrote about creating a Ruby on Rails app that would create HTML formatted signatures for Outlook and web-based e-mail systems  (see: Outlook and the Double-Spaced Signature ).  This small application was my first chance to do true data-based entry using form fields (most of my prior RoR projects just tapped into existing databases for searching and presentation in a browser).

Well, most of my software development background has focused primarily on creating data-based applications with heavy data-entry and reporting, so I was eager to see if Rails was up to the task.  I used the Scaffolding classes built into Rails 2.1x to initially generate the pages (see, I wanted to call them screens or forms) to do the CRUD work, then I set about understanding how they worked and editing them to look a little better.

I understand the whole “disconnected” “stateless” model of web development, but I never dreamed it would be this dang hard to put a drop-down box and a couple of text fields inside form tags on a page and get them to interact with my database and with each other.  Of course, now that I know how to do it, it seems simple (and Rails is great again, the web makes sense, and all is right with the world).  Of course, what I could have finished in VFP in about 60 seconds took me many frustrating hours with more than one Rails/Ajax book thrown across the room.

For this application, I only have 2 tables (told you it was simple); Locations and Signatures.  The “Locations” table is a lookup table containing information that will be related to multiple “Signatures” (usually one Signature per person).

Locations Table:Locations schema

Signatures Table:Signatures schema

 

 

 

 

 

 

 

 

 

Locations.id  — Relates to —>> Signatures.location_id

Since each Signature relates back to a Location, if the Company changes their logo or web site, no change to the Signature record is needed, you need only regenerate the HTML signature for Outlook.  Okay, enough “database theory 101”.

Here is what I wanted to accomplish:

captured_Image.png Display a page where the user could select their “Location” from a drop-down combo field…

 

 

 

…once selected, the data from the “Location” phone/fax fields would automatically populate the first two phone fields of the “Signature” record. SigAfterLocation

…the phone fields would temporarily “highlight” to indicate to the user that something had changed

…then the fields would go back to their normal color, still containing the updated data from the Location record.

captured_Image.png[4]

 

 

 

 

Finally, the user “Saves” their Signature, and clicks the “Create My Signature” link which generates/downloads the HTML file containing their nicely formatted Outlook Signature.

captured_Image.png[6]

 

Piece-O-Cake!!!  No really, I wrote a complete version of this page using my desktop development tool (VFP) and it took me almost no time to have all the logic working and I even had a browser preview of the finished HTML that updated itself as you updated your signature.   Here is a screenshot of my desktop form written in VFP…

captured_Image.png[29]

 

We need our page to contain:

  • A Form (form_for)
  • A Drop-Down List (collection_select)
  • Some Labels (form.label)
  • Some Fields (form.field)
  • A Submit Button (form.submit)
  • Some way to know when a selection has been made from the drop-down list
  • Some way of updating the page based on the drop-down selection

 

    <% form_for @signature do |f| %>
    <div id="cbo_location_id", style="height: 31px">
        <%= f.label :choose_location, "Choose Your Location:", 
                    :style => "top: 5px; left: 3px; position: relative; color: white; font-weight: bolder;" %>

        <%= collection_select("signature", "location_id", @locations, "id", "loc_name", 
                              options ={:prompt => "...Select A Location", 
                              :include_blank => false}, 
                              html_options={:style => "top: 7px; left: 15px; position: relative;"}) %>
                              
        <%= observe_field("signature_location_id", 
                          :url => { :action => "location_lookup" }, 
                          :with => "'id=' + value") %>
    </div>
    
    ...other form fields here...

        <table width="350" height="150" cellpadding="0" cellspacing="0">
           <tbody> <tr> <th align="center"> <%= f.label :phone_desc, "Desc.", :style => "color: white;" %> </th>
                        <th align="center"> <%= f.label :phone_num, "Number", :style => "color: white;" %> </th></tr>
                   <tr> <td align="center"> <%= f.text_field :sig_p1_desc, :size => "10", :style => "" %>  </td>
                        <td align="center"> <%= f.text_field :sig_p1_num, :size => "20", :style => "" %>   </td></tr>
                   <tr> <td align="center"> <%= f.text_field :sig_p2_desc, :size => "10", :style => "" %>  </td>
                        <td align="center"> <%= f.text_field :sig_p2_num, :size => "20", :style => "" %>   </td></tr>
                   <tr> <td align="center"> <%= f.text_field :sig_p3_desc, :size => "10", :style => "" %>  </td>
                        <td align="center"> <%= f.text_field :sig_p3_num, :size => "20", :style => "" %>   </td></tr>
                   <tr> <td align="center"> <%= f.text_field :sig_p4_desc, :size => "10", :style => "" %>  </td>
                        <td align="center"> <%= f.text_field :sig_p4_num, :size => "20", :style => "" %>   </td></tr>
             </tbody>
          </table>

    <div style="height: 35px"> <p> <%= f.submit "&nbsp;Save&nbsp;" %>  </p>  </div>
  <% end %>

 

We need our form to:

  • Know that a selection has been made from the drop-down list:

       ( \app\views\signatures\edit.html.erb )

    <%= observe_field("signature_location_id",                           
                       :url => { :action => "location_lookup" },
                       :with => "'id=' + value") %>
  • Retrieve the chosen “Location” data:

      ( \app\controllers\signatures_controller.rb )

    class SignaturesController < ApplicationController
    
      def location_lookup
        @location = Location.find(params[:id])
      end
      ...other controller methods...
    end
  • Update/refresh the fields on the page that have changed, and highlight them: 
      ( \app\views\signatures\location_lookup.js.rjs )

    page[:signature_sig_p1_desc].value = 'p.' page[:signature_sig_p1_num].value = @location.loc_phone.strip page[:signature_sig_p2_desc].value = 'f.' page[:signature_sig_p2_num].value = @location.loc_fax.strip page.visual_effect :highlight, :signature_sig_p1_desc, :duration => 2 page.visual_effect :highlight, :signature_sig_p1_num, :duration => 2 page.visual_effect :highlight, :signature_sig_p2_desc, :duration => 2 page.visual_effect :highlight, :signature_sig_p2_num, :duration => 2

 

So, what is happening here?

  1. My drop-down list is:

        <%= collection_select…  %>
  2. When it’s value is changed, my “field observer” gets fired:

       <%= observe_field…  %>
  3. It’s "action" is defined as:

        :url => { :action => "location_lookup" }  
    which means it is going to call the controller action/method named "location_lookup"
  4. As a parameter, it is going to pass the "id" of the Location chosen:

       :with => "’id=’ + value"
  5. The "location_lookup" action/method in the controller "finds" the passed in Location ID:

       @location = Location.find(params[:id])
  6. Then (by magic) rails renders the contents of the RJS file.

    Actually, Rails will always look for an RJS file to exist that is named the same as the controller action/method.
  7. The RJS file will first update the field values with the contents of the Location data:

        page[:signature_sig_p1_num].value = @location.loc_phone
  8. Then, the RJS file will temporarily "highlight" the changed fields:

       page.visual_effect :highlight, :signature_sig_p1_num, :duration => 2

 

*Note – we didn’t need to use an RJS file, we could have achieved the same thing by putting the RJS code directly in the "location_lookup" controller action/method:

def location_lookup
  @location = Location.find(params[:id])

  render :update do |page|
    page[:signature_sig_p1_desc].value = 'p.'
    page[:signature_sig_p1_num].value = @location.loc_phone.strip
    page[:signature_sig_p2_desc].value = 'f.'
    page[:signature_sig_p2_num].value = @location.loc_fax.strip

    page.visual_effect :highlight, :signature_sig_p1_desc, :duration => 2
    page.visual_effect :highlight, :signature_sig_p1_num, :duration => 2
    page.visual_effect :highlight, :signature_sig_p2_desc, :duration => 2
    page.visual_effect :highlight, :signature_sig_p2_num, :duration => 2    
  end 
end

I just prefer to use the RJS file to keep my controller clean of view-specific things, and if you just think that the RJS is taking the place of the View (html.erb) that would normally be rendered after a controller action/method, then it makes sense.

*Note: "page" is an automatic reference to the existing web page object (DOM) that Rails is exposing so we can monkey around with it.  Now, isn’t that nice!

Since RJS is basically just "Railsified" javascript (AJAX), you can do just about anything to the existing web page.  You can add things, hide things, change all the colors, change the words, enable/disable things, and even insert calls to new javascript functions (like links to services on other web sites).

 

In review, the process involved really isn’t that different from what I had to code in my desktop VFP application.  The biggest difference was knowing what form elements to use (and their syntax) then just understanding that the full logic is done in pieces that are spread out over several files (view, controller, RJS).  In the end, the secret sauce for updating my field display is AJAX, simplified by the Rails helpers.

Happy Field Refreshing!!  –Oomoo

 

—UPDATE ––

Do you want to DEMO this application in your browser right now?

Just go here:  Outlook Signature Application

This web-based application is a full-featured signature generator for Outlook.  You can use it to create your signature or just use it as an educational tool.  This application was completely written at (and is hosted on) Heroku!!

(hint:  If you enter your “Location” and “Signature” information, be sure to delete it before leaving the site)

In fact, you can download the full source code here:  Download Source Code

Advertisements

March 11, 2008

“Model” Relationships

Filed under: model, rails — oomoo @ 1:47 pm

Notes about how Relationships work in Rails Models.  I created the examples and pulled some of the notes from the API doc.
 

http://api.rubyonrails.com/classes/ActiveRecord/Associations/ClassMethods.html 
 
 

“belongs_to” – Means “this class is a child of the class that is named in the belongs_to statement”. 

“parent class”  –  Use the mixed case singular form. 

“foreign key field”  –  Use the signular form of the parent class name with _id appended. 
 
 

Example: 

class Order < ActiveRecord::Base

  set_table_name “orders”

  set_primary_key “ordPK” 

  #tblOrders belong to customers, use the orders.ordcustpk field.

  belongs_to :customer, :foreign_key => “ordCustPK” 

  #Orders belong to CustomerContact,

  #but only when the field CustomerContact.custconPrimary = 1

  belongs_to :customercontact,  :conditions => “custconPrimary=1” 

  #Customer

  belongs_to :shipment, :foreign_key => “ordShipPK”

end 
 

class Customer < ActiveRecord::Base

  set_table_name “customers”

  set_primary_key “CustPK” 

  has_many :orders, :foreign_key => “ordCustPK”

  has_many :customercontacts, :foreign_key => “custconCustPK”

end 
 

class Customercontact < ActiveRecord::Base

  set_table_name “customercontacts”

  set_primary_key “custconPK” 

  #orders belong to customers, use the orders.ordcustpk field.

  belongs_to :customer, :foreign_key => “custconCustPK” 

  #CustomerContacts has many Orders,

  #  related through Customer, using the key index Orders.ordCustPK.

  #To link Orders to Customer.  (the link from

  #  CustomerContacts to Customers is already specified above)

  has_many :orders, :through => :customer, :foreign_key => “ordCustPK”

end 
 

class Shipment < ActiveRecord::Base

  set_table_name “shipments”

  set_primary_key “shipPK” 

  #Each Shipment only has one Order (one-to-one relationship)

  has_one :order

end 
 
 

One-to-one

Use has_one in the base, and belongs_to in the associated model.

  class Employee < ActiveRecord::Base

    has_one :office

  end

  class Office < ActiveRecord::Base

    belongs_to :employee    # foreign key – employee_id

  end 

One-to-many

Use has_many in the base, and belongs_to in the associated model.

  class Manager < ActiveRecord::Base

    has_many :employees

  end

  class Employee < ActiveRecord::Base

    belongs_to :manager     # foreign key – manager_id

  end 

Many-to-many

There are two ways to build a many-to-many relationship.

The first way uses a has_many association with the :through option and a join model, so there are two stages of associations.

  class Assignment < ActiveRecord::Base

    belongs_to :programmer  # foreign key – programmer_id

    belongs_to :project     # foreign key – project_id

  end 

  class Programmer < ActiveRecord::Base

    has_many :assignments

    has_many :projects, :through => :assignments

  end 

  class Project < ActiveRecord::Base

    has_many :assignments

    has_many :programmers, :through => :assignments

  end 

For the second way, use has_and_belongs_to_many in both models. This requires a join table that has no corresponding model or primary key.

  class Programmer < ActiveRecord::Base

    has_and_belongs_to_many :projects       # foreign keys in the join table

  end 

  class Project < ActiveRecord::Base

    has_and_belongs_to_many :programmers    # foreign keys in the join table

  end 

Choosing which way to build a many-to-many relationship is not always simple. If you need to work with the relationship model as its own entity, use has_many :through. Use has_and_belongs_to_many when working with legacy schemas or when you never work directly with the relationship itself.  

Is it a belongs_to or has_one association?

Both express a 1-1 relationship. The difference is mostly where to place the foreign key, which goes on the table for the class declaring the belongs_to relationship. Example:

  class User < ActiveRecord::Base

    # I reference an account.

    belongs_to :account

  end 

  class Account < ActiveRecord::Base

    # One user references me.

    has_one :user

  end 

The tables for these classes could look something like:

  CREATE TABLE users (

    id int(11) NOT NULL auto_increment,

    account_id int(11) default NULL,

    name varchar default NULL,

    PRIMARY KEY  (id)

  ) 

  CREATE TABLE accounts (

    id int(11) NOT NULL auto_increment,

    name varchar default NULL,

    PRIMARY KEY  (id)

  ) 
 
 
 

Partial List of Options:

:class_name – specify the class name of the association. Use it only if that name can’t be inferred from the association name. So has_one :author will by default be linked to the Author class, but if the real class name is Person, you’ll have to specify it with this option.

:conditions – specify the conditions that the associated object must meet in order to be included as a WHERE SQL fragment, such as authorized = 1.

:order – specify the order in which the associated objects are returned as an ORDER BY SQL fragment, such as last_name, first_name DESC

:foreign_key – specify the foreign key used for the association. By default this is guessed to be the name of the associated class in lower-case and _id suffixed. So a Person class that makes a belongs_to association to a Boss class will use boss_id as the default foreign_key.  
 
 

Examples:

belongs_to :firm, :foreign_key => “client_of”

belongs_to :author, :class_name => “Person”, :foreign_key => “author_id”

belongs_to :valid_coupon, :class_name => “Coupon”, :foreign_key => “coupon_id”, :conditions => ‘discounts > #{payments_count}’

A Model Model

Filed under: model, rails — oomoo @ 1:38 pm

Example and details about a Rails Model:

*This beginning description was transcribed from a training video for which I am looking for the source to give credit. 

A “Model” is a Class that deals with the manipulation of data.  Classes allow the combination of data and methods in one place.  Because it is a full-blown Class, it can have any number of its own variables and methods.  The Model class is the perfect place for data validation for a Rails system.  It actually makes more sense to store data-based logic here than in the database (assuming that other systems are not monkeying with your data).  This is one of the reasons most Rails programmers don’t use database constraints (one of the big reasons is ActiveRecord doesn’t like it either). 

Generally, a Model Class is comprised of several things:

  • Associations  (Relationships to other tables in the database)
  • Validations  (special rails “validates…” statements)
  • Accessors  (creates “getter” and “setter” methods automatically)  (methods used to get/put data in a private class variable)
  • CallBacks  (hooks into the underlying rails logic that allow you to inject your code a specific steps)
  • Class Variables (@@varname, only one set maintained across all objects of the class)
  • Instance Variables (@varname, each object has its own set to manipulate at will)
  • Class Methods (generic logic methods not tied to specific data) (or operate on the class itself, like instance counters)
  • Instance Methods (any method that is not a class method)

# Associations ================================================

  has_one :order_account_type

  has_many :orders

  belongs_to :order_address

  belongs_to :order_user

  belongs_to :order 
 

# Validations =======================================================

  validates_presence_of :order_user_id, :order_address_id, :order_id

  validates_length_of :cc_number, :maximum => 20

  validates_format_of :cc_number, :with => /^[\d]*$/,

                      :message => ERROR_NUMBER

  validates_format_of :credit_ccv, :with => /^[\d]*$/,

                      :message => ERROR_NUMBER

  validates_numericality_of :expiration_month, :expiration_year 
 

# Accessors =======================================================

  attr_accessor :name, :promotion_code, cc_number 
 

# CALLBACKS =================================================================

  before_save :set_product_cost

  before_destroy{|record | Person.destroy_all “firm_id=#{record.id}”} 

  During the life cycle of an active record object, you can hook into 8 events:

      (-) save

      (-) valid?

      (1) before_validation

      (2) before_validation_on_create

      (-) validate

      (-) validate_on_create

      (3) after_validation

      (4) after_validation_on_create

      (5) before_save

      (6) before_create

      (-) create

      (7) after_create

      (8) after_save  
 

# CLASS VARIABLES =======================================================

  @@number_of_instances

  @@version 
 

# INSTANCE VARIABLES =======================================================

  @total_line_items

  @time_created 
 

# CLASS METHODS =============================================================

  # List of years for dropdown in UI

  def self.years

    start_year = Date.today.year

    years = Array.new

            10.times do

      years << start_year

      start_year += 1

    end

    return years

  end 

  # Search by order_number

  def self.search(search_term, count=false, limit_sql=nil)

    if (count == true) then

      sql = “SELECT COUNT(*) “

    else

      sql = “SELECT DISTINCT orders.* “

      end 

      sql << “FROM orders “

      sql << “WHERE orders.order_number = ? “

      sql << “ORDER BY orders.created_on DESC “

      sql << “LIMIT #{limit_sql}” if limit_sql 

      arg_arr = [sql, search_term, “%#{search_term}%”] 

      if (count == true) then

        count_by_sql(arg_arr)

      else

        find_by_sql(arg_arr)

      end

  end

   
 

# INSTANCE METHODS ==========================================================

  def status

    code = OrderStatusCode.find(:first, :conditions => [“id = ?”, self.order_status_code_id])

    code.name

  end 

  def tax_cost

    (self.line_items_total) * (self.tax/100)

  end 

  def name

    return “#{billing_address.first_name} #{billing_address.last_name}”

  end 
 
 
 
Create A Model 

ruby script/generate model MyTable  (from Ruby command line… in your App’s folder) 

creates…

   db/migrate/000_create_mytable.rb

   app/models/mytable.rb

   test/unit/mytable_test.rb

   test/fixtures/mytable.yml 

Edit The Model 

edit…  db/migrate/000_create_mytable.rb 

“RAKE” your changes 

rake db:migrate   (from Ruby command prompt,  in your App’s folder) 

Test

ruby script/console   (from Ruby command prompt,  in your App’s folder)

     r = MyTable.new

     r.myfield = ‘new value’

     r.save

Manually Testing A Model/Controller

Filed under: controller, model, MVC, rails — oomoo @ 1:26 pm

One of the cool things about Rails, is that with almost no work, you can easily test your new Model/Controller combination.

*Newbie Notes

To initially test a new model-controller, put the line:  “scaffold :mymodelname”  in the controller class code (comment out any other existing code).

This allows you to test that everything is hooked up properly using full CRUD scaffolding.

(No new files are created and no coding changes are done, the scaffold is generated on-the-fly). 
 

Start InstantRails (if you are using it)

   …OR…

      Start Apache/IIS

      Start MySQL 

Start the Ruby Server

    start a “ruby console” command line session
    cd myappname
    ruby script/server
    You will see…
    => Booting Mongrel (use ‘script/server webrick’ to force WEBrick)
    ** Starting Rails with development environment… 
     

Browse the View

      http://localhost:3000/myviewname    (ex:  http://localhost:3000/order) 
 

Experiment with the Model

    Start a Ruby Console:
          cmd…  ruby script/console 
    Create a Record
          @myorder = Order.new 
          myorder.quantity = 5
          myorder.price = 5.95
          myorder.desc = ‘Just a test’
          myorder.ordtime = Time.now 
    Save a Record
          @myorder.save 
    Find a Record
          myorder = Order.find :first 
    Delete a record
          myorder.destroy 
     
     

Troubleshooting 

– ActiveRecord::StatementInvalid: Mysql::Error: Column cannot be null: INSERT INTO

    – You have fields in your table that do not allow NULL.  You will need to supply a value for them before you can save.  *scaffold handles all of this for you. 

– Internet Explorer cannot display the webpage

  • You do not have Apache/IIS started.
  • You do not have MySQL started.
  • You do not have the Ruby server started  (See “Start the Server” above). 
    Remember, your “view” webpage is trying to execute ruby scripts that reside back on the server.  Without a server component running, it cannot do anything.
  • You did not enter the address correctly in the browser (mispelled the view name).

Create A New Model/Controller

Filed under: controller, model, MVC, rails — oomoo @ 1:23 pm
These instructions use an example database table named… orders 
Model
    “ruby script/generate model order”     (Note – use the singular name “order”, not “orders”) 
    “ruby script/generate scaffold order”   (This will create BOTH the Model and Controller and will implement the basic Scaffold functionality as your base code) 
    This command also creates the following files:
             app/models/ order.rb
             test/unit/ order_test.rb
             test/fixtures/ orders.yml 

Controller

    “ruby script/generate controller order index”         (Note – use the singular name “order”, not “orders”) 
    creates…
      app/contollers/orders_controller.rb
      app/helpers/orders_controller.rb
      app/views/orders/index.rhtml
      test/functional/orders_controller_test.rb 
     

View

    Automatically created in the Controller step above. 
    To manually create
          create a folder:  app/views/orders/
          create a file:  index.rhtml  (in the new folder) 
     

Test It

  ruby script/server  (Note – Ruby command line, from the “application” folder) 

  http://localhost:3000/orders (Note – “orders” is the name of the “view folder”.  This will display the “index.rhtml” file by default)

Create A New Migration Script

Filed under: migration, model, rails — oomoo @ 1:19 pm

Overview:

  1. Generate the script
  2. Edit the script
  3. Test the Script
  4. Apply the Script
  5. Roll back a script
  6. Apply the Script… again
  7. Check the “schema.rb” that gets generated
    NewbieNote*  The Database.YML file dictates the database upon which your code (and rake commands) operate.  For example, this snippet from a database.yml file will execute all commands/code on the “mybd_development” database. 
    development:
      adapter: mysql
      database: mydb_development
      username: root
      password:
      host: localhost 
     

Details:

Generate the scripts…

    For a new Table/Model…      “ruby script/generate model order”
          This command also creates the following files:
             app/models/ order.rb
             test/unit/ order_test.rb
             test/fixtures/ orders.yml 
    For a new Table/Model (just the migration script)…   “ruby script/generate migration create_orders_table”
    For making modifications…      “ruby script/generate migration this_is_the_name_of_my_migration”
    For existing Tables (legacy data)…      …see the related topic ‘Create Migration For Existing Tables’ 
     
    (you should use explicit names, describing what the migration will do, since the generator will create a file with this same name and overwrite any previously existing one) 
    Scripts are Stored In:  \db\migrate\ 
    Scripts are Sequentially Numbered…  001_create_orders_table.rb, 002_create_second_table.rb, etc. 
    Next Number that needs to be applied, stored in table “schema_info” field named “version” 
    “db:migrate” will apply the scripts in sequential order, starting with the next number after the one stored in “schema_info.version”. 
     

Edit the script file (see below) 

    You probably want to include the “:force => true” parameter when creating a new table. 
    Make sure you have properly coded the “self.down” method in case you need to roll back to a prior version! 

Test the Migration Scrips…   “rake db:migrate –dry-run –trace” 

    If “nothing” happens, then your migration was not applied.  The most obvious reason is that the “schema_info.version” field already
    contains a number equal to, or higher than, the migration you are trying to apply. 
     

Apply the Migration Scripts…  “rake db:migrate”

    Did an error occur?  See the “troubleshooting” section below. 
    If “nothing” happens, then your migration was not applied.  The most obvious reason is that the “schema_info.version” field already
    contains a number equal to, or higher than, the migration you are trying to apply. 
     

Roll back a script…  “rake db:migrate VERSION=004”    (substitute the desired “version #” for the number “4”) (Use the number “0” to drop the tables) 

    If you manually “dropped” the table yourself, you need to add the table back (just add one field), so the rollback will work.
    OR, you could manually change the number stored in “schema_info.version” to a number prior to when the table existed. 
    It is a good idea to “roll back” EVERY script, just to make sure your “self.down” works properly (before the next \db\migrate\ script gets created). 

Apply the Migration Script…Again  “rake db:migrate”   (After it rolls back successfully, just “rake db:migrate” again to put your migration back into effect.) 

Check the “schema.rb” file to check your new model.  Can be run manually by…  “rake db:schema:dump” 

_______________________________________________________________________________________________

_______________________________________________________________________________________________ 
 
 

Table/Model Naming Conventions: 

    Model / Class – ( singular, first letter Capitalized, CamelCase for models like SteeringWheel)
    Table / schema (in database) – ( plural, with underscores instead of spaces between words, like steering_wheels)  
    Model/Class       Table/Schema
    Order                 orders
    LineItem             line_items
    Person                people
    Address             addresses
    Legacy               legacies
    Mouse               mice 
     

__________________________________________________________________________________________________

__________________________________________________________________________________________________ 
 

Migration Data Types

      string

      text

      integer

      float

      float :limit => 25   (yields a double)

      date

      time

      datetime

      timestamp

      binary

      boolean

      decimal, :precision => 15, :scale => 10   
               (precision = total digits, scale = digits right of decimal point)
 

      t.column :, :string, :limit => 0, :default => , :null => false

      t.column :, :integer, :default => ‘0’ :null => false

      t.column :, :decimal, :precision => 10, :scale => 0 :default => ‘0’ :null => false

      t.column :, :date, :default => ‘0000-00-00’, :null => false 
 

      *Force the ordCustPK field to be a Foreign Key (FK) referencing Customers.CustPK:

      t.column :ordCustPK, :integer, :default => ‘0’ :null => false,  
               :references
=> [:customers, :CustPK] 

      *Force the Primary Key (PK) of the customers table to be the CustPK field:

      create_table(:customers, :force => true :primary_key => ‘CustPK’) 
 

______________________________________________________________________________________________

______________________________________________________________________________________________ 
 

Some Additional Migration Syntax

      add_index “activitylog”, [“logcreatedate”], :name => “logcreatedate”

      add_constraint :projects, :unique => :name

      add_constraint :projects, :foreign_key => :owner_id, :references => :users

            OR… execute ALTER TABLE posts ADD CONSTRAINT fk_posts FOREIGN KEY(discussion_id) REFERENCES discussions(id) 

      say_with_time “Updating salaries…”

            …Inserts your own messages/benchmarks by using the #say_with_time method 
 

Available transformations

  • create_table(name, options) Creates a table called name and makes the table object available to a block that can then add columns to it, following the same format as add_column. See example above. The options hash is for fragments like “DEFAULT CHARSET=UTF-8” that are appended to the create table definition.
  • drop_table(name): Drops the table called name.
  • rename_table(old_name, new_name): Renames the table called old_name to new_name.
  • add_column(table_name, column_name, type, options): Adds a new column to the table called table_name named column_name specified to be one of the following types: :string, :text, :integer, :float, :decimal, :datetime, :timestamp, :time, :date, :binary, :boolean. A default value can be specified by passing an options hash like { :default => 11 }. Other options include :limit and :null (e.g. { :limit => 50, :null => false }) — see ActiveRecord::ConnectionAdapters::TableDefinition#column for details.
  • rename_column(table_name, column_name, new_column_name): Renames a column but keeps the type and content.
  • change_column(table_name, column_name, type, options): Changes the column to a different type using the same parameters as add_column.
  • remove_column(table_name, column_name): Removes the column named column_name from the table called table_name.
  • add_index(table_name, column_names, index_type, index_name): Add a new index with the name of the column, or index_name (if specified) on the column(s). Specify an optional index_type (e.g. UNIQUE).
  • remove_index(table_name, index_name): Remove the index specified by index_name.

____________________________________________________________________________________________________

____________________________________________________________________________________________________ 
 

Primary Key:

The Rails convention is for every table to have a field named “id” which is an auto-incrementing integer primary key.  
 
 
 

_____________________________________________________________________________________________________

_____________________________________________________________________________________________________ 
 
 

Specify Your Own Primary Key:

The Rails convention is for every table to have a field named “id” which is an auto-incrementing integer primary key

(that the Rails migration code will automatically insert into your tables). 

If you are working with an existing database, you probably don’t want to redefine all the primary key fields to suit Rails. 
 

In your Migration code, just use the “:primary_key =>” parameter in your create_table method call, like this: 
 

class CreateAppTables < ActiveRecord::Migration

  def self.up

    create_table “employees”, :force => true, :primary_key => “empPK” do |t|

      t.column “empFirstName”,    :string,    :limit => 100, :default => “”,   :null => false

      t.column “empLastName”,     :string,    :limit => 100, :default => “”,   :null => false

      t.column “empActive”,       :boolean,                  :default => true, :null => false

      t.column “empDateCreate”,   :timestamp,                                  :null => false

      t.column “empLastLogon”,    :datetime,                                   :null => false

      t.column “empCustomerMgr”,  :integer,   :limit => 5,   :default => 0,    :null => false

    end

end 

Note – Since you are specifying the primary key, DO NOT also list the primary key as a field !!!

Notice above that since “empPK” is the primary key field, no “t.column” line exists for the field “empPK”. 
 
 

In your Model code, you must also tell Rails not to use the standard “id” primary key. 

Include a statement like this in your model code: 

class Employee ActiveRecord::Base 

  set_primary_key “empPK” 

end 
 
 
 

________________________________________________________________________________________________

________________________________________________________________________________________________ 
 
 

(see also) http://redhillconsulting.com.au/rails_plugins.html 
If you use any engines, etc. that have their own migrations,  

You will need to edit them to keep the “Row Version Migration” plugin from inserting the extra columns. 

In the create table statement, include  :row_version => false 
 

You will also need to edit the field definitions for any field that ends with “id” to keep the “Foreign Key Migration” plugin

from trying to force foreign key constraints on the table. 

Add the following to the end of the each “id” field definition  :references => nil 
 

________________________________________________________________________________________________

________________________________________________________________________________________________ 
 
 

Examples

This migration will add a boolean flag to the accounts table and remove it again:

    class AddSsl < ActiveRecord::Migration
        def self.up
          add_column :accounts, :ssl_enabled, :boolean, :default => 1
        end 
        def self.down
          remove_column :accounts, :ssl_enabled
        end
    end 

Example of a more complex migration that also needs to initialize data:

  class AddSystemSettings < ActiveRecord::Migration

    def self.up

      create_table :system_settings do |t|

        t.column :name,     :string

        t.column :label,    :string

        t.column :value,    :text

        t.column :type,     :string

        t.column :position, :integer

      end 

      SystemSetting.create :name => “notice”, :label => “Use notice?”, :value => 1

    end 

    def self.down

      drop_table :system_settings

    end

  end 
 
 
 

EXAMPLE1:

class CreateRepairs < ActiveRecord::Migration

  def self.up

    execute “DROP TABLE IF EXISTS `repairs`” 

      create_table(:repairs, :force => true :options => ‘ENGINE=MyISAM’) do |t|

            t.column :ordPK, :integer, :null => false, :auto_increment => true

            t.column :ordCustPK, :integer, :null => false

            t.column :ordCustPONum, :string, :limit => 20, :default =>

            t.column :ordTotal, :decimal, :precision => 10, :scale => 2 :default => ‘0.00’

      end

  end 

  def self.down

    drop_table :repairs

  end

end 
 

EXAMPLE2:

class CreateRepairs < ActiveRecord::Migration

  def self.up

      create_table(:repairs, :force => true) do |t|

            t.column :ordPK, :integer, :default => 0, :null => false

            t.column :ordCustPONum, :string, :limit => 20, :null => false

            t.column :ordFinalDisposition, :string, :limit => 30, :null => false

            t.column :orDateOrderTaken, :date, :null => false  

            t.column :ordItemWeight, :decimal, :precision => 10, :scale => 0, :default => 0

            t.column :ordShippingWeight, :float, :limit => 25, :default => 0, :null => false

    end

  end 

  def self.down

    drop_table :repairs

  end

end 
 
 

Not all migrations change the schema. Some just fix the data:

  class RemoveEmptyTags < ActiveRecord::Migration

    def self.up

      Tag.find(:all).each { |tag| tag.destroy if tag.pages.empty? }

    end 

    def self.down

      # not much we can do to restore deleted data

      raise IrreversibleMigration

    end

  end 

Others remove columns when they migrate up instead of down:

  class RemoveUnnecessaryItemAttributes < ActiveRecord::Migration

    def self.up

      remove_column :items, :incomplete_items_count

      remove_column :items, :completed_items_count

    end 

    def self.down

      add_column :items, :incomplete_items_count

      add_column :items, :completed_items_count

    end

  end 

And sometimes you need to do something in SQL not abstracted directly by migrations:

  class MakeJoinUnique < ActiveRecord::Migration

    def self.up

      execute “ALTER TABLE `pages_linked_pages` ADD  
               UNIQUE `page_id_linked_page_id` (`page_id`,`linked_page_id`)”

    end 

    def self.down

      execute “ALTER TABLE `pages_linked_pages` DROP INDEX `page_id_linked_page_id`”

    end

  end 
 
 

_______________________________________________________________________________________________

_______________________________________________________________________________________________ 
 

The Next “Migration Number” that needs to be applied, is stored in table “schema_info” field named “version”

MVC Simple Sample

Filed under: controller, model, MVC, view — oomoo @ 12:45 pm

Assumptions: 

    – MySQL Database named “mydata_development” 
    – Table named my_orders 
     
     

Point YML to correct Database: 

    – File Location:  \config\database.yml 
    development:
      adapter: mysql
      database: mydata_development
      username: root
      password:
      host: localhost 
     
     

Model for Table “my_orders”: 

    – File Location:  \app\models\my_order.rb 
    class MyOrder < Activerecord::Base 
    end 
     
     

Controller: 

    – File Location:  \app\controllers\admin\myorders_controller.rb 
    class MyordersController < ApplicationController
      #scaffold :MyOrder – uncomment to have a full on-the-fly web CRUD scaffold.
     
      def index
        @myorder = MyOrder.find :first
      end
     
      def new
        @myorder = MyOrder.new 
        @myorder.ordPK = 3
        @myorder.ordCustPONum =
      end
    end 
     
     

View: 

    – File Location:  \app\views\myorders\index.rhtml 
    <h1>MyOrder#index</h1>
    <p>Find me in app/views/myorders/index.html.erb</p> 
    <%= @myorder.ordPK %>
    <%= @myorder.ordCustPONum %> 
     

To Run: 

March 5, 2008

A Simple Drill Down

Filed under: model, rails, ruby — oomoo @ 11:09 pm

A Simple Drill Down (list of orders to a single order)

Model
order.rb
class Order < ActiveRecord::Base
  def self .find_all_orders
    find( :all , :order => “id” )
  end
end


Controller

orders_controller.rb
def list
  @orders = Order.find_all_orders
end


Views
List.html.erb
< p > Find me in app/views/orders/ list .html.erb </ p >
< table class = “list” cellpadding = “0” cellspacing = “0”>
  <%
for order in @orders %>
    < tr >
      < td ><% = link_to ( h ( order . id ),
                           
: action   => ‘show’ ,
                           
: id => order )
%></ td >
      < td ><% = order . ordCustPONum %></ td >
      < td ><% = order . ordOEM %></ td >
      < td ><% = order . ordDescription %></ td >
      < td ><% = order . ordModelNum %></ td >
      < td ><% = order . ordSerialNum %></ td >
      < td ><% = order . ord Status %></ td >
    </ tr >
  <% end %>
</ table >
 

Show.html.erb 
< p > Find me in app/views/orders/show.rhtml </ p >
Order #: <% = @order . ord Number %>< br />
Customer PO #: <% = @order.ordCustPONum %>< br />

A Model Is A Class

Filed under: classes, model — oomoo @ 11:09 pm

A Model Is A Class

A “Model” is a Class that deals with the manipulation of data.  Classes allow the combination of data and methods in one place.  Because it is a full-blown Class, it can have any number of its own variables and methods.

The Model class is the perfect place for data validation for a Rails system. It actually makes more sense to store data-based logic here than in the database (assuming that other systems are not monkeying with your data). This is one of the reasons most Rails programmers don’t use database constraints (one of the big reasons is ActiveRecord doesn’t like it either).


Generally, a Model Class is comprised of several things:

  • Associations (Relationships to other tables in the database)
  • Validations (special rails “validates…” statements)
  • Accessors (creates “getter” and “setter” methods automatically) (methods used to get/put data in a private class variable)
  • CallBacks (hooks into the underlying rails logic that allow you to inject your code a specific steps)
  • Class Variables (@@varname, only one set maintained across all objects of the class)
  • Instance Variables (@varname, each object has its own set to manipulate at will)
  • Class Methods (generic logic methods not tied to specific data) (or operate on the class itself, like instance counters)
  • Instance Methods (any method that is not a class method)


# Associations
=======================================================

has_one :order_account_type
has_many :orders
belongs_to :order_address
belongs_to :order_user
belongs_to :order
 

# Validations
=======================================================

validates_presence_of :order_user_id, :order_address_id, :order_id
validates_length_of :cc_number, :maximum => 20
validates_format_of :cc_number, :with =>
/^[\d]*$/, :message => ERROR_NUMBER
validates_format_of :credit_ccv, :with => /^[\d]*$/, :message => ERROR_NUMBER
validates_numericality_of :expiration_month, :expiration_year

# Accessors
=======================================================
attr_accessor :name, :promotion_code, cc_number

# CALLBACKS
==========================================================

before_save :set_product_cost
before_destroy{|record | Person.destroy_all “firm_id=#{record.id}”}

During the life cycle of an active record object, you can hook into 8 events:
  (-) save
  (-) valid?
  (1) before_validation
  (2) before_validation_on_create
  (-) validate
  (-) validate_on_create
  (3) after_validation
  (4) after_validation_on_create
  (5) before_save
  (6) before_create
  (-) create
  (7) after_create
  (8) after_save


# CLASS VARIABLES
=======================================================

@@number_of_instances
@@version
# INSTANCE VARIABLES
=======================================================
@total_line_items
@time_created
# CLASS METHODS
=======================================================
# List of years for dropdown in UI

def self.years
  start_year = Date.today.year
  years = Array.new
  10.times do
    years << start_year
    start_year +=
1
  end
  return
years
end # Search by order_number
def self.search(search_term, count=false, limit_sql=nil)
  if (count == true) then
    sql = “SELECT COUNT(*) ”
  else
    sql = “SELECT DISTINCT orders.* ”
  end
  sql << “FROM orders ”
  sql << “WHERE orders.order_number = ? ”
  sql << “ORDER BY orders.created_on DESC ”
  sql << “LIMIT #{limit_sql}” if limit_sql arg_arr = [sql, search_term, “%#{search_term}%”]

  if
(count == true) then
    count_by_sql(arg_arr)
  else
    find_by_sql(arg_arr)
  end
end

# INSTANCE METHODS
==========================================================

def
status
  code = OrderStatusCode.find(
:first, :conditions => [“id = ?”, self.order_status_code_id])
  code.name
end def tax_cost
  (
self.line_items_total) * (self.tax/100)
end

def
name
  return “#{billing_address.first_name} # billing_address.last_name}”
end

December 31, 2007

Public Models (oh myyyyy)

Filed under: model, public, rails, ruby — oomoo @ 8:53 pm
Public Model Objects -or- Public ActiveRecord Objects

Sometimes you just need a “global” or “public” object that contains data.  If you store your “application settings” or “company configuration” information in a database table (which I like to do), then it makes sense that you could use ActiveRecord like you would for any other table, just in a global context. 

This would allow you to easily read and update the information.  It also neatly puts it in a single object instead of spreading it out across a bunch of individual public variables.  Yes, you could dump it in the Session or use an excellent plug-in like AppConfig, but if you have several application-wide groups of data like “application settings” and “company configuration”, they end up all together in one object model (I think). 

For simplicity, let’s say I have a table named app_settings that stores a bunch of application-wide settings.  To get a public/global model/activerecord object, I would do the following:

Create a standard Model (\app\models\appsetting.rb)
class AppSetting < ActiveRecord::Base
end

Create a standard Controller (\app\controllers\appsettings_controller.rb)
class AppSettings_ Controller < ApplicationController
  def index
    list
    render
:action => ‘list’
  end
  def list
    @ appsettings = AppSetting .find( 1 )
  end
  def
show
    @ appsettings = AppSetting .find(params[ : id ])
  end
end

 

Add code to application.rb (\app\controllers\application.rb)

#If $AppSetting object is empty or does not exist, create it now.
begin
  if
$ AppSetting .nil?
    $ AppSetting = AppSetting .find( 1 )
  end
rescue
  $ AppSetting = AppSetting .find( 1 )
end

 

Add “AppSettings” to your Views:

Application Name : <% = $ AppSetting . AppLongName %>
Version : <% = $ AppSetting . AppVersion %>

*Don’t forget to restart your application server after modifying application.rb (ruby script/server)
*Don’t forget that the names of these models, controllers, and fields are case-sensitive.

Create a free website or blog at WordPress.com.