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 31-40 of 66 total

Add a to_conditions method to ActiveRecord::Base for converting models to finder :conditions hash.

// Mixes in a to_conditions method to ActiveRecord::Base. Converts the attributes of an AR object to a
// ActiveRecord::Base#find :conditions hash. Useful for comparing AR objects, especially when looking for
// duplicates.
// E.g.
//
// if not Post.find(:all, :conditions => my_post.conditions).empty?
// puts "Duplicate found"
// end

   1  
   2  module Bezurk #:nodoc:
   3    module ActiveRecord #:nodoc:
   4      module Extensions
   5        def to_conditions
   6          attributes.inject({}) do |hash, (name, value)|
   7            hash.merge(name.intern => value)
   8          end
   9        end
  10        alias :to_conditions_hash :to_conditions
  11      end
  12    end
  13  end
  14  
  15  ActiveRecord::Base.send(:include, Bezurk::ActiveRecord::Extensions)

RSS Twitter Bot

Republish an RSS feed on a twitter account. This was the source I used to run the Woot Twitter Bot before they took it over.

   1  
   2  require 'rubygems'
   3  require 'active_record'
   4  require 'simple-rss'
   5  require 'open-uri'
   6  require 'twitter'
   7  
   8  #twitter account to post to
   9  twitter_email = "yourtwitteremail@bla.com"
  10  twitter_password = "secret"
  11  
  12  #rss feed to post
  13  rss_url = "http://yoursite.com/index.xml"
  14  rss_user_agent = "http://twitter.com/yourbot"
  15  
  16  #sqlite db
  17  path_to_sqlite_db = "/PATH/TO/db.sqlite"
  18  
  19  
  20  ActiveRecord::Base.logger = Logger.new(STDERR)
  21  ActiveRecord::Base.colorize_logging = false
  22  
  23  ActiveRecord::Base.establish_connection(
  24      :adapter => "sqlite3",
  25      :dbfile  => path_to_sqlite_db
  26  )
  27  
  28  #uncomment this section the first time to create the table
  29  #
  30  #ActiveRecord::Schema.define do
  31  #    create_table :item do |table|
  32  #        table.column :title, :string
  33  #        table.column :link, :string
  34  #    end
  35  #end
  36  
  37  class Item < ActiveRecord::Base
  38    def to_s
  39      "#{self.title[0..(130-self.link.length)]} - #{self.link}"
  40    end
  41  end
  42  
  43  #run the beast
  44  rss_items = SimpleRSS.parse open(rss_url ,"User-Agent" => rss_user_agent)
  45  
  46  for item in rss_items.items
  47    Item.transaction do
  48      unless existing_item = Item.find(:all, :conditions => ["link=?", item.link]).first
  49        twitter ||= Twitter::Base.new(twitter_email, twitter_password)
  50        new_item = Item.create(:title => item.title, :link => item.link) 
  51        twitter.post(new_item.to_s)
  52      end
  53    end
  54  end


Run this once with the lines uncommented to create the DB, then slap it in your crontab.

SQL-Injection save parser generates ORDER BY statement

Parses a string and generates an SQL order statement.

Because it's SQL-Injection save you can put it in your link_to method as :order => '+name' and then call #parse_order( params[:order] ).

Examples:
'+name' => 'name'
'+lastname+firstname' => 'lastname, firstname'
'+lastname-gender' => 'lastname, gender DESC'

   1  
   2  module ActiveRecord
   3    class Base
   4      class << self
   5  
   6        def parse_order( order )
   7          order = order.to_s.gsub /([ \+\-][a-z_]+)/ do |match|
   8            next unless self.column_names.include?( match[1..-1] )
   9  
  10            case match[0, 1]
  11            when '-' then "#{ match[1..-1] } DESC, "
  12            else "#{ match[1..-1] }, "
  13            end
  14          end and order[0..-3]
  15        end
  16      
  17      end
  18    end
  19  end

