frankjmattia-Re5JQEeQqe8AvxtiuMwx3w@public.gmane.org
2010-Jun-25 05:12 UTC
extracting a portion of a model to a separate file
G''day, I have a project that relies heavily on pluginaweeks "state_machine" gem. While I would have liked to post this to a state_machine forum, 1. I couldn''t find one and 2. this is probably a generic ruby concept that I just can''t piece together on my own - so asking here may be *more* appropriate (but still less appropriate than a straight ruby forum... maybe). I have a ton of models that each have fairly complex state machines. state_machine defines them with blocks directly in your model class like so: state_machine :initial => :created do state :created event :review do transition any => :reviewing end end My state_machine blocks however are very large and clutter up my model.rb files... What I want to do is extract out all of the state machine code to another file that I can then "include" in my model. I know include is the wrong terminology in ruby.. maybe I want to modularize them? Whatever it''s called. It''s preferable (for maintainability, etc) for my models to look something like class Thing < ActiveRecord::Base #validations #callbacks #etc... line_of_code_that_loads_external_state_file_here #maybe eval somehow #maybe make a module that gets "mixed in"... i really dont know the right approach end Any help would be appreciated. - FJM -- You received this message because you are subscribed to the Google Groups "Ruby on Rails: Talk" group. To post to this group, send email to rubyonrails-talk-/JYPxA39Uh5TLH3MbocFF+G/Ez6ZCGd0@public.gmane.org To unsubscribe from this group, send email to rubyonrails-talk+unsubscribe-/JYPxA39Uh5TLH3MbocFF+G/Ez6ZCGd0@public.gmane.org For more options, visit this group at http://groups.google.com/group/rubyonrails-talk?hl=en.
Marnen Laibow-Koser
2010-Jun-25 13:58 UTC
Re: extracting a portion of a model to a separate file
frankjmattia-Re5JQEeQqe8AvxtiuMwx3w@public.gmane.org wrote:> G''day, > > I have a project that relies heavily on pluginaweeks "state_machine" > gem. While I would have liked to post this to a state_machine forum, > 1. I couldn''t find one and 2. this is probably a generic ruby concept > that I just can''t piece together on my own - so asking here may be > *more* appropriate (but still less appropriate than a straight ruby > forum... maybe). > > I have a ton of models that each have fairly complex state machines. > state_machine defines them with blocks directly in your model class > like so: > > state_machine :initial => :created do > state :created > > event :review do > transition any => :reviewing > end > end > > My state_machine blocks however are very large and clutter up my > model.rb files...IMHO, they don''t *clutter* your models; they are *part of* your models. I tend to think you want the state_machine blocks in the model files so that you can review your state machine behaviors right along with your methods and everything else. So I''m not sure that it''s such a great idea to do what you''re asking. However, it''s not that hard. Read on for ideas!> > What I want to do is extract out all of the state machine code to > another file that I can then "include" in my model. I know include is > the wrong terminology in ruby.. maybe I want to modularize them? > > Whatever it''s called. It''s preferable (for maintainability, etc)Why would this help maintainability? It seems to me that it would be more maintainable to keep it all together as outlined above?> for > my models to look something like > > class Thing < ActiveRecord::Base > #validations > #callbacks > #etc... > > line_of_code_that_loads_external_state_file_here > #maybe eval somehow > #maybe make a module that gets "mixed in"... i really dont know the > right approach > endWell, you could take several approaches here. You could reopen the class in a separate file... ### app/models/thing.rb class Thing < ActiveRecord::Base # validations # methods require ''thing_states'' end ### lib/thing_states.rb class Thing < ActiveRecord::Base state_machine stuff end ...you could store the state machine configuration in a YAML file or something and read it with YAML.load or whatever that function is...you could write a wrapper function for state_machine with a shorter syntax... But again, I think that this is probably not a great idea. If your state machine definitions are part of your model functionality, they should probably be in the same model file with everything else. If your state machines are just "clutter", then they probably shouldn''t be in your code at all. (Note all the "probably"s here -- I could well be wrong.)> > Any help would be appreciated. > - FJMBest, -- Marnen Laibow-Koser http://www.marnen.org marnen-sbuyVjPbboAdnm+yROfE0A@public.gmane.org -- Posted via http://www.ruby-forum.com/. -- You received this message because you are subscribed to the Google Groups "Ruby on Rails: Talk" group. To post to this group, send email to rubyonrails-talk-/JYPxA39Uh5TLH3MbocFF+G/Ez6ZCGd0@public.gmane.org To unsubscribe from this group, send email to rubyonrails-talk+unsubscribe-/JYPxA39Uh5TLH3MbocFF+G/Ez6ZCGd0@public.gmane.org For more options, visit this group at http://groups.google.com/group/rubyonrails-talk?hl=en.
frankjmattia-Re5JQEeQqe8AvxtiuMwx3w@public.gmane.org
2010-Jun-25 14:47 UTC
Re: extracting a portion of a model to a separate file
On Jun 25, 9:58 am, Marnen Laibow-Koser <li...-fsXkhYbjdPsEEoCn2XhGlw@public.gmane.org> wrote:> frankjmat...-Re5JQEeQqe8AvxtiuMwx3w@public.gmane.org wrote: > > G''day, > > > I have a project that relies heavily on pluginaweeks "state_machine" > > gem. While I would have liked to post this to a state_machine forum, > > 1. I couldn''t find one and 2. this is probably a generic ruby concept > > that I just can''t piece together on my own - so asking here may be > > *more* appropriate (but still less appropriate than a straight ruby > > forum... maybe). > > > I have a ton of models that each have fairly complex state machines. > > state_machine defines them with blocks directly in your model class > > like so: > > > state_machine :initial => :created do > > state :created > > > event :review do > > transition any => :reviewing > > end > > end > > > My state_machine blocks however are very large and clutter up my > > model.rb files... > > IMHO, they don''t *clutter* your models; they are *part of* your models. > I tend to think you want the state_machine blocks in the model files so > that you can review your state machine behaviors right along with your > methods and everything else. So I''m not sure that it''s such a great > idea to do what you''re asking. > > However, it''s not that hard. Read on for ideas! > > > > > What I want to do is extract out all of the state machine code to > > another file that I can then "include" in my model. I know include is > > the wrong terminology in ruby.. maybe I want to modularize them? > > > Whatever it''s called. It''s preferable (for maintainability, etc) > > Why would this help maintainability? It seems to me that it would be > more maintainable to keep it all together as outlined above?Maintainability was probably not the best word. Readability? Looking for a better abstraction? I dunno. Having a model with only 300 lines of code with 80 of those for a state machine -- looks ugly. There has to be a more readable abstraction. Now that I see how it works, I''m thinking something like: instance_eval(File.read("states.rb"), "states.rb")> > for > > my models to look something like > > > class Thing < ActiveRecord::Base > > #validations > > #callbacks > > #etc... > > > line_of_code_that_loads_external_state_file_here > > #maybe eval somehow > > #maybe make a module that gets "mixed in"... i really dont know the > > right approach > > end > > Well, you could take several approaches here. You could reopen the > class in a separate file... > ### app/models/thing.rb > class Thing < ActiveRecord::Base > # validations > # methods > require ''thing_states'' > end > > ### lib/thing_states.rb > class Thing < ActiveRecord::Base > state_machine stuff > end > > ...you could store the state machine configuration in a YAML file or > something and read it with YAML.load or whatever that function is...you > could write a wrapper function for state_machine with a shorter > syntax... > > But again, I think that this is probably not a great idea. If your > state machine definitions are part of your model functionality, they > should probably be in the same model file with everything else. If your > state machines are just "clutter", then they probably shouldn''t be in > your code at all. (Note all the "probably"s here -- I could well be > wrong.)They are definitely not "clutter" I just meant that they clutter up an otherwise readable file. I think making some sort of wrapper for instance_eval that figures out the model name then instance evals it from say... app/models/states/order_states.rb or somesuch. I see your point about keeping it together but there comes a point where an abstraction becomes necessary... I may not be at that exact point yet, but I see it over the horizon and want to work it out now.> > Any help would be appreciated. > > - FJM > > Best, > -- > Marnen Laibow-Koserhttp://www.marnen.org > mar...-sbuyVjPbboAdnm+yROfE0A@public.gmane.org > -- > Posted viahttp://www.ruby-forum.com/.-- You received this message because you are subscribed to the Google Groups "Ruby on Rails: Talk" group. To post to this group, send email to rubyonrails-talk-/JYPxA39Uh5TLH3MbocFF+G/Ez6ZCGd0@public.gmane.org To unsubscribe from this group, send email to rubyonrails-talk+unsubscribe@googlegroups.com. For more options, visit this group at http://groups.google.com/group/rubyonrails-talk?hl=en.
Marnen Laibow-Koser
2010-Jun-25 15:04 UTC
Re: extracting a portion of a model to a separate file
frankjmattia-Re5JQEeQqe8AvxtiuMwx3w@public.gmane.org wrote:> On Jun 25, 9:58�am, Marnen Laibow-Koser <li...-fsXkhYbjdPsEEoCn2XhGlw@public.gmane.org> wrote: >> > I have a ton of models that each have fairly complex state machines. >> >> >> >> >> > What I want to do is extract out all of the state machine code to >> > another file that I can then "include" in my model. I know include is >> > the wrong terminology in ruby.. maybe I want to modularize them? >> >> > Whatever it''s called. It''s preferable (for maintainability, etc) >> >> Why would this help maintainability? �It seems to me that it would be >> more maintainable to keep it all together as outlined above? > > Maintainability was probably not the best word. Readability? Looking > for a better abstraction? I dunno. Having a model with only 300 lines > of code with 80 of those for a state machine -- looks ugly. There has > to be a more readable abstraction.Then I''d recommend finding a more readable abstraction that keeps it all in the same place. Moving your state machine code to a separate file essentially means that an important piece of your model functionality is invisible when you most need to see it.> > Now that I see how it works, I''m thinking something like: > > instance_eval(File.read("states.rb"), "states.rb")I think you''ve got the instance_eval syntax wrong here, but why on earth would you use instance_eval instead of require or include? There are legitimate uses for eval and instance_eval in Ruby, but they are few and far between -- enough so that their use should IMHO be considered a code smell. If you think you need eval or instance_eval, stop and think -- there''s almost always a better way. [...]> > I see your point about keeping it together but there comes a point > where an abstraction becomes necessary...What are you trying to abstract? Moving to a separate file is not, in itself, abstraction. If an abstraction is what''s wanted here, let''s find one instead of shuffling the same code around between files.> I may not be at that exact > point yet, but I see it over the horizon and want to work it out now.Nothing wrong with speculating about future directions; just beware of premature optimization. Best, -- Marnen Laibow-Koser http://www.marnen.org marnen-sbuyVjPbboAdnm+yROfE0A@public.gmane.org -- Posted via http://www.ruby-forum.com/. -- You received this message because you are subscribed to the Google Groups "Ruby on Rails: Talk" group. To post to this group, send email to rubyonrails-talk-/JYPxA39Uh5TLH3MbocFF+G/Ez6ZCGd0@public.gmane.org To unsubscribe from this group, send email to rubyonrails-talk+unsubscribe@googlegroups.com. For more options, visit this group at http://groups.google.com/group/rubyonrails-talk?hl=en.
Marnen Laibow-Koser wrote:> frankjmattia-Re5JQEeQqe8AvxtiuMwx3w@public.gmane.org wrote: >> On Jun 25, 9:58�am, Marnen Laibow-Koser <li...-fsXkhYbjdPsEEoCn2XhGlw@public.gmane.org> wrote: >>> > I have a ton of models that each have fairly complex state machines. >>> >>> >>> >>> >>> > What I want to do is extract out all of the state machine code to >>> > another file that I can then "include" in my model. I know include is >>> > the wrong terminology in ruby.. maybe I want to modularize them? >>> >>> > Whatever it''s called. It''s preferable (for maintainability, etc) >>> >>> Why would this help maintainability? �It seems to me that it would be >>> more maintainable to keep it all together as outlined above? >> >> Maintainability was probably not the best word. Readability? Looking >> for a better abstraction? I dunno. Having a model with only 300 lines >> of code with 80 of those for a state machine -- looks ugly. There has >> to be a more readable abstraction. > > Then I''d recommend finding a more readable abstraction that keeps it all > in the same place. Moving your state machine code to a separate file > essentially means that an important piece of your model functionality is > invisible when you most need to see it. > >> >> Now that I see how it works, I''m thinking something like: >> >> instance_eval(File.read("states.rb"), "states.rb") > > I think you''ve got the instance_eval syntax wrong here, but why on earth > would you use instance_eval instead of require or include?Because neither require nor include do what I need them to do. Require runs another file but if that other file relies on functions defined on the requiring class, it can''t access them... can it? And include takes all the methods in the included file and adds them to the including class. Except the state definitions aren''t method definitions. They are class functions that accept blocks which need to be run in the context of the calling class. (Please forgive me if my terminology isn''t entirely accurate. I''m still asking why class_eval and instance_eval were named they way they were.)> > There are legitimate uses for eval and instance_eval in Ruby, but they > are few and far between -- enough so that their use should IMHO be > considered a code smell. If you think you need eval or instance_eval, > stop and think -- there''s almost always a better way. > > [...] >> >> I see your point about keeping it together but there comes a point >> where an abstraction becomes necessary... > > What are you trying to abstract? Moving to a separate file is not, in > itself, abstraction. If an abstraction is what''s wanted here, let''s > find one instead of shuffling the same code around between files.Yeah, this is more of a "code shuffling" today. Eventually I''d like to encapsulate state definitions into a reusable chunk of code so that multiple models can rely on the same definitions without the brittleness that comes with repeating them. A wrapper around instance_eval lets me reuse state_machines if I so desire without having to duplicate their code.> >> I may not be at that exact >> point yet, but I see it over the horizon and want to work it out now. > > Nothing wrong with speculating about future directions; just beware of > premature optimization. > > Best, > -- > Marnen Laibow-Koser > http://www.marnen.org > marnen-sbuyVjPbboAdnm+yROfE0A@public.gmane.org-- Posted via http://www.ruby-forum.com/. -- You received this message because you are subscribed to the Google Groups "Ruby on Rails: Talk" group. To post to this group, send email to rubyonrails-talk-/JYPxA39Uh5TLH3MbocFF+G/Ez6ZCGd0@public.gmane.org To unsubscribe from this group, send email to rubyonrails-talk+unsubscribe@googlegroups.com. For more options, visit this group at http://groups.google.com/group/rubyonrails-talk?hl=en.
Marnen Laibow-Koser
2010-Jun-25 16:00 UTC
Re: extracting a portion of a model to a separate file
Frank Mattia wrote: [...]>>> Now that I see how it works, I''m thinking something like: >>> >>> instance_eval(File.read("states.rb"), "states.rb") >> >> I think you''ve got the instance_eval syntax wrong here, but why on earth >> would you use instance_eval instead of require or include? > > Because neither require nor include do what I need them to do. Require > runs another file but if that other file relies on functions defined on > the requiring class, it can''t access them... can it?Sure. Just reopen the requiring class. This is exactly what I was doing in my example in http://www.ruby-forum.com/topic/212124#921467 ; you may want to review it.> And include takes > all the methods in the included file and adds them to the including > class. Except the state definitions aren''t method definitions. They are > class functions that accept blocks which need to be run in the context > of the calling class.Good point. You''ll probably need extend instead of include, and you may need the included and/or extended hooks. But this is definitely possible. [...]>> What are you trying to abstract? Moving to a separate file is not, in >> itself, abstraction. If an abstraction is what''s wanted here, let''s >> find one instead of shuffling the same code around between files. > > Yeah, this is more of a "code shuffling" today. Eventually I''d like to > encapsulate state definitions into a reusable chunk of code so that > multiple models can rely on the same definitions without the brittleness > that comes with repeating them.Then do it right and put it into a module (or use inheritance).> A wrapper around instance_eval lets me > reuse state_machines if I so desire without having to duplicate their > code.Nope! instance_eval just introduces brittleness. Learn about Ruby''s module system and use that instead. You almost never need to evaluate arbitrary literal code in Ruby, which means you almost never need *eval. Metaprogramming is an amazing thing. Best, -- Marnen Laibow-Koser http://www.marnen.org marnen-sbuyVjPbboAdnm+yROfE0A@public.gmane.org -- Posted via http://www.ruby-forum.com/. -- You received this message because you are subscribed to the Google Groups "Ruby on Rails: Talk" group. To post to this group, send email to rubyonrails-talk-/JYPxA39Uh5TLH3MbocFF+G/Ez6ZCGd0@public.gmane.org To unsubscribe from this group, send email to rubyonrails-talk+unsubscribe-/JYPxA39Uh5TLH3MbocFF+G/Ez6ZCGd0@public.gmane.org For more options, visit this group at http://groups.google.com/group/rubyonrails-talk?hl=en.
Gentlemen thanks for having this conversation. Was pulling my hair out trying to get state_machine to work with a mixin. I need to define state_machines behaviors unique to each client while keeping a single code base. I ended up using opening up the class like Maren suggested. class OnDemandWorkorderWorkflow < Workflow belongs_to :workorder attr_accessible :type, :state, :workorder_id require "#{Rails.root}/lib/clients/#{CUSTOMER}/workflows/#{FILE_REFERENCE}_on_demand_workorder_workflow" end required file looks like this: class OnDemandWorkorderWorkflow < Workflow state_machine :state, :initial => :new do ....... # lots of events and transitions ....... end -- Posted via http://www.ruby-forum.com/. -- You received this message because you are subscribed to the Google Groups "Ruby on Rails: Talk" group. To post to this group, send email to rubyonrails-talk-/JYPxA39Uh5TLH3MbocFF+G/Ez6ZCGd0@public.gmane.org To unsubscribe from this group, send email to rubyonrails-talk+unsubscribe-/JYPxA39Uh5TLH3MbocFF+G/Ez6ZCGd0@public.gmane.org For more options, visit https://groups.google.com/groups/opt_out.
On Friday, 25 June 2010 01:12:47 UTC-4, frankj...-Re5JQEeQqe8AvxtiuMwx3w@public.gmane.org wrote:> > G''day, > > I have a project that relies heavily on pluginaweeks "state_machine" > gem. While I would have liked to post this to a state_machine forum, > 1. I couldn''t find one and 2. this is probably a generic ruby concept > that I just can''t piece together on my own - so asking here may be > *more* appropriate (but still less appropriate than a straight ruby > forum... maybe). > > I have a ton of models that each have fairly complex state machines. > state_machine defines them with blocks directly in your model class > like so: > > state_machine :initial => :created do > state :created > > event :review do > transition any => :reviewing > end > end > > My state_machine blocks however are very large and clutter up my > model.rb files... > > What I want to do is extract out all of the state machine code to > another file that I can then "include" in my model. I know include is > the wrong terminology in ruby.. maybe I want to modularize them? >One mechanism that can be helpful that hasn''t been mentioned - ActiveSupport::Concern. See here for more detail: http://www.fakingfantastic.com/2010/09/20/concerning-yourself-with-active-support-concern/ --Matt Jones -- You received this message because you are subscribed to the Google Groups "Ruby on Rails: Talk" group. To post to this group, send email to rubyonrails-talk-/JYPxA39Uh5TLH3MbocFF+G/Ez6ZCGd0@public.gmane.org To unsubscribe from this group, send email to rubyonrails-talk+unsubscribe-/JYPxA39Uh5TLH3MbocFF+G/Ez6ZCGd0@public.gmane.org To view this discussion on the web visit https://groups.google.com/d/msg/rubyonrails-talk/-/H8Smd2xt4xoJ. For more options, visit https://groups.google.com/groups/opt_out.
Matt Jones wrote in post #1080951:> On Friday, 25 June 2010 01:12:47 UTC-4, frankj...-Re5JQEeQqe8AvxtiuMwx3w@public.gmane.org wrote: >> I have a ton of models that each have fairly complex state machines. >> >> My state_machine blocks however are very large and clutter up my >> model.rb files... >> >> What I want to do is extract out all of the state machine code to >> another file that I can then "include" in my model. I know include is >> the wrong terminology in ruby.. maybe I want to modularize them? >> > > One mechanism that can be helpful that hasn''t been mentioned - > ActiveSupport::Concern. See here for more detail: >Funny, as soon as I came back to check the update I said to myself "knowing what I know now, I would have probably used a concern". I second your suggestion.>http://www.fakingfantastic.com/2010/09/20/concerning-yourself-with-active-support-concern/> > --Matt Jones-- Posted via http://www.ruby-forum.com/. -- You received this message because you are subscribed to the Google Groups "Ruby on Rails: Talk" group. To post to this group, send email to rubyonrails-talk-/JYPxA39Uh5TLH3MbocFF+G/Ez6ZCGd0@public.gmane.org To unsubscribe from this group, send email to rubyonrails-talk+unsubscribe-/JYPxA39Uh5TLH3MbocFF+G/Ez6ZCGd0@public.gmane.org For more options, visit https://groups.google.com/groups/opt_out.