Never been to DZone Snippets before?

Snippets is a public source code repository. Easily build up your personal collection of code snippets, categorize them with tags / keywords, and share them with the world

« Newer Snippets
Older Snippets »
Showing 11-20 of 64 total

Find all rows for a certain day; Count days between two dates

# Find all rows created on a certain day; Rails apparently has a built-in :db string format
self.find(:all, :conditions => ["created_at >= ? AND created_at <= ?", day.beginning_of_day.to_s(:db), day.end_of_day.to_s(:db)])

# Find number of days between two dates
def days_between_dates(first, last)
  (YMD(last) - YMD(first))
end

def YMD(date)
  date.to_date.to_s.gsub("-", "").to_i
end

State change observer for ActiveRecord in Rails

This is something to observe state changes as indicated by an attribute in an ActiveRecord Object in Rails. Normally, if you just need to observe an object and do things based on what state it's in, use the Observer provided in Rails. You can then use callbacks to do whatever you need to in the observer. However, if you want to do something upon a particular state transition, then you can try the stuff below:

Create a model like this. Here, the attribute mode keeps track of the state.
class CreateCarTransmission < ActiveRecord::Migration
  def self.up
    create_table :car_transmission do |t|
      t.column :engine_id, :integer, :null => false
      t.column :mode, :string, :null => false, :default => "park"
    end
  end

  def self.down
    drop_table :car_transmission
  end
end

In the model, you include the state transition observable class, and call state_observable.
class CarTransmission < ActiveRecord::Base
  include StateTransition::Observable
  state_observable CarTransmissionNotifier, :state_name => :mode
end

Then you define a notifier class that has different callbacks based on the transitions. It follows the pattern, "#{state_name}_from_#{state you're coming from}_to_#{state you're going to}"
class CarTransmissionNotifier < StateTransition::Observer
  def mode_from_drive_to_reverse(transmission)
    # send out mail and flash lights about how this is bad.
  end
end


So whenever you change the state from "drive" to "reverse" of a car transmission model in the controller, the notifier will fire up.

To use it, put the following code in a file called 'state_transition.rb', put it in your lib/ directory in rails.
require 'observer'

module StateTransition
  module Observable
    class StateNameNotFoundError < RuntimeError
      def message
        "option :state_name needs to be set to the name of an attribute"
      end
    end

    def self.included(mod)
      mod.extend(ClassMethods)
    end

    module ClassMethods

      def state_observable(observer_class, options)
        raise StateNameNotFoundError.new if options[:state_name].nil?
        state_name = options[:state_name].to_s
        
        include Object::Observable

        define_method(:after_initialize) do 
          add_observer(observer_class.new)
        end

        define_method("#{state_name}=") do |new_state|
          old_state = read_attribute(state_name)
          if old_state != new_state
            write_attribute(state_name, new_state) # TODO yield the update method
            changed
            notify_observers(self, state_name, old_state, new_state)
          end
        end
      end

    end
    
  end

  class Observer
    def update(observable, state_name, old_state, new_state)
      send("#{state_name}_from_#{old_state}_to_#{new_state}", observable)
    rescue NoMethodError => e
      # ignore any methods not found here
    end  
  end

end


more details here

Active Record YAML Backup Solution - Drop in rake task and config file to backup db and directorys of your choice (user uploaded files)

EZ drop in backup rake task for your rails projects - works well - 5 - 10 min set up, one rake file and one config file
backs up db and any directory's to any number of servers with rsync


BSD License or whatever, but it would be cool if you told me your using it
2007 ISS http://industrialstrengthinc.com

this is a sample config file and a rake task you can drop into any rails project to do backups, easy to automate.
Backs up your specified environment db to activerecord yml files (one per table) and zips them up in a human readable timestamped file
syncs that and any other set of arbitrary directory's to any number of arbitrary servers with rsync
be sure to set ssh key based logins
to run: rake backup
also: rake backup:db, rake backup:restoredb (promts for a file created by backup:db), rake backup:push (to push out what u got)
note: this uses something like 30 lines of code from some blog site I got it from that I cant recall, yay for that guy, thanks

## example crontab entry:

3 * * * * cd /rails_deployment_dir/current && nice rake backup RAILS_ENV=production >> /rails_deployment_dir/production_backup_system.log

## config file - goes in config/backup.yml

production: 
  dirs: 
   - db/backups
   - public/uploaded_images
  servers: 
    -  name: backup server number 1
       host: gridserver.com
       port: 22
       user: blah@whatever.com
       dir: /home/blah/backups
    -  name: backup server two
       host: kradradio.com
       port: 22
       user: kraduser
       dir: /home/kraduser/backups_from_my_rails_proj