ActiveRecord each_by_page

Perform an operation on a set of models including ActiveRecord callbacks, without instantiating all models at once.

Step through and instantiate each member of the class and execute on it, but instantiate no more than per_page instances at any given time.
Safe for destructive actions or actions that modify the fields your :order or :conditions clauses operate on.

   1  
   2  module ActiveRecord
   3    class Base
   4      def each_by_page per_page, options = {}, &block
   5        # By-id for model-modifying blocks
   6        # Build SQL to get ids of all matching records using the options provided by the user
   7        sql = construct_finder_sql(options.dup.merge({ :select => 'id' }))
   8        # Get the results as an array of tiny hashes { "id" => "1" } and flatten them out to just the ids
   9        all_ids = connection.select_all(sql).map { |h| h['id'] }
  10        at_a_time = 0..(per_page-1)
  11   
  12        # chop apart the all_ids array a segment at a time
  13        begin
  14          ids = all_ids.slice!(at_a_time)
  15          ids_cases = []
  16          ids.each_with_index { |id, i| ids_cases << "WHEN #{id} THEN #{i}" }
  17          ids_cases = ids_cases.join(' ')
  18   
  19          # Do the deed on this page of results
  20          find(:all, options.merge(
  21            :conditions => [ 'id IN (?)', ids ],
  22            :order => "CASE id #{ids_cases} END"
  23          )).each &block
  24   
  25        end until all_ids.empty?
  26      end
  27    end
  28  end


link to blog entry

Export Adobe FDF and XFDF from ActiveRecord

