Passing a block into a class_eval

Here’s a function similar to attr_accessor, except it takes a block,
which is used to validate assignments to the attribute, and substitute
in a string value to be used as a key to the hash inside class_eval.

It’s just ugly having to use a hash to store the block. class_eval and
friends should support saving parameters into global variables for the
duration? Or is there an obvious way of doing this that I’ve missed?
I removed some checking for simplicity.

Clifford H…

def validated_attr(names, &block)
begin
Thread.critical = true
$__VALIDATOR_BLOCK ||= {}
while $__VALIDATOR_BLOCK.has_key?([r = (rand
16777216).to_i]);
end
$__VALIDATOR_BLOCK[r] = block
ensure
Thread.critical = false
end

names.each{|name|
    class_eval <<-END
        def #{name}
            @#{name}
        end
        def #{name}=(__val)
            if !$__VALIDATOR_BLOCK[#{r.to_s}.to_i].call(__val)
                throw "Invalid assignment of \#{__val.inspect} to 

#{name}"
end
@#{name} = __val
end
END
}
end

class Foo
validated_attr(:bar) {|v| v.kind_of?(Integer) &&
(0…14).include?(v)}
end

f = Foo.new

begin
f.bar = 2 # Ok
rescue => e
puts e.to_s
end

begin
f.bar = 15 # Not ok
rescue => e
puts e.to_s
end

begin
f.bar = “Hi there” # Not ok
rescue => e
puts e.to_s
end

On 2/16/07, Clifford H. [email protected] wrote:

Here’s a function similar to attr_accessor, except it takes a block,
which is used to validate assignments to the attribute, and substitute
in a string value to be used as a key to the hash inside class_eval.

Ruby 1.9 accepts blocks as parameters to blocks.

-austin

Austin Z. wrote:

Ruby 1.9 accepts blocks as parameters to blocks.

I can’t immediately see how that solves this problem…?
I need to create a string to eval so I can fabricate the
method names, and I want a block to be available within
that eval. Can you show a quick example?

[email protected] wrote:

I guess my first take on it would be this:
(using define_method rather than def so that the local variable block
will still be in scope).

Excellent! I added the missing instance_variable_set and I’m away.
My full example has type checking, and whether nil is allowed.
Oh, and type-checked array attributes in the same fashion.
Will post soon… or is there an existing package to add this to?

class Foo
MAX = 14
typed_attr Integer, nil, :foo {|v| v <= MAX }
array_attr Range, :bar {|v| v.end+(v.exclude_end? ? 0 : 1) < MAX }
end

Clifford H…

Hi –

On Sat, 17 Feb 2007, Clifford H. wrote:

Austin Z. wrote:

Ruby 1.9 accepts blocks as parameters to blocks.

I can’t immediately see how that solves this problem…?
I need to create a string to eval so I can fabricate the
method names, and I want a block to be available within
that eval. Can you show a quick example?

I guess my first take on it would be this:

class Module
def validated_attr(*names, &block)
names.each do |name|
define_method(name) { instance_variable_get("@#{name}") }
define_method("#{name}=") {|val|
unless block.call(val)
raise “Invalid assignment of #{val.inspect} to #{name}.”
end
}
end
end
end

(using define_method rather than def so that the local variable block
will still be in scope).

David

Hi –

On Sat, 17 Feb 2007, Clifford H. wrote:

[email protected] wrote:

I guess my first take on it would be this:
(using define_method rather than def so that the local variable block
will still be in scope).

Excellent! I added the missing instance_variable_set and I’m away.

Whoops, I knew it looked too short :slight_smile:

My full example has type checking, and whether nil is allowed.

I’ll bet you mean class checking :slight_smile: But that’s another (long)
story…

David

Of course, that should be:

class Foo
MAX = 14
typed_attr(Integer, nil, :foo) {|v| v <= MAX }
array_attr(Range, :bar) {|v| v.end+(v.exclude_end? ? 0 : 1) < MAX }
end

Can’t use a block without parenthesizing the params…

Hi –

On Sat, 17 Feb 2007, Clifford H. wrote:

Of course, that should be:

class Foo
MAX = 14
typed_attr(Integer, nil, :foo) {|v| v <= MAX }
array_attr(Range, :bar) {|v| v.end+(v.exclude_end? ? 0 : 1) < MAX }
end

Can’t use a block without parenthesizing the params…

You can, actually:

irb(main):013:0> def x(*args); p args; yield; end
=> nil
irb(main):014:0> x 1,2 do puts “hi” end
[1, 2]
hi

:slight_smile:

David

[email protected] wrote:

My full example has type checking, and whether nil is allowed.
I’ll bet you mean class checking :slight_smile: But that’s another (long)
story…

It is. class checking is a limited form of type checking however, and
you can omit class checking and/or let the block can do whatever extra
type checking is needed. :slight_smile: It’s nice to automatically allow/disallow
assignment of nil also.

Thanks for the note on do…end, I almost never use it, but this is a
rare case where having the same feature at a different precedence is
a good idea.

This is ready to be made a gem when I’ve done the unit tests. I think
I’ll call it “checked”. I use the “method as superclass” magic to
create checked subclasses of Array, and support both simple attributes
(with a default value) and array attributes. Hmm, perhaps I should do
hashes also… later :-).

