Binding scope

Hi,

I'm trying to implement a simple parser and quite likely going about it
the wrong way.

I would like to be able to include a bit of ruby code in a string, which
is saved to a field in a database. When the string is retrieve from the
database at a later stage, I want to parse it for code and evaluate the
code, substituting the result value into the string.

I've been trying to do something like this:

module Substitutable
  @other_word = "other word"
  proc = Proc.new{}
  String.class_eval do
    define_method :to_s do
      self.gsub(/<code>(.*?)<\/code>/) do |match|
        code = match.scan(/<code>(.*)<\/code>/).flatten.first
        eval(code, proc.binding)
      end
    end
  end
end

# If I then create a class and include Substitutable, I get the
following output

class Tester

  include Substitutable

  def say
    @word = "hello"
    '<code>@other_word</code>'.to_s # => "other word"
    '<code>@word</code>'.to_s # => ""
    '<code>"hard coded word"</code>'.to_s # => "hard coded word"
  end
end

Tester.new.say

So I know that conceptually passing the a proc to the eval method does
enable me to set an instance variable out of the scope of the new to_s
method. But I cannot work out how to get the scope of the class within
which Substitutable has been "include"d. I've tried various things
like...

a) creating an "proc" method within the class that returns a proc object
b) creating a "@proc" instance variable within the class

...but with no luck.

Is what I'm trying to do possible?

Thanks very much in advance...

Dave

···

--
Posted via http://www.ruby-forum.com/.

Hi,

Thought I'd try one poke to see if I could resurrect this. I'm still
stumped on how to solve this.

Thanks in advance.

Regards,
Dave

···

--
Posted via http://www.ruby-forum.com/.

Dave Birch wrote:

Hi,

I'm trying to implement a simple parser and quite likely going about it
the wrong way.

I would like to be able to include a bit of ruby code in a string, which
is saved to a field in a database. When the string is retrieve from the
database at a later stage, I want to parse it for code and evaluate the
code, substituting the result value into the string.

You are aware of the serious security problems with this, right?

[...]

But I cannot work out how to get the scope of the class within
which Substitutable has been "include"d.

Perhaps you should use the Module#included callback to register it.

Best,

···

--
Marnen Laibow-Koser
http://www.marnen.org
marnen@marnen.org
--
Posted via http://www.ruby-forum.com/\.

Hi Marnen

Thanks very much for the reponse.

Marnen Laibow-Koser wrote:

Dave Birch wrote:

You are aware of the serious security problems with this, right?

Yes, this isn't a public-facing site, and there will be no front-end
access to the database that will generate and store the code.

[...]

But I cannot work out how to get the scope of the class within
which Substitutable has been "include"d.

Perhaps you should use the Module#included callback to register it.

I will look at this - thanks again for the tip.

Regards,
Dave

···

Best,
--
Marnen Laibow-Koser
http://www.marnen.org
marnen@marnen.org

--
Posted via http://www.ruby-forum.com/\.

Dave Birch wrote:

Hi Marnen

Thanks very much for the reponse.

Marnen Laibow-Koser wrote:

Dave Birch wrote:

You are aware of the serious security problems with this, right?

Yes, this isn't a public-facing site, and there will be no front-end
access to the database that will generate and store the code.

Then you shouldn't need to do this in the first place. If there's no
frontend access to the DB, presumably that means that the app itself is
generating the code. If so, then it can keep it as a method in a live
object rather than writing it to the database and opening up a security
hole.

[...]

But I cannot work out how to get the scope of the class within
which Substitutable has been "include"d.

Perhaps you should use the Module#included callback to register it.

I will look at this - thanks again for the tip.

Regards,
Dave

Best,
--
Marnen Laibow-Koser
http://www.marnen.org
marnen@marnen.org

Best,

···

--
Marnen Laibow-Koser
http://www.marnen.org
marnen@marnen.org
--
Posted via http://www.ruby-forum.com/\.

Marnen Laibow-Koser wrote:

Dave Birch wrote:

Hi Marnen

Thanks very much for the reponse.

Marnen Laibow-Koser wrote:

Dave Birch wrote:

You are aware of the serious security problems with this, right?

Yes, this isn't a public-facing site, and there will be no front-end
access to the database that will generate and store the code.

Then you shouldn't need to do this in the first place. If there's no
frontend access to the DB, presumably that means that the app itself is
generating the code. If so, then it can keep it as a method in a live
object rather than writing it to the database and opening up a security
hole.

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 <code>@results.size</code>
and they spent <code>@results.collect(&:spend).sum</code> on your
product this year".

Doees this make sense?

···

[...]

But I cannot work out how to get the scope of the class within
which Substitutable has been "include"d.

Perhaps you should use the Module#included callback to register it.

I will look at this - thanks again for the tip.

Regards,
Dave

Best,
--
Marnen Laibow-Koser
http://www.marnen.org
marnen@marnen.org

Best,
--
Marnen Laibow-Koser
http://www.marnen.org
marnen@marnen.org

--
Posted via http://www.ruby-forum.com/\.

Check with a rails group, but I'm pretty sure you'll find that you can,
in fact, do this natively in rails without the security hole.

-s

···

On 2010-01-15, Dave Birch <thisisdaveb@gmail.com> 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).

--
Copyright 2010, all wrongs reversed. Peter Seebach / usenet-nospam@seebs.net
| Seebs.Net <-- lawsuits, religion, and funny pictures
Fair game (Scientology) - Wikipedia <-- get educated!

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 CREATE TABLE
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
              );

···

On Fri, Jan 15, 2010 at 8:42 AM, Dave Birch <thisisdaveb@gmail.com> 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 <code>@results.size</code>
and they spent <code>@results.collect(&:spend).sum</code> on your
product this year".

              __________________________________________________

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 ActiveRecord::Base
# for other (better, depending on your needs) documentation, see
Active Record Query Interface — Ruby on Rails Guides
#
Active Record Associations — Ruby on Rails Guides
#
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' }

Hmm, formatting looks really bad up on my email, here is a pastie for it
http://pastie.org/private/taieshtr0nwhnv1unkrjq

Josh, thanks VERY much for the response. I'm going to digest it and
will let you know once I've got it working.

Have a good weekend.

Cheers,
Dave

Josh Cheek wrote:

···

Hmm, formatting looks really bad up on my email, here is a pastie for it
http://pastie.org/private/taieshtr0nwhnv1unkrjq

--
Posted via http://www.ruby-forum.com/\.

Dave Birch wrote:

Josh, thanks VERY much for the response. I'm going to digest it and
will let you know once I've got it working

No. Better to go for a solution without the security hole. It should be
possible, but ask on the Rails list instead.

Have a good weekend.

Cheers,
Dave

Josh Cheek wrote:

Hmm, formatting looks really bad up on my email, here is a pastie for it
http://pastie.org/private/taieshtr0nwhnv1unkrjq

Best,

···

--
Marnen Laibow-Koser
http://www.marnen.org
marnen@marnen.org
--
Posted via http://www.ruby-forum.com/\.