development: 
  dirs: 
   - db/backups
   - public/uploaded_images
  servers: 
    -  name: local self
       host: localhost
       port: 5222
       user: oneman
       dir: /home/oneman/Documents/development_backup


## rake file lib/tasks/backup.rake

desc "Backup Everything Specified in config/backup.yml"
task :backup => [ "backup:db",  "backup:push"]

namespace :backup do
 
    RAILS_APPDIR = RAILS_ROOT.sub("/config/..","")
    
   def interesting_tables
     ActiveRecord::Base.connection.tables.sort.reject! do |tbl|
       ['schema_info', 'sessions', 'public_exceptions'].include?(tbl)
     end
   end
  
   desc "Push backup to remote server"
   task :push  => [:environment] do 
      FileUtils.chdir(RAILS_APPDIR)
      backup_config = YAML::load( File.open( 'config/backup.yml' ) )[RAILS_ENV]
      for server in backup_config["servers"]
       puts "Backing up #{RAILS_ENV} directorys #{backup_config['dirs'].join(', ')} to #{server['name']}"
       puts "Time is " + Time.now.rfc2822 + "\n\n"
         for dir in backup_config["dirs"]
          local_dir = RAILS_APPDIR + "/" + dir + "/"
          remote_dir = server['dir'] + "/" + dir.split("/").last + "/"
          puts "Syncing #{local_dir} to #{server['host']}#{remote_dir}"
          sh "/usr/bin/rsync -avz -e 'ssh -p#{server['port']} ' #{local_dir} #{server['user']}@#{server['host']}:#{remote_dir}"
         end
       puts "Completed backup to #{server['name']}\n\n"
      end
   end

    task :storedb => :environment do 

      backupdir = RAILS_APPDIR + '/db/backup'
      FileUtils.mkdir_p(backupdir)
      FileUtils.chdir(backupdir)
      puts "Dumping database to activerecord yaml files in #{backupdir}"
      interesting_tables.each do |tbl|

        klass = tbl.classify.constantize
        puts "Writing #{tbl}..."
        File.open("#{tbl}.yml", 'w+') { |f| YAML.dump klass.find(:all).collect(&:attributes), f }      
      end
      puts "Database Dumped.\n\n"
    end

    desc "Dump Current Environment Db to file"    
    task :db => [:environment, :storedb ] do
      backupdir = RAILS_APPDIR + '/db/backup'
      archivedir = RAILS_APPDIR + '/db/backups'
      backup_filename = "#{RAILS_ENV}_db_backup_#{Time.now.strftime("%B.%d.%Y_at_%I.%M.%S%p_%Z")}.tar.bz2"
      FileUtils.mkdir_p(archivedir)
      puts "Archiving #{backupdir} yaml files to #{backup_filename}\n\n"
      `tar -C #{backupdir} -cjf #{backup_filename} *`
      `mv #{backup_filename} #{archivedir}`
    end

    desc "Restore Current Environment Db from a file"    
    task :restoredb => [:environment] do 
        backupdir = RAILS_APPDIR + '/db/backup'
        archivedir = RAILS_APPDIR + '/db/backups'
        print "Input a file to load into the db: #{archivedir}/"
        backup_filename = STDIN.gets.chomp
        puts "Loading backup file: #{backup_filename}"
        FileUtils.chdir(archivedir)
        `tar -xjf #{backup_filename}`
        `mv *.yml #{backupdir}`
        FileUtils.mkdir_p(backupdir)
        FileUtils.chdir(backupdir)
    
        interesting_tables.each do |tbl|
        puts "Clearing #{tbl} table.."
        ActiveRecord::Base.connection.execute "TRUNCATE #{tbl}"
        puts "Loading #{tbl} backup file..."
        table = YAML.load_file("#{tbl}.yml")        

        if table.length > 0 && table.first.key?("id")
            highestid = 0
            table.each do |fixture|
             if fixture["id"] > highestid
                highestid = fixture["id"]
             end
            end

            ActiveRecord::Base.connection.execute "SELECT setval('#{tbl}_id_seq',#{highestid})"
            puts "Setting #{tbl}_id sequence to #{highestid}"
        end
         
        #klass = tbl.classify.constantize
        ActiveRecord::Base.transaction do 
        
          puts "Inserting #{table.length} values into #{tbl}"
          table.each do |fixture|
            ActiveRecord::Base.connection.execute "INSERT INTO #{tbl} (#{fixture.keys.join(",")}) VALUES (#{fixture.values.collect { |value| ActiveRecord::Base.connection.quote(value) }.join(",")})", 'Fixture Insert'
          end        
          puts "#{tbl} table restored.\n\n"
        end
       end
    end

 
end