I needed to export XFDF from an application. This code is kind of untested, but is based on the solution I came up with. I would appreciate responses/modifications. It's very straightforward mixin stuff, for the most part.
   1  
   2  # (X)FDF Export for ActiveRecord
   3  # Based on Justin Koivisto's FDF library for PHP
   4  # Author: Sean Cribbs, seancribbs_AT_gmail_DOT_com, http://seancribbs.com
   5  module FDF
   6    def self.included(base)
   7      base.extend ClassMethods
   8    end
   9    
  10    module ClassMethods
  11      # Options:
  12      #  <tt>:filename</tt> - The filename of the associated PDF document.  REQUIRED!
  13      #  <tt>:indentation</tt> - How much to indent the resulting XFDF (XML)
  14      #  <tt>:include</tt> - Which associated models to include in the generated XFDF.
  15      #  <tt>:exclude_attributes</tt> - Which attributes of the current model should not be exported.  By default all non-internal attributes are exported (i.e. everything but _id fields).
  16      #  <tt>:include_attributes</tt> - Which attributes of the current model should be exported in addition to the default. By default all non-internal attributes are exported (i.e. everything but _id fields).
  17      #  <tt>:attributes</tt> - Override which attributes to export.
  18      def exports_xfdf(options = {})
  19        raise ArgumentError, "A :filename option must be specified." unless options[:filename]
  20        options[:indentation] ||= 2
  21        options[:include] = options[:include].is_a?(Array) ? options[:include] : [options[:include]].compact
  22        unless included_modules.include? XFDFMethods
  23          class_inheritable_accessor :xfdf_options
  24          extend ClassMethods
  25          include XFDFMethods
  26        end
  27        self.xfdf_options = options
  28      end
  29      
  30      # Options:
  31      #  <tt>:filename</tt> - The filename of the associated PDF document.  REQUIRED!
  32      #  <tt>:include</tt> - Which associated models to include in the generated FDF.
  33      #  <tt>:exclude_attributes</tt> - Which attributes of the current model should not be exported.  By default all non-internal attributes are exported (i.e. everything but _id fields).
  34      #  <tt>:include_attributes</tt> - Which attributes of the current model should be exported in addition to the default. By default all non-internal attributes are exported (i.e. everything but _id fields).
  35      #  <tt>:attributes</tt> - Override which attributes to export.
  36      def exports_fdf(options = {})
  37        raise ArgumentError, "A :filename option must be specified." unless options[:filename]
  38        options[:include] = options[:include].is_a?(Array) ? options[:include] : [options[:include]].compact
  39        unless included_modules.include? FDFMethods
  40          class_inheritable_accessor :fdf_options
  41          extend ClassMethods
  42          include FDFMethods
  43        end
  44        self.fdf_options = options
  45      end
  46    end
  47  
  48    module XFDFMethods
  49      def to_xfdf(options = {})
  50        options.reverse_merge! self.class.xfdf_options
  51        fields = Util.collect_values(self, self.class.content_columns.map(&:name), options)
  52        filename = options[:filename]
  53        xml = Builder::XmlMarkup.new :indentation => options[:indentation]
  54        xml.instruct!
  55        xml.xfdf("xmlns" => "http://ns.adobe.com/xfdf/", "xml:space" => "preserve") {
  56          xml.f :href => filename
  57          xml.fields {
  58            fields.each do |field, value|
  59              xml.field(:name => field) {
  60                if value.is_a? Array
  61                    value.each {|item| xml.value(item.to_s) }
  62                else
  63                  xml.value(value.to_s)
  64                end
  65              }
  66            end
  67          }
  68        }
  69        xml.target!
  70      end    
  71    end
  72    
  73    module FDFMethods
  74      def to_fdf(options={})
  75        options.reverse_merge! self.class.fdf_options
  76        fields = Util.collect_values(self, self.class.content_columns.map(&:name), options)
  77        filename = options[:filename]
  78        data = "%FDF-1.2\n%âã�?Ó\n1 0 obj\n<< \n/FDF << /Fields [ "
  79        fields.each do |field, value|
  80          if value.is_a? Array
  81            data << "<</T(#{field})/V["
  82            value.each {|v| data << "(#{v.strip})"}
  83            data << "]>>"
  84          else
  85            data << "<</T(#{field})/V(#{value.strip})>>"
  86          end
  87        end
  88      end
  89      data << "] \n/F (#{filename}) /ID [ <#{MD5.md5(Time.now).to_s}>\n ] >>" <<
  90              " \n>> \nendobj\ntrailer\n" << "<<\n/Root 1 0 R \n\n>>\n%%EOF\n"
  91    end
  92    
  93    module Util
  94      def self.collect_values(object, defaults, options = {})
  95        attrs = []
  96        if options[:attributes]
  97          attrs = stringify_all(options[:attributes]) rescue []
  98        else        
  99          [:include_attributes, :exclude_attributes].each do |opt|
 100            options[opt] = stringify_all(options[opt]) rescue []
 101          end
 102          attrs = stringify_all(defaults) + options[:include_attributes] - options[:exclude_attributes]
 103        end
 104        fields = attrs.inject({}) do |hash, key|
 105          value = object.send(key) rescue nil
 106          hash.merge key => value
 107        end
 108        fields.merge collect_association_values(object, options)
 109      end
 110      
 111      def self.collect_association_values(object, options = {})
 112        return {} if options[:include].blank?
 113        values = {}
 114        options[:include].each do |association|
 115          unless object.send(association).blank?
 116            models = object.send(association)
 117            unless models.is_a? Array
 118              columns = models.class.content_columns.map(&:name)
 119              values.merge! association_dump(association, models, columns)
 120            else
 121              models.each_with_index do |model, index|
 122                columns = model.class.content_columns.map(&:name)
 123                values.merge! association_dump("#{association.singularize}_#{index+1}", model, columns)
 124              end
 125            end
 126          end
 127        end
 128        values
 129      end
 130      
 131      def self.association_dump(prefix, object, attributes)
 132        attributes.inject({}) do |hash, attr|
 133          value = object.send(attr) rescue nil
 134          hash.merge "#{prefix}_#{attr}" => value
 135        end
 136      end
 137      
 138      def self.stringify_all(ary)
 139        ary.compact.map(&:to_s).uniq
 140      end
 141    end
 142  end
 143  ActiveRecord::Base.send :include, FDF


