-
- Using Presenters in Rails
-
Sitting thrugh my first tutorials session at RailsConf we’ve come across an interesting discussion about the philosophy of using the ‘Presenter Pattern’ to refactor code in your applications controller.
-
- Ruby: Tax Estimator
-
I got bored a couple of weeks ago and wanted to figure out how much I’d be paying in taxes depending on how much I made. I wrote this little script in Ruby to give me a basic estimate of how much I’d owe. You can use it at your own risk or make it better and share it with me!
Here is the source:
# -------------------------------------------------------------------- # Amounts to analyze taxes for. # -------------------------------------------------------------------- amounts = [40000, 50000, 60000] pto = 80 # Specify amount of hours as integer # -------------------------------------------------------------------- # Tax schedule for 2006 supplied by IRS # (http://www.irs.gov/formspubs/article/0,,id=150856,00.html) # # Arizona tax information obtained from # (http://dab.nfc.usda.gov/pubs/docs/taxformulas/formulas/statecitycounty/taxaz/taxaz.html) # -------------------------------------------------------------------- tax_rates = [0.1, 0.15, 0.25, 0.28, 0.33, 0.35] cutoff_levels = [0, 7550, 30650, 74200, 154800, 336550] pre_tax_balances = [0, 755, 4220, 15107.5, 37675.5, 97653] az_tax_rates = [0.1,0.19] az_cutoff = 10000 work_hours = 52*40-pto # -------------------------------------------------------------------- # Extend the numeric clas to accomodate formatting. # -------------------------------------------------------------------- class Numeric def to_currency sprintf("$%0.2f", self) end def to_percent sprintf("%d%", self*100) end end # -------------------------------------------------------------------- # Calculate the amounts. # -------------------------------------------------------------------- for amount in amounts # Determine the federal witholding bracket index = 0 for cutoff in cutoff_levels if amount > cutoff then bracket = index end index += 1 end # Determine the state tax withholding percentage (amount < az_cutoff) ? az_index = 0 : az_index = 1 # Calculate withholding amounts and remaining income tax = (amount-cutoff_levels[bracket])*tax_rates[bracket] + pre_tax_balances[bracket] az_tax = tax * az_tax_rates[az_index] income = amount - tax - az_tax # Print out the results puts "---------------------------------------------------------------" puts "Income Analyses for #{amount.to_currency} w/ #{pto} hours of paid time off " puts "---------------------------------------------------------------" puts "This level of income would put you in the level #{bracket+1} tax bracket which charges #{tax_rates[bracket].to_percent}." puts "In addition this income qualifies for the #{az_tax_rates[az_index]} state income tax level." puts "Total income: #{income.to_currency} after #{tax.to_currency} in federal tax expenses and #{az_tax.to_currency} in state tax expenses.." puts "------------------------------\nPost Tax Income\n------------------------------" puts "Monthly income: #{(income/12).to_currency}" puts "Bi-Weekly income: #{(income/24).to_currency}" puts "------------------------------\nHourly Income\n------------------------------" puts "You are going to work #{work_hours} hours this year." puts "That will make up #{(work_hours.to_f/(365*12)).to_percent} percent of your active time." puts "Before tax your hourly income is #{(amount / work_hours).to_currency} taking paid time off into account." puts "After tax your hourly income is #{(income / work_hours).to_currency} taking paid time off into account." puts "\n\n" end
-
- Ruby: Working with files and directories.
-
I wrote this snippet today to cycle through the present working directory and do some cleaning up so to speak. We got a ton of images from one of our clients and they all need to be setup as galleries for slideshow pro in flash. So I needed a few things to be done.. first of all the images were all conveniently placed in their own directories for each album, however, the thumbnails were stored together with the large photo files so I needed to separate them from each other. I wrote this snippet… my first time writing a ruby script to do some nifty stuff with directories and files quickly. This will actually rename all of the directories to be more web friendly i.e. ‘Bay Area’ becomes ‘bay_area’ etc. etc. and then it will rename all of the images uniformly but place thumbnails in a tn subdirectory and large images in an lg subdirectory. Finally, it also generates a SSP friendly XML doc for each directory.
# Gallery creator.. # This ruby file should be placed into the working directory # containing all of the directories you wish to be converted # into galleries. # USER DEFINED SETTINGS # ---------------------------- # ignore: # Array of directories you wish to ignore.. i.e. parent, and current. ignore = ['.','..'] # large_id: # A string used to identify images to be moved into the large directory. large_id = "555" # small_id: # A string used to identify images to be moved into the thumbnail directory. small_id = "72" # SCRIPT BEGINS: # Don't change unless you really want to. # ---------------------------- # Initialize directory object... directory = Dir.new(Dir.pwd) # Cycle through directory directory.each do |d| # Only go through directories and ignore directories specified by user. unless File.ftype(d) != "directory" or ignore.include?(d) # Jump into directory and parse files. Dir.chdir(d) Dir.foreach(Dir.pwd) do |f| # Create directories for thumbnails or large images. %w(tn lg).each do |newdir| unless File.exists?(newdir) Dir.mkdir(newdir) end end # Cycle through files. unless File.ftype(f) != "file" # Create new file name and set target directory. if f =~ /#{large_id}/ newname = f.split(large_id.to_s).each{|i| i.downcase!}.join("") newdir = "lg" elsif f =~ /#{small_id}/ newname = f.split(small_id.to_s).each{|i| i.downcase!}.join("") newdir = "tn" else newdir = nil end # Open the old file... oldfile = File.new(f) # Jump to large directory if possible. unless newdir.nil? Dir.chdir(newdir) # Write new file if it doesn't exist. unless File.exists?(newname) File.open(newname,"w+") do |n| n.write(oldfile.read) end end # Move back and delete the old file. Dir.chdir("../") File.delete(f) end # Log event. puts "Renamed: #{f} to #{newname} moved to '#{newdir}'" end end # Return to parent Dir.chdir("../") # Rename to web friendly urls. newdir = d.split(" ").each{|i| i.downcase!}.join("_") File.rename(d,newdir) # Build the XML docs. xml = "< ?xml version=\"1.0\" encoding=\"UTF-8\"?>\n\n dir_for_xml = Dir.new("#{newdir}/lg/") dir_for_xml.each do |filename| unless ignore.include?(filename) then xml += "\n" \n" end end xml += "\n" File.open("#{newdir}.xml","w") do |n| n.write(xml) end end end
In my case it’s important to note that the files were grouped together with names like: ‘torrey-ridge555_1.jpg’ and ‘torrey-ridge72_1.jpg’. Detecting the ‘555′ or ‘72′ safely solved the problem in my case ofcourse there could be some situations where something this easy could backfire. To make the file names uniform I simply split the file by the identifier and joined it back together.
-
- Rails: What’s the best way to handle my lists?
-
Ok so I’m having a slight problem building my CMS for a client. I want to utilize the same model to handle the photos for their case studies as well as their services. Each has their own model. So the Photo model belongs to a Project or a Service. But in reality it belongs to a project OR a service. Thus I have a problem using acts_as_list.
class Photo < ActiveRecord::Base belongs_to :service belongs_to :project acts_as_taggable acts_as_list :scope => :project_id file_column :image, :magick => { :versions => { "thumb" => "50x50", "medium" => "640x480>" } } endIf I could do something like:
acts_as_list :scope => :project_id || :service_id
Then this would be a piece of cake but it’s not that easy. How would you solve this? I know it’s not that challenging.
-
- Creating Excel Documents with Ruby On Rails
-
Generating an excel document is not so difficult to achieve but limited unfortunately. You cannot write formulas or generate charts via the existing available tools but you can achieve usable results overall. Here is a snippet I used to generate sales forecasts involving data pulled together from two active record objects. Here is the gem you’ll need.
- Update: the link I was posting to prior points to a new Ruby project. The correct link can be found here.
# Builds an excel report. def report # Grab time span for report get_span # Define stats levels to include. status = %w(high medium low lost won) # Create workbook. file = "#{session[:user_id]}_#{Time.now.strftime("%m%d%G%s")}_forecast.xls" workbook = Excel.new("#{RAILS_ROOT}/reports/#{file}") heading = Format.new( :color => "green", :bold => true, :underline => true ) data = Format.new( :color => "black", :bold => false, :underline => false ) workbook.add_format(heading) workbook.add_format(data) # Cycle through each status level status.each do |status| start_column, start_row = 2, 3 worksheet = workbook.add_worksheet(status) opportunities = get_opportunities_that_are(status) #Cycle through the opportunities row = start_row totals, dates = [], [] for opp in opportunities worksheet.write(row,start_column,opp.client,heading) column = start_column + 1 opp.find_forecasts_within(@span[0],@span[-1]).each do |i| worksheet.write(row,column,i.volume,data) totals[column] = i.volume + totals[column].to_i dates[column] = i.date.strftime("%b '%y") column += 1 end row += 1 end # Generate the totals row and monthly headings column = start_column+1 @span.length.times do worksheet.write(row,column,totals[column],heading) worksheet.write(start_row-1,column,dates[column],heading) column += 1 end end workbook.close redirect_to :action => 'show' end
Tagged ruby
- Improve a Snippet: Make a better search function.
-
UPDATED: May 16th - I coded this search app a while back. It’s not the greatest but it works. Can you make it better? Right now it just takes a field and a phrase. The phrase is parsed. It then generates SQL. You could write a separate parsing function separately which could return the array that feeds this function. You could also rewrite this function to support an array of fields. Take your best stab. I’ll post the final snippet with everyone’s input.
# Advanced search function accepts a string split apart by whitespace. Accepts a field name # and a phrase. Splits the phrase by whitespace into separate terms. If the string is quoted # the terms are considered to be required together. If not terms are assumed to be either or. # This code is far from perfect please feel free to improve it to support , deliminations and # what not. # Snippet written by J. Jeffers. Copyright 2006 / I share freely. def advanced_search(fields,phrases) # Check to see if what type of search is required. If the phrase is "quoted" we will # use AND to require all terms. Otherwise, we will use OR to find any matches. if phrases =~ /"[^"\r\n]*"/ glue = ' AND ' else glue = ' OR ' end # Break the list down from a string into a smart array of useable terms. list = clean_up(phrases).split(/\s/) discreet_list = [] results = [] count = 0 discreet_count = 0 # Cycle through every item in the list. for item in list # Check to see if the phrase begins with a !exclamation point. If so we will negate # this term to find terms that do not match if item =~ /!\w*/ item.gsub!(/!/,'') if item =~ /BLANK/ fields.each do |field| results[count] = "#{field} != ''" count += 1 end else fields.each do |field| results[count] = "#{field} NOT LIKE '#{item}'" count += 1 end end # Check for wildcards in the phrase... elsif item =~ /!%\w*|\w*%/ fields.each do |field| results[count] = "#{field} LIKE '#{item.to_s}'" count += 1 end # Check for a plain blank request... elsif item =~ /BLANK/ discreet_list[discreet_count] = '' discreet_count += 1 # Otherwise just dump the list into an array of absolute matches... else discreet_list[discreet_count] = item.to_s discreet_count += 1 end end # Compile the discreet list... (discreet_list.length > 1) ? list = discreet_list.join("','") : list = discreet_list.to_s unless list.length < 1 fields.each do |field| results[count] = "#{field} IN ('#{list}')" count += 1 end end # Compile the final SQL string... return results.join(glue).to_s end
- I’m on Rails: Coding Statistics.
-
I recently coded a basic statistics applicaiton for a client that offers a website affiliate program. I thought it might be worth sharing how easy it is to code this type of software in Rails. Keep in mind this is extremely basic but never the less a good illustration of how to tackle a cumbersome project relatively easily.
Decide what to track.
For this client all we needed to track were the number of actual page impressions, the amount of clicks, and the actual amount of unique visitors going to a specific users account.
Playing Big Brother.
Watching visitor activity and logging it is easy. I created a model for hits, clicks, and visits. There is a parent model to all of these called ‘User’. So here’s how I broke down the relationships:
- Hits belong to users.
- Visits belong to users.
- Clicks belong to both users and visitors.
Below is the code that logs the click through information and sends the user off on their happy way. It basically takes an id and a path so a typical link would look like: http://www.domain.com/send_to/id/encoded_url_path_to_redirect_to
def send_to if @user = User.find(:first, :conditions => ["name=?",@params[:id].to_s.upcase]) # Get parameter info... url = params[:path].to_s ip = request.remote_ip # Log the click information. click = Click.new click.url = url @user.clicks < < click # Log the visitor information. visitor = Visitor.create_if_new_today(ip,@user.id) visitor.clicks < < click # Log the hit. hit = Hit.new @user.hits < < hit if @user.save redirect_to url end endIt should be worth noting that I’ve decided to track where every click is redirecting too. I’ve also decided to track every click as a hit for the user as well as technically it is an impression.
Logging unique visitor information as well as the page impression itself (the hit) is easy too as seen below.
def index if params[:id] == 'nonexistent' params[:id] = 'admin' end if @user = User.find(:first, :conditions => ["name=?",params[:id].to_s.upcase]) # Log the hit. hit = Hit.new @user.hits < < hit # Log the visitor information. ip = request.remote_ip visitor = Visitor.create_if_new_today(ip,@user.id) @user.visitors < < visitor @time = Time.now @user_id = params[:id].to_s.upcase else redirect_to(:controller => 'show', :id =>'nonexistent') end endGrabbing the stats.
Once you’ve logged the stats pulling them via rails is a piece of cake. I wrote a protected function inside of my report controller to pull the hits, clicks, and visits for a specific time length and return them in a hash.
# Display user stats. def report @time = Time.now @user = User.find(session[:user_id]) @todays = stats(@user,1.days.ago) @last_weeks = stats(@user,1.weeks.ago) @last_months = stats(@user,4.weeks.ago) @records = transactions end protected # Generates a hash containing hits, visitors, and clicks from the past. def stats(user,time) clicks = user.clicks.find(:all, :conditions => ["created_on > ?", time]) hits = user.hits.find(:all, :conditions => ["created_on > ?", time]) visitors = user.visitors.find(:all, :conditions => ["created_on > ?", time]) return { :hits => hits, :visitors => visitors, :clicks => clicks } end
- Ruby: Extending classes and method chaining.
-
Once you start fooling around with the Ruby language you really do get hooked on just how powerful it is. I was disappointed when I first found out that the String.capitalize function only formatted the very first letter of a string to a capital letter as this is undesirable when you are formatting something like a street address. Not to worry though, extending the String class is easy in ruby and writing the function to do it requires only one line of code!
class String def capitalize_each self.split(" ").each{|word| word.capitalize!}.join(" ") end def capitalize_each! replace capitalize_each end end puts "hello WORLD!".capitalize_each #=> "Hello World!" s = "6825 W. GALVESTON ST." puts s.capitalize #=> "6825 w. galveston st." puts s.capitalize_each #=> "6825 W. Galveston St." puts s #=> "6825 W. GALVESTON ST." s.capitalize_each! puts s #=> "6825 W. Galveston St."Pretty nifty eh? If any one can show me how to rewrite the function as a destructive function please comment because I’m stumped.
Update:
Thanks to Assaf’s comment I was able to write the destructive version of capitalize each.
- Ruby: Creating destructive methods.
-
Here’s a basic example of using Ruby to create a class with destructive methods. Though this is not as practical as just typing the actual mathematic operations themselves. It does illustrate how destructive functions work and how to write them. Note the ‘return self’ in each of the destructive methods. This allows you to perform the method chains, without it the method returns a fixnum by default and the method chaining raises an error. If anyone knows of a DRY way to perform this let me know.
class Calculator def initialize(value) @value = value end def multiply!(value) @value *= value return self end def add!(value) @value += value return self end def clear! @value = 0 return self end def calculate puts @value end end c = Calculator.new(10) c.add!(2).multiply!(4).calculate #=> 48 c.clear!.add!(5).multiply!(4).calculate #=> 20
- Easy File Uploads with Ruby on Rails.
-
This is nifty… check out this file_column behavior for Ruby on Rails. Wow that looks damn easy.