conditions hash to a LIKE conditions array

This method turns a conditions hash (like params[:some_object]) into a conditions array where each field is searched for via a LIKE, and can be used as the :conditions in any find or paginate call, for example:

{name => 'foo', location => 'bar'} becomes ["name LIKE ? and location LIKE ?", "%foo%", "%bar%"]

New to ruby, so no doubt there is a more concise (or even built in?) way to do this.

  def to_like_conditions( conditions )

    like_conditions = []
    key_count = conditions.size
    k = ""
    conditions.each_key do |key|
      k += "#{key} LIKE ?"
      if key_count > 1 
        k += " and "
      end
      key_count -= 1
    end
    like_conditions << k

    conditions.each_value do |value| 
      like_conditions << "%#{value}%"
    end

    like_conditions

  end


RSpec Association Matchers

Ok, what I essentially wanted to accomplish was something like:

describe Product, 'with Group' do

  it 'should belong to group' do
    Product.should belong_to(:product_group)
  end

end

describe ProductGroup, 'with Product' do

  it 'should have many products depending (on group)' do
    ProductGroup.should have_many(:products).depending
  end

end


Here is the code:

module AssociationMatchers

  class AssociationReflection

    def initialize(type, name)
      @messages = {
        :missing_association =>
          '%s is not associated with %s.',
        :wrong_type =>
          "%s %s %s./nExpected: %s",
        :wrong_options =>
          "Options are incorrect.\nExpected: %s Got: %s",
        :missing_column =>
          "Missing foreign key.\nExpected: %s"
      }
      @name = name
      @expected_type = type
      @expected_options = {}
    end
    
    def matches?(target)
      Class === target or
      raise ArgumentError, 'class expected'

      @target = target

      unless @assoc = target.reflect_on_association(@name)
        @failure = :missing_association
        return false
      end 

      unless @assoc.macro.eql?(@expected_type)
        @failure = :wrong_type
        return false
      end

      if @expected_options.any? { |o| @assoc.options[o.first] != o.last }
        @failure = :wrong_options
        return false
      end

      @column ||= @assoc.primary_key_name || @assoc.klass.name.foreign_key

      @failure = case @assoc.macro.to_s
      when 'belongs_to'
        if @target.column_names.include?(@column.to_s) then nil
        else
          :missing_column
        end
      when /(?:has_many|has_one)/
        if    @assoc.options[:through] then nil
        elsif @assoc.klass.column_names.include?(@column.to_s) then nil
        else
          :missing_column
        end
      end

      return @failure.nil?
    end

    def failure_message
      case @failure
      when :missing_association
        @messages[@failure] % [@target.name, @name]
      when :wrong_type
        @messages[@failure] % [
          @target.name,
          @assoc.macro,
          @name,
          @expected_type
        ]
      when :wrong_options
        @messages[@failure] % [
          @expected_options.inspect,
          @assoc.options.inspect
        ]
      when :missing_column
        @messages[@failure] % @column
      end
    end
    def negative_failure_message
    end

    ### Generic Options

    def of(class_name)
      class_name = class_name.name if Class === class_name
      @expected_options[:class_name] = class_name
      self
    end
    def for(foreign_key)
      @column = foreign_key
      self
    end
    def due_to(conditions)
      @expected_options[:conditions] = conditions
      self
    end
    def ordered_by(statement)
      @expected_options[:order] = statement
      self
    end
    def including(*models)
      @expected_options[:include] = (models.length == 1)? models.first: models
      self
    end

  end
  class BelongsToReflection < AssociationReflection

    def initialize(name)
      super :belongs_to, name
    end

    def counted(column)
      @expected_options[:counter_cache] = column
      self
    end
    def polymorphic(true_or_false = true)
      @expected_options[:polymorphic] = true_or_false
      self
    end

  end

  class HasOneReflection < AssociationReflection

    def initialize(name)
      super :has_one, name
    end

    def as(interface_name)
      @expected_options[:as] = interface_name
      self
    end
    def depending(dependency = true)
      @expected_options[:dependent] = dependency
      self
    end
    def extended_by(mod)
      @expected_options[:extend] = mod
      self
    end

  end
  class HasManyReflection < AssociationReflection

    def initialize(name)
      super :has_many, name
    end

    def as(interface_name)
      @expected_options[:as] = interface_name
      self
    end
    def depending(dependency = :destroy)
      @expected_options[:dependent] = dependency
      self
    end

  end
  class HasAndBelongsToManyReflection < AssociationReflection

    def initialize(name)
      super :has_and_belongs_to_many, name
    end

  end

  def belong_to(model)
    BelongsToReflection.new model
  end
  def have_one(model)
    HasOneReflection.new model
  end
  def have_many(models)
    HasManyReflection.new models
  end
  def have_and_belong_to_many(models)
    HasAndBelongsToManyReflection.new models
  end
  alias_method :habtm, :have_and_belong_to_many

