Testing if something is ‘true’ to all objects in an array/collection in ruby
Maybe there is something that does this already. I didnt know, so I whipped it together really quick. Its an extension to the array class that lets you know if something is 'true' to all objects in the array.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 | class Array def all_true?(&block) t_ref = true if block_given? self.each do |_val_| t_ref &= !!(yield(_val_)) end else self.each do |_val_| t_ref &= !!(_val_) end end t_ref end end |
You can use / test it like so:
1 2 3 4 5 6 | [ true, true, true ].all_true? #=> true [ true, true, false ].all_true? #=> false # Want to run some other non boolean logic on it? [ 1, 2, 3, 4, 5].all_true?{ |val| val.integer? } #=> true [ 1, 'a', 3, 4, 6].all_true?{ |val| val.integer? } #=> false |
Yeah, I just need to know is shits the same across the whole collection of active record objects sometimes. Now I can do it, go data.
Attaching local or remote files to Paperclip and Milton Models in Rails (Mocking content_type and original_filename in a Tempfile)
I was working on a project today where I needed to import some data from MySpace accounts (yeah, MySpace), which included importing the users profile image. In the controller that did the importing I was using OpenURI to retrieve the image and then turn it into a Tempfile to be attached the the model, like so:
1 2 3 4 5 6 7 | def import #...snip tempfile = Tempfile.new( my_filename) tempfile.write open( image_url ).read @imported_user.images.create(:file => tempfile) #...snip end |
This doesn't work. It blows up missing one of two methods:
- #original_filename
- #content_type
If you inspect a normal file upload in Rails which has these methods, you'll find that is just a regular old Tempfile. But it has the methods! If you create a Tempfile manually, it won't have the methods. That's because Rails magics them on. I am not sure why they don't create a subclass like Rails::Tempfile that contains these methods and just use that. I guess its because OOP is retarded (sarcasm).
So, I wrote a little subclass that will take a file path, and quack like the magic'd rails Tempfiles you get from an upload so you can attach local files to models or even remote files.
This works pretty straightforward and I'm currently using it in production. It won't work on windows systems because the dependency on the 'file' binary. Also, some linux systems are missing this library by default, so make sure you yum|dpkg|apt|port or whatever to get it installed.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 | require 'open-uri' require 'digest/sha1' class RemoteFile < ::Tempfile def initialize(path, tmpdir = Dir::tmpdir) @original_filename = File.basename(path) @remote_path = path super Digest::SHA1.hexdigest(path), tmpdir fetch end def fetch string_io = OpenURI.send(:open, @remote_path) self.write string_io.read self.rewind self end def original_filename @original_filename end def content_type mime = `file --mime -br #{self.path}`.strip mime = mime.gsub(/^.*: */,"") mime = mime.gsub(/;.*$/,"") mime = mime.gsub(/,.*$/,"") mime end end |
Usage is pretty simple:
1 2 3 | remote_file = RemoteFile.new("http://www.google.com/intl/en_ALL/images/logo.gif") remote_file.original_filename #=> logo.gif remote_file.content_type #= image/gif |
Using it in your controller:
1 2 3 4 5 | def import #...snip @imported_user.images.create(:file => RemoteFile.new( url_to_image )) #...snip end |
Expiring rails fragement caches using an expires time instead of a sweeper (expires_in) – (a ‘duh’ post)
So, I'm setting up fragment caching for some stuff, and I honestly don't care about setting up an observer to sweep some stuff. I just want to cache this one slow fragment for like 5 minutes. I've seen these tutorials all over the web that shows people using the expires_in setting to auto-expire cache like so:
1 | Rails.cache.write('test_key', 'test_value', :expires_in => 5.minutes) |
That's great and all, but how the hell do I use it for a Page, Action or Fragment cache? Well you just pass the same :expires_in param and yay it works as long as its a mem_cache_store (although, I did see a hackaroo for file_store)
Even the Rails Guide on caching mentions 'expires_in' but doesn't show how to use it.
1 2 3 | class StupidController < ApplicationController caches_page :whatever, :expires_in => 5.minutes end |
What really got me was when I was trying to use it with a fragment cache. I tried this and it didn't work:
1 | - cache(:action => 'home', :action_suffix => 'advertisements', :expires_in => 10.minutes) do |
Why? Well, the cache method takes two hashes, and if you leave off the curly braces, that expires_in ends up in the first one, which is used for the name... To roll it right do:
1 | - cache({:action => 'home', :action_suffix => 'advertisements'}, :expires_in => 10.minutes) do |
Duh, the more you know.
A Rails rake file for compressing your Javascript with YUI
Here is a quick rake file I threw together to use with my Ruby YUI 2.0 (I-refuse-to-make-a-gem-
edition).
This of course should be used with the Ruby YUI Compressor I posted about the other day.
This script will tar/gzip all of your javascripts just incase something stupid happens. So you have a little safety net.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 | require 'fileutils' require 'pathname' namespace :js do desc <<-INFO Output Compressed JavaScript to STDOUT; COMPRESS=ALL to compress all Javascript files that DONT contain ".min." INFO task :yui => :environment do targz() if ENV['COMPRESS'] == 'ALL' Dir[Rails.root.to_s / :public / :javascripts / '**/*.js'].each do |javascript| next if javascript =~ /\.min\./ compress(javascript) end else if File.exist?(Rails.root + "config/yui.yml") javascripts = YAML.load(File.open("config/yui.yml").read)["javascripts"] if !javascripts.blank? javascripts.each {|script| compress(Rails.root + "public/javascripts" + script)} else raise Exception, "No javascript files in config/yui.yml" end else raise Exception, "config/yui.yml Not Found; Do rake js:generate to create one or COMPRESS=ALL to run without a config file" end end end desc "Generate YUI Compressor Config file" task :generate => :environment do config_path = Rails.root + "config/yui.yml" File.open(config_path, "w+") do |f| f.puts <<-CONFIG --- javascripts: - "application.js" - "jquery.js" CONFIG puts config_path end end def targz() tgz_file = "#{Time.now.to_i}.javascripts.tgz" `cd #{Rails.root + "public"}; tar -zcf #{tgz_file} javascripts/` puts "Backed up assets to: #{tgz_file}" end def compress(path) puts "Compressing #{path}" file_handle = File.open(path) compressed_output = YUI.compress_safe file_handle file_handle.close #overwrite the file File.open(path, "w+") { |file| file.puts compressed_output } end end |
Wanna use it?
1. Drop it in your lib/tasks folder or wherever your Rakefile looks
You can compress 'everything' or specific files.
If you want to only compress specific Javascript files do:
1 2 3 | rake js:generate
# Edit your config/yui.yml file
rake js:yui |
If you want to compress "everything" (It actually won't compress files that contain ".min.". You can remove the regexp's if you don't like it.)
1 | rake js:yui COMPRESS=ALL |
Yay, now your raking it in (Pun harhar) w/ your web2.0 yui compressed rails app. Woot woot.
An Excel CSV exporter for ActiveRecord
This is a mix of two blog posts on exporting ActiveRecord data to CSV. This explicitly was designed to export stuff so excel wouldn't freak out.
This strips new lines from any string field, set the BOM and converts the encoding to utf16.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 | require "fastercsv" require "iconv" # Excel info from: http://blog.plataformatec.com.br/2009/09/exporting-data-to-csv-and-excel-in-your-rails-app/?utm_source=feedburner&utm_medium=feed&utm_campaign=Feed:+PlataformaBlog+(Plataforma+Blog) # Original post: http://www.brynary.com/2007/4/28/export-activerecords-to-csv # Usage: # Given User is an ActiveRecord model # # Options for Model.to_csv, Array.to_csv, and ActionController::Base#send_csv # # :only - Array, trumps :exclude. Only these attributes will be included instead of all attributes (things listed in :methods will still be sent) # :only => [ :id, :name, :created_at ] # :methods - Array, additional methods to evaluate and add to the CSV response. Note: you can send nested method calls # :methods => [ :to_s, :complex_method, "my.method.on.a.related.object"] # :exclude - Array, attributes to exclude from CSV result # # From ActionController # send_csv User, :only => [:first_name, :email, :created_at] #This will do all records # send_csv User.all(:conditions => ["created_at > ?", some_date]), :only => [:first_name, :email, :created_at] # # From ActiveRecord Model # User.to_csv :only => [ :username, :email, :created_at ], :methods => [ :age, "account.id"] # All users additionally get their related Account#id number # User.all(:limit => 10).to_csv :exclude => [:password, :birthdate] # class ActiveRecord::Base # Shortcut for CSV of whole table def self.to_csv(*args) find(:all).to_csv(*args) end # Get the column headers def self.csv_columns(options={}) tmp_columns = if options[:only] options[:only] #only trumps exclude else self.content_columns.map{|curr_col| curr_col.name } - options[:exclude].map{|curr_col| curr_col.to_s } end tmp_columns + options[:methods].map{|curr_col| curr_col.to_s } end # Record to a row level csv array def to_csv(options={}) self.class.csv_columns(options).map { |curr_col| curr_col = curr_col.to_s #Its a chain of method calls, on intermediary nil, just return nil if !curr_col.index(".") col_val = self.send(curr_col) else col_val = self curr_col.split(".").each {|curr_method| col_val = col_val.send(curr_method) unless col_val.nil?} end col_val.gsub!("\n", " ") if col_val.is_a?(String) # Strip newlines, Appease Excel Gods col_val } end end class Array # Convert an array of objects into a CSV String. def to_csv(options = {}) column_options = { :only => options.delete(:only), :exclude => options.delete(:exclude) || [], :methods => options.delete(:methods) || [] } options = { :col_sep => "\t" # Appease Excel Gods }.merge(options) if all? { |e| e.respond_to?(:to_csv) } header_row = first.class.csv_columns(column_options).to_csv content_rows = map { |e| # Get all the values of non-excluded rows e.to_csv(column_options) }.map{|r| # Call to_csv on the array of row-level values, this will join them into a CSV row r.to_csv(column_options) } ([header_row] + content_rows).join else FasterCSV.generate_line(self, options) end end end # module for extending ActionController module ExcelCSVExporter BOM = "\377\376" #Byte Order Mark, Appease Excel Gods def send_csv(kollection, options={}) filename = options.delete(:filename) || I18n.l(Time.now, :format => :short) + ".csv" content = kollection.to_csv(options) # Appease Excel Gods content = BOM + Iconv.conv("utf-16le", "utf-8", content) send_data content, :filename => filename end end ActionController::Base.send :include, ExcelCSVExporter |
Wanna use it?
1 2 3 4 5 6 7 | class MyCoolController < ActionController::Base def index respond_to {|want| want.csv{ send_csv MyCoolModel.all, :exclude => [:secret_column], :methods => [:complex_method, "related.table.method"]} } end end |