7 Replies Latest reply on Sep 14, 2012 9:05 AM by Jon Tara

    Calling model methods from any controller?

    Stephen Skidmore

      As I try to get my head around rhodes I've read that any controller can access any model.  That's good because I was stuck on an MVC as a self contained unit.  But when I try, rhodes calls my model an undefined local variable.  I hope I'm just blowing the syntax.

       

      I have a receiving controller and and a shipToList model.  I want to populate a list box on a page in my receiving viewer with records from the shipToList model.  In the receiving controller I have

       

      def doPOSearch

           @shipTos = shipToList.find(:all)

           render :action => :poSearch

       

      end

       

      When I run it I get -  Application initialize failed: #<NameError: undefined local variable or method `shipToList'

       

      Have included enough detail for anyone to spot my mistake?

       

      Thank you

       

      Steve

        • Re: Calling model methods from any controller?
          Jon Tara

          In ShipToList.rb, add require_source 'ShipToList'.

           

          I just do this routinely on every one of my models. It eliminates issues with order of use.

           

          BTW, you never need a require for the model in controllers. Once loaded, models are available anywhere in your Rhodes app, without needing a require.

          1 of 1 people found this helpful
            • Re: Calling model methods from any controller?
              Stephen Skidmore

              This isn't clear to me so I tried to answer this one myself but cannot find require_source documented anywhere.  It is not demonstrated in the samples.  It is mentioned in the docs as a fix but there are no examples of usage.  Is it a compiler directive used outside of a class?   Or a function call used inside of the model class?

               

              From what I can put together it is used to force a model to load before the first call from its own controller?  If this is true why does it go in the model class itself?  It doesn't seem as if it would ever get called?

               

              Jon - I see you furnish many answers here and seem to be a subject matter expert.  I find this documentation extremely fragmented, incomplete and frustrating.  Assuming you did not write rhodes, where did you learn this from?  What source have I not been able to find?

               

              Thank you

               

              Steve

                • Re: Calling model methods from any controller?
                  Jon Tara

                  Yes, that is what require_source does - it forces the model to always load before ethe first call from it's own controller. Put it at the top of your model, outside of the class.

                   

                  Keep in mind the Rhodes compiled-Ruby environment, which I think is not well-understood or well-explained. This is dictated both by practical considerations and by vendor restrictions, most notibly Apple's ban on run-time interpretation. (This is why you cannot use eval() in a Rhodes app.) The Ruby in Rhodes apps is compiled at build time into bytecode (since Ruby does not yet compile to machine code.)

                   

                  Each Ruby file is compiled into a bytecode file with extension .iseq., which is incorporated into the runtime. These files are loaded at run-time as-needed. i.e. when a require is encountered. Once loaded, I'm pretty sure they just stay in memory, BTW. This is why I encourage developers to use partials liberally instead of just one big ugly ERB. You might think "oh, that's terrible, it is loading and interpreting the partial every time." No, when you call a partial, you are incurring the overhead of a single Ruby function call, which is trivial. ERBs are compiled to Ruby. The Ruby is compiled to bytecode. I might use 10 or 20 partials for a given controller. It keeps things nicely-organized.

                   

                  I think the term "source" here is actually a misnomer. Every file's source is needed, obviously. Every file needs to be compiled and incorporated in the executable. require_source is essentially a "compiler directive" that arranges that the file will be loaded when the app is initialized, rather than on-demand.

                   

                  Models are "special". Notice that you never need to use a require statment to use a model anywhere in your Ruby code. This is nice and convenient, but it really isn't there for your convenience. I think it's more a side-effect of the way models are designed to work integrally with an associated controller.

                   

                  But, normally they are loaded only when the model is first used. Each controller automatically requires the associated model. Thus, the model normally is loaded when the associated controller is first required. If you use a model before it's associated controller is required, now you have a problem. That's why there's a require_source escape-hatch. It says "always load this at app start-up."

                   

                  I can't imagine why - in most circumstances at least - you wouldn't want to just load all models at start-up. So, I just use require_source routinely, in every model.

                   

                  I agree the documentation is fragmented, incomplete, and frustrating. Well put.

                   

                  I have been working on my own app, a fairly complicated one because it is a kind of IM client and all of the protocol is done in Ruby and uses threading extensively. (I have ported XMPP4R to Rhodes, and I have made it available on github.) And I have been working on it for way too long (I suppose because I am a perfectionist), and since I need to eat, I have been doing some Rhodes and jQuery Mobile consulting for others. I've contributed some small pull requests - mainly bug fixes - to the Rhodes project, all of which have been accepted, and I have a good working relationship with the Rhodes developers. It's made easier because I have some significant Ruby on Rails experience as well. (I wrote the back-end moderation system used to handle complaints when little Susy says a bad word online in Playstation games - among other backend game web services - while working with some very talented Ruby developers at Sony's San Diego Studio.)

                   

                  And I've bothered to read much of the source code. So, ultimately the source is - the source.

                   

                  Message was edited by: Jon Tara - changed "exec" to eval()"

                  1 of 1 people found this helpful
                    • Re: Calling model methods from any controller?
                      Stephen Skidmore

                      Jon - Thank you for the well thought out answer.  I've read it several time now and it definitely sheds some light under the hood of this beast.  I'm sure it will help others also. 

                       

                      But...require_source does not fix my issue.  Interestingly I make the same call, both from controllers that the model is not associated with.  I call ShipToLoc.find(:all) from the application controller and from the receiving controller.  The call from the application controller is successful, the call from the other returns a nil object.

                       

                      The source is attached if anyone cares to look.  I'd appreciate any help.

                       

                      At this point the app is just a test.  2 models one for ship to locations and one for receiving, each with only 1 parameter.  In the application controller I call ShipToLoc.find(:all) to see if the model is populated.  If it is not populated I load the contents of a text file.   This code is from the RhoElements tutorial and works fine.  Clicking on the Ship To Locs button shows that the model was loaded correctly.  Clicking the Receiving button launches the ShowPOSearch method in the Receiving controller.  I make the same call to ShipToLoc.find(:all) which returns nil this time.  Target page fails to render with an undefined method 'each' for  nil:NilClass.  I can't find any dumb mistakes, but thats why we call them dumb.  Right.  I did add require_source to the model even though the application controller had already loaded the model but it did not help.  Since the model is already loaded I'm wondering if access to it is different from the two controllers, though I have not been able to find anything to support that idea.  The call to find was copied from the one that worked so typos are out. 

                       

                      At this point I'm actually looking forward to having my nose rubbed in a dumb mistake.

                       

                      Thank you

                       

                      Steve

                        • Re: Calling model methods from any controller?
                          Stephen Skidmore

                          I didn't think I would be rubbing my own nose it though.  My mistake is not realizing that controller methods and like named erb files are apparently in the same class and so they share instance variables. 

                           

                          For example @me declared in controller::index is still in scope in index.erb. 

                           

                          But @otherme declared in controller::mymethod is out off scope in mypage.erb and must be accessed with an accessor method.

                           

                          So my calls -  @shipTos = shipToLoc.find(:all)  - do work.

                          But in my page the instance of @shipTos is out of scope.  The @shipTo used in my page is a new instance and because it is uninitialized it is indeed a nil object.  But I can still get to the first @shipTos with

                           

                          def getShipTos

                               @myShipTos = @shipTos

                          end

                           

                          That was a 2 day lesson, I hope it saves someone else similiar pain.

                            • Re: Calling model methods from any controller?
                              Jon Tara

                              Aha.

                               

                              It's useful to have a background in Rails. The documentation seems to make that assumption, as it doesn't explain this very well. I *do* have a background in Rails, and I found myself having to guess what features are and are not implemented, because the documentation is so vague.

                               

                              The docs really need a lot of work.

                               

                              I recently saw a bunch of RhoMobile-related contract job postings in Schaumberg, IL. I don't recall if any of those were for technical writers, but I hope so.

                    • Re: Calling model methods from any controller?
                      Hamid Seleman

                      Hi,

                         I have had the same problem long time ago with a twisted solution. I never figured out why and I hope someone could kindly enough tell me why. My requirement was a little odd that I need to have a model without the rest of the controller and views files as generated by "rhodes model" generator command and be used in other controllers. For example, if I need ShipToList Model, I would create "ShipToList" folder under app and placed my model ship_to_list.rb file there. Alternatively you could use "rhodes model" generator and remove the views and controller files. I notice there are strict file naming and structure to follow, if you do it by hand, otherwise could just use the generator. Hope that helps.