Also available from: http://pastie.caboo.se/38835

Active Record log

from <http://weblog.jamisbuck.org/2007/1/31/more-on-watching-activerecord>

In config/environment.rb:

   1  
   2  def log_to(stream)
   3    ActiveRecord::Base.logger = Logger.new(stream)
   4    ActiveRecord::Base.clear_active_connections!
   5  end


then in the console :
   1  
   2  >> log_to STDOUT
   3  => ...
   4  >> Post.find(:first)
   5    Post Load (0.000138)   SELECT * FROM posts LIMIT 1
   6  => #<Post:0x1234 ...>
   7  >>


The best part is, by clearing the active connections after setting the logger, you can change the logger at any time, even after you’ve made any number of find calls.

And, you can pass your own stream objects into it:
   1  
   2  >> buffer = StringIO.new
   3  => ...
   4  >> log_to buffer
   5  => ...
   6  >> Post.find(:first)
   7  => #<Post:0x1234 ...>
   8  >> p buffer.string
   9  => "  \e[4;35;1mPost Load (0.000138)\e[0m   \e[0mSELECT * FROM posts LIMIT 1\e[0m\n"
  10  >>

Simulating a foreign key constraint in ActiveRecord (Ruby on Rails)

Say for example we have products and a business rule that every product must have a category. Before ActiveRecord we would have done this with a NOT NULL foreign key constraint at the database level.

It wasn't entirely obvious (to me) how to achieve this in an ActiveRecord model - you need *both* of the following two lines:

   1  
   2    validates_presence_of :category, :message => " must be specified" 
   3    validates_associated :category


Hope this helps someone.

Synchronizing Rails DB Contents via Fixtures

The following rake task will dump the contents of the current environment's database to YAML fixtures. Stick the following in lib/tasks/fixtures.rake:

   1  
   2  namespace :db do
   3    namespace :fixtures do
   4      
   5      desc 'Create YAML test fixtures from data in an existing database.  
   6      Defaults to development database.  Set RAILS_ENV to override.'
   7      task :dump => :environment do
   8        sql  = "SELECT * FROM %s"
   9        skip_tables = ["schema_info"]
  10        ActiveRecord::Base.establish_connection(:development)
  11        (ActiveRecord::Base.connection.tables - skip_tables).each do |table_name|
  12          i = "000"
  13          File.open("#{RAILS_ROOT}/test/fixtures/#{table_name}.yml", 'w') do |file|
  14            data = ActiveRecord::Base.connection.select_all(sql % table_name)
  15            file.write data.inject({}) { |hash, record|
  16              hash["#{table_name}_#{i.succ!}"] = record
  17              hash
  18            }.to_yaml
  19          end
  20        end
  21      end
  22    end
  23  end


After making changes to the database that you'd like to dump to fixtures:

   1  
   2  rake db:fixtures:dump


After checking out updated fixtures from SVN:

   1  
   2  rake db:migrate
   3  rake db:fixtures:load

Filtering based on a has_and_belongs_to_many association

To create an indexed view of either all of the items (in a normal index view), or filtered based on an association, you can do the following. For example, if you have a many to many relationship between "users" and "tags", and you wish an index view of users filtered on whether they contain a particular tag, you can do the following:

In the tag index view:
   1  
   2  <h1>Listing tags</h1>
   3  
   4  <table>
   5    <tr>
   6      <th>Name</th>
   7    </tr>
   8  
   9  <% for tag in @tags %>
  10    <tr>
  11      <td><%=h tag.name %></td>
  12      <td><%= link_to 'Users', user_path(:tag_id=>tag) %></td>
  13      <td><%= link_to 'Show', tag_path(tag) %></td>
  14      <td><%= link_to 'Edit', edit_tag_path(tag) %></td>
  15      <td><%= link_to 'Destroy', tag_path(tag), :confirm => 'Are you sure?', :method => :delete %></td>
  16    </tr>
  17    <% end %>
  18  </table>
  19  <br />
  20  <%= link_to 'New tag', new_tag_path %>


