I really love things in Ruby on Rails like: class Blog < ActiveRecord::Base has_many :comments end Particularly, I love the fact that in Ruby, you can do meta-programming to define has_many in a way that extends the class accordingly. This allows you to build a language for the development environment you are working with. When I found out that has_many was simply a function call that added functions and relations to the class, I went to irb and typed: class Test attr_reader :foo def something(x) @foo = x end something "bar" end a = Test.new puts a.foo Fully expecting to see "bar". Instead, I saw "NoMethodError: undefined method `something'' for Test:Class". Exploring Rails source code, I found that this meta-programming feature of Ruby is not as easy to create as I thought. My dream is that Ruby could do this. Is there anyone out there that knows of a movement I could join to incorporate this behavior, or should I start this movement? -Lucas http://rufy.com/
Gavin Sinclair
2005-Mar-27 03:47 UTC
Re: I have a dream for easier meta-programming in Ruby
On Sunday, March 27, 2005, 1:35:44 PM, Lucas wrote:> class Test > attr_reader :foo> def something(x) > @foo = x > end> something "bar" > end> a = Test.new > puts a.foo> Fully expecting to see "bar". Instead, I saw "NoMethodError: undefined > method `something'' for Test:Class".What you''ve done is equivalent to Test.something "bar" Whereas what you _need_ to do is: a = Test.new # You got this bit right a.something "bar" puts a.foo # And this bit> Exploring Rails source code, I found that this meta-programming > feature of Ruby is not as easy to create as I thought. My dream is > that Ruby could do this. Is there anyone out there that knows of a > movement I could join to incorporate this behavior, or should I > start this movement?You need to work out exactly what you want to do. Provide code examples and expected outputs. Explain them. The example above, for instance, is not very good. It does show that some things about Ruby are confusing at first, and maybe you can make some suggestions to improve that situation. Maybe take some of the Rails code and demonstrate how you think it should be able to be written. Cheers, Gavin
Nicholas Seckar
2005-Mar-27 06:04 UTC
Re: I have a dream for easier meta-programming in Ruby
I''m a little lost as to what you are hoping from with your example. Here''s a semi-useful example of meta programming in Ruby: a naive interceptor implementation: module Interceptable def intercept(selector, options={}) intercepted_selector = "intercepted_#{selector}" unless instance_methods.include?(intercepted_selector) self.send(:alias_method, intercepted_selector, selector) interceptors = ((@interceptors ||= {})[selector] ||= {:before => [], :after => []}) self.send(:define_method, selector) do |*args| args = interceptors[:before].inject(args) {|args, intc| intc.call(self, *args) || args} result = self.send(intercepted_selector, args) interceptors[:after].inject(result) {|r, intc| intc.call(r) || r} end interceptors[options[:when] || :before] << Proc.new end end end class A extend Interceptable def square(x) x * x end end puts A.new.square(10) # => 100 A.intercept(:square) {|a, x| x + 1} puts A.new.square(10) # => 121 This example doesn''t illustrate the typical override of def append_features(base) super(base) base.extend(ClassMethods) end which I suspect might be your gripe wrt. meta programming. Personally, I wouldn''t mind a core extension of module AutoExtend def append_features(base) super(base) base.extend(self::ClassMethods) if self.const_defined? :ClassMethods end end class Module ; include AutoExtend end -- Nicholas Seckar aka. Ulysses
Lucas Carlson wrote:> Particularly, I love the fact that in Ruby, you can do meta-programming > to define has_many in a way that extends the class accordingly. This > allows you to build a language for the development environment you are > working with. When I found out that has_many was simply a function call > that added functions and relations to the class, I went to irb and typed: > > class Test > attr_reader :foo > > def something(x) > @foo = x > endThis ought to be def self.something(x) @foo = "x" end> something "bar" > endWhy is it this way? Inside a Class or Module definition def foo() end creates an instance method with the name foo. All instances of the Class you are defining or all instances of classes including the Module you are defining will then automatically have that method. The Class or Module itself won''t. So what you need to do is to create a method on the Class or Module *itself*. This can be accomplished via the def self.foo() end syntax which creates a method in the singleton class of your Class. (Singleton classes store the methods for individual objects.) Note that the above will only make the something "bar" construct available to Test and its subclasses. If you want to introduce a meta programming method to all Module and Class definitions you can do it like this: class Module def foo() ... end end Note that this time it''s not called "self.foo" as all classes and modules are instances of Module. (classes are instances of Class which inherits from Module.) Perhaps you will see the connection easier if you try out the following sample code in IRB: irb(main):001:0> obj = "foo" => "foo" irb(main):002:0> def obj.reverse() "ought to be ''oof''" end => nil irb(main):003:0> obj.reverse => "ought to be ''oof''" irb(main):004:0> "hello".reverse => "olleh" This creates the singleton method reverse for only one object. The same was happening above when we created the something method for only one object which happened to be the class Test.
David Adams
2005-Mar-27 13:13 UTC
Re: Re: I have a dream for easier meta-programming in Ruby
>This ought to be > > def self.something(x) > @foo = "x" > end > > something "bar" > endWhile this removes the "undefined method" error, it still doesn''t do what the original poster intended. @foo is an instance variable so it won''t get set until the class is instantiated. You can use @@foo in this example and it will work, but @@foo will be the same across all instances of your class. I think what the original poster was asking--which hasn''t really been answered--is how would one go about defining Rails-like functionality like "has_many" or "validates_uniqueness_of" etc. in classes of one''s own. The Rails code is a little overwhelming for someone who doesn''t yet breathe Ruby like air. -dave
Glenn Smith
2005-Mar-27 17:21 UTC
Re: Re: I have a dream for easier meta-programming in Ruby
> I think what the original poster was asking--which hasn''t really been > answered--is how would one go about defining Rails-like functionality > like "has_many" or "validates_uniqueness_of" etc. in classes of one''s > own. The Rails code is a little overwhelming for someone who doesn''t > yet breathe Ruby like air.Totally agree David. I''m most interested in how the has_many functionality etc. is acheived. Sadly I''m still being weaned off VB air at the moment ! On Sun, 27 Mar 2005 07:13:19 -0600, David Adams <daveadams-Re5JQEeQqe8AvxtiuMwx3w@public.gmane.org> wrote:> >This ought to be > > > > def self.something(x) > > @foo = "x" > > end > > > > something "bar" > > end > > While this removes the "undefined method" error, it still doesn''t do > what the original poster intended. @foo is an instance variable so it > won''t get set until the class is instantiated. You can use @@foo in > this example and it will work, but @@foo will be the same across all > instances of your class. > > I think what the original poster was asking--which hasn''t really been > answered--is how would one go about defining Rails-like functionality > like "has_many" or "validates_uniqueness_of" etc. in classes of one''s > own. The Rails code is a little overwhelming for someone who doesn''t > yet breathe Ruby like air. > > -dave > _______________________________________________ > Rails mailing list > Rails-1W37MKcQCpIf0INCOvqR/iCwEArCW2h5@public.gmane.org > http://lists.rubyonrails.org/mailman/listinfo/rails >
Lucas Carlson
2005-Mar-27 20:20 UTC
Re: I have a dream for easier meta-programming in Ruby
Woops, I can see that my second dream is already a reality, I should have tried that in IRB before I talked. :) -Lucas http://www.rufy.com/
I am getting an error using "Pagination" with the new rails 0.11.1 version. I have a very simple case of pagination in my program. In my controller I have: paginate :films and in my view I have: <%= pagination_links(@film_pages) %> and I get the following error: NameError in Films#list (eval):1:in `paginator_and_collection_for'': uninitialized constant ActionController::Pagination::Film This code worked before in 0.11.0. Did someone else experience the same? Pb. _________________________________________________________________ Express yourself instantly with MSN Messenger! Download today it''s FREE! http://messenger.msn.click-url.com/go/onm00200471ave/direct/01/
David Adams wrote:>>This ought to be >> >> def self.something(x) >> @foo = "x" >> end >> >> something "bar" >>end > > > While this removes the "undefined method" error, it still doesn''t do > what the original poster intended. @foo is an instance variable so it > won''t get set until the class is instantiated. You can use @@foo in > this example and it will work, but @@foo will be the same across all > instances of your class.No, this is an instance variable on the class which is perfectly valid. It can be accessed via Test.instance_variable_get(:@foo) in the above case. You can also create accessors for it like this: class << self attr_accessor :foo end> I think what the original poster was asking--which hasn''t really been > answered--is how would one go about defining Rails-like functionality > like "has_many" or "validates_uniqueness_of" etc. in classes of one''s > own. The Rails code is a little overwhelming for someone who doesn''t > yet breathe Ruby like air.I''d do it exactly like this. If you define the above self.something in a common base class it will work the way it is intended to do. Am I overseeing any problems?
Hi I''m having trouble using the new "Yellow fade" effect (newly upgraded Rails 0.11.1): View: <p> <%= link_to_function ''Highlight text'', "Effect.Highlight(''paragraph'')" %> </p> <p id=''paragraph''>Text to highlight</p> Clicking ''Highlight text'' generates the following error (Firefox 1.0.2): Error: this.initialize has no properties Source File: http://blackbird:3000/javascripts/prototype.js Line: 15 Also get a Javascript error in IE6. I must be missing something, any help appreciated. Cheers, Stuart -- Stuart Rackham
> <%= link_to_function ''Highlight text'', "new > Effect.Highlight(''paragraph'')" %>Needs the new for initialization. -- David Heinemeier Hansson, http://www.basecamphq.com/ -- Web-based Project Management http://www.rubyonrails.org/ -- Web-application framework for Ruby http://www.loudthinking.com/ -- Broadcasting Brain
David Adams
2005-Mar-27 23:18 UTC
Re: Re: I have a dream for easier meta-programming in Ruby
But he''s trying to set the instance variable in the class definition. That doesn''t take. class S attr_reader :zzz def self.rails_like_attribute(x) @zzz = x end end class T < S rails_like_attribute ''testing'' end puts T.new.zzz This outputs ''nil'', when the intent was to output ''testing''. If you swap things around to use a class variable, you get a different problem: class S def zzz @@zzz end def self.rails_like_attribute(x) @@zzz = x end end class T < S rails_like_attribute ''one'' end puts T.new.zzz class Q < S rails_like_attribute ''two'' end puts Q.new.zzz puts T.new.zzz That code outputs: one > first instance of T two > first instance of Q two > instance of T created after def of Q So, my question, at least, is: how do you set this up so that each subclass can have a different setting using only lines like "rails_like_attribute ''three''"? Rails does this extensively, but I haven''t seen any documentation of exactly how it works. PickAxe has not enlightened me. Digging through the ActiveRecord code is helpful in some ways but confusing in others. Can anyone give a simple example of how to do what I want (and presumably help the original poster)? -dave On Sun, 27 Mar 2005 23:12:04 +0200, Florian Groß <florgro-Re5JQEeQqe8AvxtiuMwx3w@public.gmane.org> wrote:> David Adams wrote: > > >>This ought to be > >> > >> def self.something(x) > >> @foo = "x" > >> end > >> > >> something "bar" > >>end > > > > > > While this removes the "undefined method" error, it still doesn''t do > > what the original poster intended. @foo is an instance variable so it > > won''t get set until the class is instantiated. You can use @@foo in > > this example and it will work, but @@foo will be the same across all > > instances of your class. > > No, this is an instance variable on the class which is perfectly valid. > > It can be accessed via Test.instance_variable_get(:@foo) in the above > case. You can also create accessors for it like this: > > class << self > attr_accessor :foo > end > > > I think what the original poster was asking--which hasn''t really been > > answered--is how would one go about defining Rails-like functionality > > like "has_many" or "validates_uniqueness_of" etc. in classes of one''s > > own. The Rails code is a little overwhelming for someone who doesn''t > > yet breathe Ruby like air. > > I''d do it exactly like this. If you define the above self.something in a > common base class it will work the way it is intended to do. > > Am I overseeing any problems? > > _______________________________________________ > Rails mailing list > Rails-1W37MKcQCpIf0INCOvqR/iCwEArCW2h5@public.gmane.org > http://lists.rubyonrails.org/mailman/listinfo/rails >
David Heinemeier Hansson wrote:>> <%= link_to_function ''Highlight text'', "new >> Effect.Highlight(''paragraph'')" %> > > > Needs the new for initialization.Thanks, it now works like a charm! Cheers, Stuart
David Adams wrote:> > So, my question, at least, is: how do you set this up so that each > subclass can have a different setting using only lines like > "rails_like_attribute ''three''"? Rails does this extensively, but I > haven''t seen any documentation of exactly how it works. PickAxe has > not enlightened me. Digging through the ActiveRecord code is helpful > in some ways but confusing in others. Can anyone give a simple > example of how to do what I want (and presumably help the original > poster)? > > -dave >A simple example # Meta-programming # Add a public array property to an object instance class Meta def self.has_many( sym ) attr_tmp = "def #{sym.to_s} \n" attr_tmp << " @#{sym.to_s} = [] unless @#{sym.to_s}\n" attr_tmp << " @#{sym.to_s}; end" eval ( attr_tmp ) end end class Foo < Meta has_many :foo has_many :baz end f = Foo.new # See what methds the new object has ... puts (f.methods - Object.methods ).inspect # ["foo", "baz"] f.foo.push "X" f.baz << "hello" p f.foo.first # "X" p f.baz # ["hello"] James Britt
David Adams wrote:> But he''s trying to set the instance variable in the class definition. > That doesn''t take.Which is not exactly possible as at definition time you do not yet have access to your instances. But you can set up instance methods already.> class S > attr_reader :zzz > def self.rails_like_attribute(x) > @zzz = x > end > end > > class T < S > rails_like_attribute ''testing'' > end > > puts T.new.zzz > > This outputs ''nil'', when the intent was to output ''testing''. > > If you swap things around to use a class variable, you get a different problem: > > [Snip, don''t do that then.] > > So, my question, at least, is: how do you set this up so that each > subclass can have a different setting using only lines like > "rails_like_attribute ''three''"? Rails does this extensively, but I > haven''t seen any documentation of exactly how it works. PickAxe has > not enlightened me. Digging through the ActiveRecord code is helpful > in some ways but confusing in others. Can anyone give a simple > example of how to do what I want (and presumably help the original > poster)?Perhaps you are looking for .define_method then: class S def self.rails_like_attribute(x) define_method(:zzz) { x } end end But the class instance variable approach works as well: class S class << self def rails_like_attribute(x) @zzz = x end attr_reader :zzz end def zzz() self.class.zzz end end Is this what was wanted then? It all pretty much looks the same for me, unfortunately.
I tried something similar today, and wanted to put the code into a module. After digging through the rails source, here is a working test case that I created. The trick is in the append_features call: ------------------------------------ module SomeModule def bark puts "woof" end def self.append_features(base) super base.extend(ClassMethods) end module ClassMethods def createsomething(attrname) define_method(attrname) { "Method #{attrname} called" } end end end class SomeClass include SomeModule createsomething :meow end a = SomeClass.new a.bark a.meow ------------------------------------
Hi, I am getting an error using "Pagination" with the new rails 0.11.1 version. I have a very simple case of pagination in my program. In my controller I have: paginate :films and in my view I have: <%= pagination_links(@film_pages) %> and I get the following error: NameError in Films#list (eval):1:in `paginator_and_collection_for'': uninitialized constant ActionController::Pagination::Film This code worked before in 0.11.0. Did someone else experience the same? Pb. _________________________________________________________________ FREE pop-up blocking with the new MSN Toolbar - get it now! http://toolbar.msn.click-url.com/go/onm00200415ave/direct/01/
On Sun, 27 Mar 2005 20:49:09 +0000, Pranav Bihari <pbihari-PkbjNfxxIARBDgjK7y7TUQ@public.gmane.org> wrote:> and I get the following error: > NameError in Films#list > (eval):1:in `paginator_and_collection_for'': uninitialized constant > ActionController::Pagination::Film > > This code worked before in 0.11.0. Did someone else experience the same?I''m getting the same error, just upgrading from rails 0.10.1 to 0.11.1. I deleted the old pagination_helper.rb file and did a ''rails . --skip''. What are we missing? -- Urban Artography http://artography.ath.cx
On Mon, 28 Mar 2005 21:51:36 -0700, Rob Park <rbpark-Re5JQEeQqe8AvxtiuMwx3w@public.gmane.org> wrote:> What are we missing?Oh, duh. Had to add ''model :photo'' to the beginning of the class definition in the controller. (Pranav, you''d have to add ''model :film'' most likely). Does anybody know why this change is necessary? -- Urban Artography http://artography.ath.cx
Yes, I realized that we need to include the model explicitly. But I don''t see why. Also, the :params functionality still doesn''t work for me even in 0.11.1. I follow the instructions on the Twiki page on creating a paginate partial and using it in my view, while passing additional query arguments. Can someone check the new paginator functionality with 0.11.1 and let us know if it is working as it should? Thanks, Pb>From: Rob Park <rbpark-Re5JQEeQqe8AvxtiuMwx3w@public.gmane.org> >Reply-To: Rob Park <rbpark-Re5JQEeQqe8AvxtiuMwx3w@public.gmane.org>, rails-1W37MKcQCpIf0INCOvqR/iCwEArCW2h5@public.gmane.org >To: rails-1W37MKcQCpIf0INCOvqR/iCwEArCW2h5@public.gmane.org >Subject: Re: [Rails] pagination error >Date: Mon, 28 Mar 2005 21:54:03 -0700 > >On Mon, 28 Mar 2005 21:51:36 -0700, Rob Park <rbpark-Re5JQEeQqe8AvxtiuMwx3w@public.gmane.org> wrote: > > What are we missing? > >Oh, duh. Had to add ''model :photo'' to the beginning of the class >definition in the controller. (Pranav, you''d have to add ''model :film'' >most likely). > >Does anybody know why this change is necessary? > >-- >Urban Artography >http://artography.ath.cx >_______________________________________________ >Rails mailing list >Rails-1W37MKcQCpIf0INCOvqR/iCwEArCW2h5@public.gmane.org >http://lists.rubyonrails.org/mailman/listinfo/rails_________________________________________________________________ Express yourself instantly with MSN Messenger! Download today it''s FREE! http://messenger.msn.click-url.com/go/onm00200471ave/direct/01/
On Mar 28, 2005, at 8:54 PM, Rob Park wrote:> On Mon, 28 Mar 2005 21:51:36 -0700, Rob Park <rbpark-Re5JQEeQqe8AvxtiuMwx3w@public.gmane.org> wrote: >> What are we missing? > > Oh, duh. Had to add ''model :photo'' to the beginning of the class > definition in the controller. (Pranav, you''d have to add ''model :film'' > most likely). > > Does anybody know why this change is necessary?I''m seeing the same symptom (paginate worked in 0.11.0, not in 0.11.1), but adding the ''model'' line doesn''t seem to help anything. Adding a ''require "model"'' to the top of my controller file fixes the problem for *1* page view after restarting the app; after that, it''s broken again. I added a bunch of debugging code to paginator_and_collection_for, and when it runs, none of my model classes are defined. The controller classes are present, but the models are not. That''s why we''re seeing the ''unitialized constant ActionController::Pagination::<model>, because the model class doesn''t exist at that point. I''ve tried to follow the load/unload/reload code, and I don''t have a clue what it''s doing. This is on OS X with Ruby 1.8.2, built from source last week, using Rails 0.11.1 from gems. Scott
On Mar 29, 2005, at 3:54 PM, Scott Laird wrote:> > On Mar 28, 2005, at 8:54 PM, Rob Park wrote: > >> On Mon, 28 Mar 2005 21:51:36 -0700, Rob Park <rbpark-Re5JQEeQqe8AvxtiuMwx3w@public.gmane.org> wrote: >>> What are we missing? >> >> Oh, duh. Had to add ''model :photo'' to the beginning of the class >> definition in the controller. (Pranav, you''d have to add ''model :film'' >> most likely). >> >> Does anybody know why this change is necessary? > > I''m seeing the same symptom (paginate worked in 0.11.0, not in > 0.11.1), but adding the ''model'' line doesn''t seem to help anything. > Adding a ''require "model"'' to the top of my controller file fixes the > problem for *1* page view after restarting the app; after that, it''s > broken again. I added a bunch of debugging code to > paginator_and_collection_for, and when it runs, none of my model > classes are defined. The controller classes are present, but the > models are not. That''s why we''re seeing the ''unitialized constant > ActionController::Pagination::<model>, because the model class doesn''t > exist at that point. > > I''ve tried to follow the load/unload/reload code, and I don''t have a > clue what it''s doing. > > This is on OS X with Ruby 1.8.2, built from source last week, using > Rails 0.11.1 from gems.Someone asked me to follow this up with details from my controller: class GroupsController < ApplicationController model :group # added as part of testing 0.11.1, wasn''t needed in 0.11.0 layout :standard-layout def list order=set_order @paginator, @items=paginate(:groups, :order_by=>order, :per_page=>15) end def show @group=Group.find(@params[:id]) end ... end This worked flawlessly with 0.11.0, but dies every time I try to visit /groups/list with a error like this: NameError in Groups#list (eval):1:in `paginator_and_collection_for'': uninitialized constant ActionController::Pagination::Group app/controllers/groups_controller.rb:15:in `list'' ./script/server:48 Now, adding ''require "group"'' to the top of the file makes this work *once*. Hitting reload gives me the error again, and keeps giving me the error until I restart webrick. I added some debugging code to paginator_and_collection_for, and it shows that *none* of my model classes exist when it''s called. I determined this by walking ObjectSpace and printing the name of all Class objects. Strangely, though, the ''show'' method works flawlessly every time. I tried creating a new Rails project and then copying over my models, controllers, and views from 0.11.0, just to see if I''d corrupted environment.rb or something, but that didn''t make any difference--it''s still broken. Anyone have any suggestions? Scott
On Mar 30, 2005, at 10:11 AM, Scott Laird wrote:> Someone asked me to follow this up with details from my controller: > > class GroupsController < ApplicationController > model :group # added as part of testing 0.11.1, wasn''t needed in > 0.11.0 > layout :standard-layout > > def list > order=set_order > > @paginator, @items=paginate(:groups, :order_by=>order, > :per_page=>15) > end > > def show > @group=Group.find(@params[:id]) > end > > ... > end > > This worked flawlessly with 0.11.0, but dies every time I try to visit > /groups/list with a error like this: > > NameError in Groups#list > > (eval):1:in `paginator_and_collection_for'': uninitialized constant > ActionController::Pagination::Group > > app/controllers/groups_controller.rb:15:in `list'' > ./script/server:48 > > Now, adding ''require "group"'' to the top of the file makes this work > *once*. Hitting reload gives me the error again, and keeps giving me > the error until I restart webrick. I added some debugging code to > paginator_and_collection_for, and it shows that *none* of my model > classes exist when it''s called. I determined this by walking > ObjectSpace and printing the name of all Class objects. > > Strangely, though, the ''show'' method works flawlessly every time. > > I tried creating a new Rails project and then copying over my models, > controllers, and views from 0.11.0, just to see if I''d corrupted > environment.rb or something, but that didn''t make any difference--it''s > still broken.Ugh, brown-paper bag time. I was editing one version of the code while running a different version; that explains why adding ''model'' didn''t help. Once I fixed that, I found that either of two things would fix the problem: 1. Adding the model line, as mentioned a couple messages up-thread. 2. Adding a reference to the model *before* calling the Pagination code. So adding ''Group.find(1)'' before the call to paginate also fixed things. Scott