Thursday, February 25, 2010

Using REXML to read small xml files and load into database table

Save the following code in lib folder of your application as read_xml_file.rb . using command prompt run " ruby script/runner lib/read_xml_file.rb". The following code is possible to read only small xml files ( usually in kbs).


require 'rexml/document'
require 'rubygems'
include REXML
#def import_xml(tag)
file= File.new(RAILS_ROOT + "/file.xml")
doc= Document.new(file.read)

Ingredient.benchmark("Truncating ingredient table and inserting new records") do
#truncate ingredient table
ActiveRecord::Base.connection.execute("TRUNCATE ingredient")
# read table contents
XPath.each( doc, "//INGREDIENT_SUBSTANCES//ING" ){|ingredient|
ingredient_params = {}
#if there are more than one attributes for table then create a hash.
ingredient.children.each do |child|
key = "#{child.name}".to_sym
if key.to_s == "ISID"
@isid_value = child.text
else
ingredient_params[key] = child.text
end
#@desc_value = child.text if key.to_s == "DESC"

end
#create new instance
ingredient = Ingredient.new(ingredient_params)
#set the primary key if mass assigning of primary key is not possible.
ingredient.ISID = @isid_value
#save
ingredient.save
}
end

TRUNCATE All Tables in a Ruby on Rails

There might be a case where you need to delete all the rows from all the tables in the database to end up with just the structure(a bare DB). The right thing to do is to run the TRUNCATE command on all the tables. It will delete all the data and also reset the auto increment value. But the only current way to get a bare DB in rails now is to run rake db:reset. This will run rake db:drop, rake db:create and rake db:migrate. The downside of this is that if you have a reasonable number of migrations this command can take quite some time to run which is really not efficient when you are iterating fast. Hence following rake task will
TRUNCATE all the tables.

namespace :db do
task :load_config => :rails_env do
require 'active_record'
ActiveRecord::Base.configurations = Rails::Configuration.new.database_configuration
end

desc "Create Sample Data for the application"
task(:truncate => :load_config) do
begin
config = ActiveRecord::Base.configurations[RAILS_ENV]
ActiveRecord::Base.establish_connection
case config["adapter"]
when "mysql"
ActiveRecord::Base.connection.tables.each do |table|
ActiveRecord::Base.connection.execute("TRUNCATE #{table}")
end
when "sqlite", "sqlite3"
ActiveRecord::Base.connection.tables.each do |table|
ActiveRecord::Base.connection.execute("DELETE FROM #{table}")
ActiveRecord::Base.connection.execute("DELETE FROM sqlite_sequence where name='#{table}'")
end
ActiveRecord::Base.connection.execute("VACUUM")
end
rescue
$stderr.puts "Error while truncating. Make sure you have a valid database.yml file and have created the database tables before running this command. You should be able to run rake db:migrate without an error"
end
end
end


Just create a file named db_truncate.rake in your lib/tasks directory
with this code in it. Save the file and then run rake db:truncate. Your
database now should have no data now. Before you run the task make sure
that you have a valid database.yml file and have created the database
tables before running this command. You should be able to run rake
db:migrate without an error

In case you want to truncate a single table from database all you have to do is
ActiveRecord::Base.connection.execute("TRUNCATE table_name")



Monday, February 15, 2010

Avoid validations on create or update in rails

There might be several situations where we might have to avoid the default validations defined in the model when we update or create a record.
a.)One solution to avoid validation is by doing something like this
@something = Something.find(params[:id])
@something.column_save = 'your data'

respond_to do |format|
if @something.save(false) #save without validation
b.)Another solution is to write a separate method to validate in the model
please see the example below
validate :validates_uniqueness_of_name

def validates_uniqueness_of_name
return if deleted == true
num_duplicates = self.class.count(:conditions => ["name = ? AND deleted = ?",self.name, false])
if num_duplicates > 0
errors.add(:name, :taken)
end
end

How to delete a many-to-many association with Rails

One solution is to create a new model for the association. It should be the case
if you add attributes to the association (because push_with_attributes is now deprecated).
You can then simply find the association given the ids of your linked object and call destroy.

However, when you don't have any attribute in your liaison, the has_and_belongs_to_many
is nicer to work with. (you don't need a rails model for the liaison.)
Here is a link to the methods has_and_belongs_to_many adds where we can read :


"collection.delete(object, …) - removes one or more objects from the
collection by removing their associations from the join table.
This does not destroy the objects."

Let's assume we dispose of 2 models 'Post' and 'Category' with a N-N association :

class Post < ActiveRecord::Base
has_and_belongs_to_many :categories
end

class Category < ActiveRecord::Base
has_and_belongs_to_many :posts
end

To delete an association (remove a post from a category) you can use this method :

  def remove_post_from_category
post = Post.find(params[:post][:id])
category = post.categories.find(params[:category][:id])

if category
post.categories.delete(category)
end

end

This function will destroy the association but won't destroy the category.

You can also removes all the categories from the posts by using :


collection.clear - removes every object from the collection.
This does not destroy the objects.
In our case its

post.categories.clear