From: Josh Cheek Date: 2010-01-16T02:53:33+09:00 Subject: Re: Binding scope --000e0cd1549e69fda5047d37ae9c Content-Type: text/plain; charset=ISO-8859-1 On Fri, Jan 15, 2010 at 8:42 AM, Dave Birch wrote: > To give you a bit of background to this - I've got a "macro" that is > generating a bunch of reports. The reports contain lines like "the > number of people in this group are X and they have spent a total of Y on > your product this year". The problem is, I can't substitute X and Y for > their real values at the time the macro runs, because I don't know what > filters the user will apply when viewing the reports. So I need a way > to do the calculations and substitute in the values when the user views > the report, and to recalculate these each time the user changes the > filter (e.g. show me only people older than 30). > > So unless I'm missing something, I can't keep a live object. > > It's a rails app, and the controller will be pulling together a > "@results" variable, but will not necessarily know what kinds of stats > need to be pulled from it. That's why i'd like to be able to do things > like "the number of people in this group are @results.size > and they spent @results.collect(&:spend).sum on your > product this year". > > I don't really see the benefit of storing the template in the database, but here is what I came up with. require 'rubygems' require 'activerecord' require 'sqlite3' require 'erb' # documentation at http://www.sqlite.org/lang_createtable.html db = SQLite3::Database.new( 'store_and_execute_strings.db' ) db.execute <<-__________________________________________________ CREATE TABLE IF NOT EXISTS reports ( id INTEGER PRIMARY KEY ASC AUTOINCREMENT, title Text, template TEXT ); __________________________________________________ db.execute <<-__________________________________________________ CREATE TABLE IF NOT EXISTS groups ( id INTEGER PRIMARY KEY ASC AUTOINCREMENT, name STRING ); __________________________________________________ db.execute <<-__________________________________________________ CREATE TABLE IF NOT EXISTS people ( id INTEGER PRIMARY KEY ASC AUTOINCREMENT, group_id INTEGER, money_spent DOUBLE, age INTEGER ); __________________________________________________ # documentation at http://api.rubyonrails.org/classes/ActiveRecord/Base.html # for other (better, depending on your needs) documentation, see http://guides.rubyonrails.org/active_record_querying.html # http://guides.rubyonrails.org/association_basics.html # http://guides.rubyonrails.org/activerecord_validations_callbacks.html ActiveRecord::Base.establish_connection :adapter => 'sqlite3' , :host => 'localhost' , :database => 'store_and_execute_strings.db' , :username => 'root' , :password => '' class Report < ActiveRecord::Base def generate( &init ) dup.instance_eval { instance_eval &init if init ERB.new( template , 0 , "%<>" ).result( binding ) } end end class Group < ActiveRecord::Base has_many :people end class Person < ActiveRecord::Base belongs_to :group end # here is an example, as I understand your criteria text_for_report = "the number of people in this group are <%= @people.length %> and they have spent " \ "a total of $<%= @people.inject(0) { |sum,person| sum+person.money_spent.to_f } %> " \ "on your product this year" Report.new( :template => text_for_report , :title => 'Customer Summary' ).save doctors = Group.new :name => 'doctors' doctors.save (1..10).each do |num| money , age = num , 20+num*5 doctors.people.build( :money_spent => money , :age => age ).save end customer_summary = Report.find_by_title 'Customer Summary' puts customer_summary.generate { @people = doctors.people } puts customer_summary.generate { @people = doctors.people.all :conditions => 'money_spent <= 5' } puts customer_summary.generate { @people = doctors.people.all :conditions => 'age < 40' } --000e0cd1549e69fda5047d37ae9c--