end


It checks:
* association exists
* association macro
* foreign key exists (except for habtm)
* options match (only a subset is supported)

Setup:
* put the code in RAILS_ROOT + "/lib/association_matchers.rb"
* put "config.include AssociationMatchers # lib/association_matchers.rb" in your spec_helper.rb configure block
* refactor your model specs...

Customize error_messages_for

// description of your code here

 module ActionView::Helpers::ActiveRecordHelper
   def error_messages_for(object_name, options = {})
    options = options.symbolize_keys
    object = instance_variable_get("@#{object_name}")
    if object && !object.errors.empty?
      content_tag("div",
      content_tag(
      options[:header_tag] || "h2",
      "Возникло #{object.errors.count} ошибок при сохранении"
      ) +
      content_tag("p", "Проблеммы возникли для следующих полей:") +
      content_tag("dl", object.errors.full_messages.collect { |msg| content_tag("dt", msg) }),
      "id" => options[:id] || "errorExplanation", "class" => options[:class] || "errorExplanation"
      )
    else
      ""
    end
  end
 end
  

Updating a join table by directly accessing the model

// project_controller.rb

def update
    @project = Project.find(params[:id])
    if @project.update_attributes(params[:project])
      
      for user in user_checkboxes :selected
        @project.update_assignment(user, params[:user_role][user.id.to_s])
      end
       
      for user in user_checkboxes :unselected
        @project.delete_assignment(user)
      end
      
      flash[:notice] = 'Project was successfully updated.'
      redirect_to :action => 'show', :id => @project
       
    else
      render :action => 'edit'
    end
  end

private

  def user_checkboxes(status)
    users = []
     for selected_user in params[:project_users].keys    
        choice = '1' if status == :selected
        choice = '0' if status == :unselected
        users << User.find(selected_user) if params[:project_users][selected_user] == choice
      end
    return users
  end 


// project.rb

class Project < ActiveRecord::Base
  has_many :assignments, :dependent => :destroy
  has_many :users, :through => :assignments
  has_many :roles, :through => :assignments

  def update_assignment(user, role_id)
    unless self.users.include?(user)
      Assignment.create(:project_id => self.id,
                        :user_id => user.id,
                        :role_id => role_id)
    else
      # update existing users
      assignment = Assignment.find_by_user_id_and_project_id(user.id, self.id)
      assignment.role_id = role_id
      assignment.save
    end
  end

  def delete_assignment(user)
    Assignment.destroy_all("user_id = #{user.id} AND project_id = #{self.id}")
  end

end

JRuby + ActiveRecord-JDBC

Sample of using JRuby w/JDBC and activerecord

db.sql
create database test;
grant all privileges on test.* to 'test'@'localhost' identified by 'test' with grant option;
drop table if exists tests;
create table tests (
  id          int          not null auto_increment,
  name        varchar(100) not null,
  description text         not null,
  primary key (id)
);


jruby_db_test.rb
# JRuby ActiveRecord-JDBC active_record
# jdbc drivers must be located in the CLASSPATH or in $JRUBY_HOME/lib
require 'rubygems'
gem 'ActiveRecord-JDBC'
require 'jdbc_adapter'
require 'active_record'
require 'java'

# Load DB configuration - yml file in same directory
def connect()
  conf = YAML::load(File.open('database.yml'))
  ActiveRecord::Base.establish_connection(conf)
end

class Test < ActiveRecord::Base
end

connect
t = Test.new
t.name = 'test'
t.save

t1 = Test.find(1)
puts t1.name


database.yml
adapter: jdbc
driver: com.mysql.jdbc.Driver
url: jdbc:mysql://localhost:3306/test
username: test
password: test

SQL Transaction in Rails

def fetch_value
	sql = ActiveRecord::Base.connection();
	sql.execute "SET autocommit=0";
	sql.begin_db_transaction
	id, value =
	sql.execute("SELECT id, value FROM sometable WHERE used=0 LIMIT 1 FOR UPDATE").fetch_row;
	sql.update "UPDATE sometable SET used=1 WHERE id=#{id}";
	sql.commit_db_transaction
 
	value;
end

Validate uniqueness of an id pair

# Prevent user from joining group twice
def validate
	errors.add_to_base("You are already a member of the #{self.group.name} Group") unless Grouping.find(:all, :conditions => {:user_id => self.user_id, :group_id => self.group_id}).blank?
end
« Newer Snippets
Older Snippets »
Showing 11-20 of 64 total