Clifford H…

[email protected] wrote:

This is ready to be made a gem when I’ve done the unit tests. I think
I’ll call it “checked”.
How about “chattr”? :slight_smile:

Thanks for the suggestion.

Chattr is in Rubyforge now, with RDoc, (new) RSpec tests, all tied up
with a nice Rakefile to build the gem and test it. Only SVN access,
I’ll get a proper release file and announcement up in the next few
days. You might like a sneak peek though :-).

Clifford H…

Hi –

On Sun, 18 Feb 2007, Clifford H. wrote:

Thanks for the note on do…end, I almost never use it, but this is a
rare case where having the same feature at a different precedence is
a good idea.

I need to stash that example away as it’s rare enough that I never
seem to be able to remember an example when people ask me…

This is ready to be made a gem when I’ve done the unit tests. I think
I’ll call it “checked”.

How about “chattr”? :slight_smile:

David

On 3/1/07, Clifford H. [email protected] wrote:

[email protected] wrote:

This is ready to be made a gem when I’ve done the unit tests. I think
I’ll call it “checked”.
How about “chattr”? :slight_smile:

Thanks for the suggestion.

Chattr is in Rubyforge now, with RDoc, (new) RSpec tests

Right on! Nice to see someone using the rspec-0.8 syntax only a day
after its release. And, if I might say so, “nice specs”. Take it as
you will.

Cheers,
David

David C. wrote:

Chattr is in Rubyforge now, with RDoc, (new) RSpec tests
Right on! Nice to see someone using the rspec-0.8 syntax only a day
after its release. And, if I might say so, “nice specs”.

Thanks! The new Rspec makes nice tests easier :-).

I’ve done the gem version change, thanks, it’ll get checked in after I
resolve the following things:

Do still need the runtest.rb at all if I rename the directory “test”?

What about the Rakefile entries for runtest?

How does this get into the gem repository - automatically? Otherwise…?

What about rubyforge “released files” - I should upload a pre-built gem
and release notes I assume?

Any comments on the RDoc?

I’m still getting the hang of this. I have some much bigger things in
the
offing, so I need to get it right. I’m just grabbing examples from
anywhere
and hacking 'til it seems to work… which isn’t great. There should be
a
quick-start guide with an example that stays up-to-date with the
latest…

Clifford H…

On 3/1/07, David C. [email protected] wrote:

Right on! Nice to see someone using the rspec-0.8 syntax only a day
after its release. And, if I might say so, “nice specs”. Take it as
you will.

Cheers,
David

Actually - I see that chattr_spec.rb is in the test directory. If you
make that a spec directory instead, then tinderbox will, in theory,
know what to do with it.

Also - the syntax you’re using won’t work with anything before 0.8.0,
so I’d make the gem statements reflect that instead of “> 0”.

gem ‘rspec’, “>= 0.8.0”

Cheers,
David

On Fri, 2007-03-02 at 13:25 +0900, Clifford H. wrote:

How does this get into the gem repository - automatically? Otherwise…?

What about rubyforge “released files” - I should upload a pre-built gem
and release notes I assume?

Yup, it’ll get deployed to the main gem index when you release the gem
via the RubyForge “released files”.

Yours,

Tom

This forum is not affiliated to the Ruby language, Ruby on Rails framework, nor any Ruby applications discussed here.

| Privacy Policy | Terms of Service | Remote Ruby Jobs