Note that
   1  user_path(:tag_id=>tag)
translates to
   1  <a href="/users?tag_id=1">Users</a>
.

In the controller for users, modify the index and change the standard @users=User.find(:all) to

   1  
   2      @tag=Tag.find(params[:tag_id]) if params[:tag_id]
   3      @users = @tag.users || throw rescue User.find(:all)


The
   1  throw rescue
was because the @tag.users could throw an exception as well as having a nil value, and this is DRYer than coding it as
   1  
   2      @users = @tag.users || User.find(:all) rescue User.find(:all)


Full details at Displaying a subset of items from an association.

Simple Ruby ActiveRecord example

Using Ruby ActiveRecord with an in-memory SQLite database. A nice simple example of this wonderful library.

   1  
   2  require 'active_record'
   3  
   4  ActiveRecord::Base.logger = Logger.new(STDERR)
   5  ActiveRecord::Base.colorize_logging = false
   6  
   7  ActiveRecord::Base.establish_connection(
   8      :adapter => "sqlite3",
   9      :dbfile  => ":memory:"
  10  )
  11  
  12  ActiveRecord::Schema.define do
  13      create_table :albums do |table|
  14          table.column :title, :string
  15          table.column :performer, :string
  16      end
  17  
  18      create_table :tracks do |table|
  19          table.column :album_id, :integer
  20          table.column :track_number, :integer
  21          table.column :title, :string
  22      end
  23  end
  24  
  25  class Album < ActiveRecord::Base
  26      has_many :tracks
  27  end
  28  
  29  class Track < ActiveRecord::Base
  30      belongs_to :album
  31  end
  32  
  33  album = Album.create(:title => 'Black and Blue',
  34      :performer => 'The Rolling Stones')
  35  album.tracks.create(:track_number => 1, :title => 'Hot Stuff')
  36  album.tracks.create(:track_number => 2, :title => 'Hand Of Fate')
  37  album.tracks.create(:track_number => 3, :title => 'Cherry Oh Baby ')
  38  album.tracks.create(:track_number => 4, :title => 'Memory Motel ')
  39  album.tracks.create(:track_number => 5, :title => 'Hey Negrita')
  40  album.tracks.create(:track_number => 6, :title => 'Fool To Cry')
  41  album.tracks.create(:track_number => 7, :title => 'Crazy Mama')
  42  album.tracks.create(:track_number => 8,
  43      :title => 'Melody (Inspiration By Billy Preston)')
  44  
  45  album = Album.create(:title => 'Sticky Fingers',
  46      :performer => 'The Rolling Stones')
  47  album.tracks.create(:track_number => 1, :title => 'Brown Sugar')
  48  album.tracks.create(:track_number => 2, :title => 'Sway')
  49  album.tracks.create(:track_number => 3, :title => 'Wild Horses')
  50  album.tracks.create(:track_number => 4,
  51      :title => 'Can\'t You Hear Me Knocking')
  52  album.tracks.create(:track_number => 5, :title => 'You Gotta Move')
  53  album.tracks.create(:track_number => 6, :title => 'Bitch')
  54  album.tracks.create(:track_number => 7, :title => 'I Got The Blues')
  55  album.tracks.create(:track_number => 8, :title => 'Sister Morphine')
  56  album.tracks.create(:track_number => 9, :title => 'Dead Flowers')
  57  album.tracks.create(:track_number => 10, :title => 'Moonlight Mile')
  58  
  59  puts Album.find(1).tracks.length
  60  puts Album.find(2).tracks.length
  61  
  62  puts Album.find_by_title('Sticky Fingers').title
  63  puts Track.find_by_title('Fool To Cry').album_id
« Newer Snippets
Older Snippets »
Showing 31-40 of 66 total