Is Rails missing a rail-road tie?

…or just as likely, I just don’t get it yet.

I’m loving Rails so far, but I just ran into a situation that I haven’t
found a clean solution for. My first question about this went
unanswered so I’ll try to simplify it.

Where can I put some code that has all of these attributes:
– it is a class with some instance variables
– it is view-related code and needs to access all of the Action-Pack
stuff like “h()” and form tags etc.
– it is not and should not be an active-record model
– it needs to live through multiple http round trips and ajax cycles,
therefore I need to be able to save an instance of the object in the
session.
– it will be used by multiple views and controllers throughout a fairly
large app

It is basically a more complex version of the things you would usually
find in the application helper file. If I just put the class in there,
all is well except that I don’t know how to give it the necessary magic
that lets an instance of an object be saved in the session.

That is really the key part. How can I make a class that is defined in
a helper file known to the stuff that serializes an object for the
session hash? I think it involves having a “.to_xml” method for the
object, but I think there is some more to it.

thanks,
jp

– it is not and should not be an active-record model

That is really the key part. How can I make a class that is defined in
a helper file known to the stuff that serializes an object for the
session hash? I think it involves having a “.to_xml” method for the
object, but I think there is some more to it.

My initial reaction is what are you storing in a session that needs
access
to view-related methods? That doesn’t seem to follow MVC as I
understand
it…

If you can split the view-related aspects out of it then you could
simply
put a normal ruby class into RAILS_ROOT/lib or RAILS_ROOT/models and go
on
your merry way…

What are you trying to do specifically?

Jeff,

Can you make it a non-AR model and store it in the session? As long as
it doesn’t get huge, it should be ok. If it does get too big, you will
probably have to look at some custom caching scheme separate from
session to manage it.

Make sense?

Wes

Wes G. wrote:

Jeff,

Can you make it a non-AR model and store it in the session? As long as
it doesn’t get huge, it should be ok. If it does get too big, you will
probably have to look at some custom caching scheme separate from
session to manage it.

Make sense?

Wes

Thanks Wes and Philip.

First, a bit of context. This object takes an array of “items” and
builds a specially formatted unordered list of clickable list items from
it, sometimes with extra per-item buttons that do special things via
ajax calls. I have implemented all of this with individual helper
methods, but it is not DRY at all, and I want to pretty it up with some
object orientation.

Philip, yeah, I sort of left that explanation out, as answering it in my
first attempt to get help on this issue made the email rather longish
and I thought that might explain the lack of response. Basically, this
object requires some configuration in order to make it work in two or
three different contexts throughout the app. Things like different CSS
tags for things depending on where it will be used in order to make it
look or work slightly differently. Also need to tell it what
controller/action to go to when somebody clicks on the links that it
makes. I want to instantiate one of these objects with all of that
info, and then have the object remember what it’s supposed to look like
during the whole time it is displayed.

Wes, that was my first thought, but there is a fly in the ointment that
I don’t have a solution to. Perhaps there is an easy way to solve that.
An AR model does not have (natively) any of the Action Pack stuff that
controllers and views need to get things done in views. Something tells
me that making an object that is both an AR and an AP would be kind of
like “crossing the streams” in GhostBusters. It just feels wrong and
very non-MVC. The stuff I’m trying to store in this object is
configuration data, not user data. It is not a model by nature. It is
an object oriented helper.

thanks,
jp

Wes G. wrote:

Jeff,

Can you make it a non-AR model and store it in the session? As long as
it doesn’t get huge, it should be ok. If it does get too big, you will
probably have to look at some custom caching scheme separate from
session to manage it.

Make sense?

Wes

Wes, I just re-read your reply. Sorry, mis-read it the first time. I
tried non-AR model, and the problem is that a non-AR model lacks the
magic that makes an AR model serializable. That is needed in order to
save the object in the session. Without that you just get an error
about trying to stick an unknown object type in the session.

Even if there is a relatively simple solution to that, the object would
have to be a child of Action Pack to be useful to me and also needs
access to all of the variables and methods of the conroller and view,
like a helper does.

It would be really delicious if that session store-ability aspect of AR
was available separate from AR.

jp

It would be really delicious if that session store-ability aspect of AR
was available separate from AR.

It is. That’s what the database is for. Create an AR object to store
your data, put the id, and only the id, in the session then load the
information you need from the database to service each request.

It sounds like you have code that tightly couples the business logic
to the view, break them apart now and save time and trouble down the
road. Then create a custom helper class that takes your database
object(s) and creates the view.

Aaron

Aaron wrote:

It would be really delicious if that session store-ability aspect of AR
was available separate from AR.

It is. That’s what the database is for. Create an AR object to store
your data, put the id, and only the id, in the session then load the
information you need from the database to service each request.

It sounds like you have code that tightly couples the business logic
to the view, break them apart now and save time and trouble down the
road. Then create a custom helper class that takes your database
object(s) and creates the view.

Aaron

Thanks Aaron. I guess I haven’t explained it well enough yet. Let’s
try an analogy…I love analogies.

Suppose you were doing a web app that deals with user-created block
diagrams. The types of boxes and how they are connected and the label
for each box is all user data. That goes in the database.

Now, you write some view code that uses RMagick to draw these block
diagrams on the screen. Let’s say that in different parts of the app
you are doing different things with the block diagrams. You may need to
have the labels act as ajax links that do something in one view and do
something else in another view. You might want to highlight all of the
decision points with some sort of CSS or different colored boxes in one
view and not do that in another view. And on an on. Same basic display
code in different views, but with context dependent differences.

In my implementation (analogous to the current problem), I would want to
write the display code once, and be able to configure it in various ways
in each place I use it. The question is, where does each instance of
the object keep this configuration data.

If I understand you correctly, you would put this configuration data in
the database. That doesn’t make any sense to me. Maybe I’m just dense.

thanks,
jp

Jeff,

I’ve run into problems in the past with storing objects in the
session. Inevitably you change the class and invalidate your session
objects. Then you are stuck migrating session objects or blowing them
all away (which might be ok in some cases). Whatever you want to
store in the session is data, and data can be put into a database.

That said, you can create your class in the helpers folder. Just
create a new file and put the class there (the file and class need to
have the same name - test_object.rb - class TestObject). This class
is accessible to controllers, views, and can be stored in the
session. I think the serialization method is “to_yaml” and this is
available to all objects. However, you may run into problems
serializing more complex objects.

Aaron

road. Then create a custom helper class that takes your database
object(s) and creates the view.

I agree with aaron… break it apart now… might be more work, but down
the road you’ll be better off. I’ve been coding Rails for just over a
year (but 40+ hours/week) and the several times I’ve hit this wall and
thought it really should be together after thinking it over some more I
find a way around it that is better in the long run…

view and not do that in another view. And on an on. Same basic display
code in different views, but with context dependent differences.

In my implementation (analogous to the current problem), I would want to
write the display code once, and be able to configure it in various ways
in each place I use it. The question is, where does each instance of
the object keep this configuration data.

So, if I understand you right, you want this:

data object -> one view that is really smart

instead of:

data object -> many views that are mostly dumb

If I understand you correctly, you would put this configuration data in
the database. That doesn’t make any sense to me. Maybe I’m just dense.

Might just be terminology… in your block diagrams above, I’d say put
everything about the diagram into the database that you would need to
draw
it. So, boxes, the relationships b/n those boxes, what type of box,
what’s inside the box, etc.

Then have a variety of different views to display that diagram… one
for
HTML, one for RMagick, one for PDF, one for XML, etc.

Doing it this way means you can save a simple integer into the session
to
pull that record back out of the database.

-philip

Philip H. wrote:

So, if I understand you right, you want this:

data object -> one view that is really smart

instead of:

data object -> many views that are mostly dumb
-philip

Nope, I want:

really smart data visualization helper class -> many really dumb views
that do different things with it.

each really dumb view “configures” an instance of the really smart data
visualization object to fit the needs of that view.

Imagine that one view is all about letting the user assign an
“importance attribute” to various boxes in the block diagram; another
view is about letting the user choose a box to “work on” or “complete”.
Different parts of the app doing different things with the block
diagram. Different ajax controller actions to hit, different small
changes to the appearance of the block diagram in different parts of the
app.

In other words, different instances of the helper object, each with
various instance variables that tell it how it is supposed to act in
this context that need to be remembered just as long as that view is
displayed and no longer.

jp

Wes, I just re-read your reply. Sorry, mis-read it the first time. I
tried non-AR model, and the problem is that a non-AR model lacks the
magic that makes an AR model serializable. That is needed in order to
save the object in the session. Without that you just get an error
about trying to stick an unknown object type in the session.

Even if there is a relatively simple solution to that, the object would
have to be a child of Action Pack to be useful to me and also needs
access to all of the variables and methods of the conroller and view,
like a helper does.

Jeff,

A couple of points. One, thanks for helping me realize something that
happened to me almost a year ago. I put a non-AR object into the object
graph of an object that I was attempting to store in session and the
session marshaling failed. I always thought that it was because of the
size of that object, but now I am forced to consider/realize that it was
because AR provides that serialization for us. So thanks for that. If
you have any additional info. about how this actually works in AR, I’d
appreciate.

Secondly, you said “An AR model does not have (natively) any of the
Action Pack stuff that controllers and views need to get things done in
views. Something tells me that making an object that is both an AR and
an AP would be kind of
like “crossing the streams” in GhostBusters.”

I believe that your model in this case is the view configuration itself,
and therefore such a “hybrid AR/AP object” is totally valid. Think of
it as a “view model”. Your view has state and behavior that needs to be
managed, right? Just because this object isn’t like all of your other
models (i.e. “isn’t user data”) doesn’t mean that there isn’t a mini-MVC
world going on inside the “greater view,” if that makes sense.

In fact, even with relatively simple forms, we see view state issues
come up (fill in this field when that one is checked, etc.) that may
be/should be completely separate from the model behavior. In Rails, we
are encouraged to add methods to our model objects to manage this sort
of thing, but it is perfectly valid to argue for “view model” objects
whose purpose is to handle the state of the view.

As for your problem with how to persist such an object across requests,
I see three options:

  1. Store all of this model metadata in the DB so that you can take
    advantage of the session management provided by AR. I suspect that you
    and I would agree if I said this feels like a “heavyweight” solution.

  2. Determine how to emulate the session storage scheme of AR (to_yaml or
    whatever needs to be done - BTW, I’d be interested in learning how to do
    this myself). It seems reasonable to me that the session storage
    mechanism should be unbound from the AR implementation anyhow, for just
    this reason.

  3. Come up with another, custom scheme, for managing data storage that
    persists across requests. I ended up doing this when I ran into this
    problem (just a month into my Rails development) by implementing my own
    hash and associated management.

The “right” thing to do feels like #2, but the “most possible” thing to
do feels like #3. #1 seems to be a bad choice only because you are
formally storing data that you only need for a few seconds minutes.
Having said that, you could totally do #1 and just be very strict about
how you manage those tables (lots of frequent cleanup, etc.).

I hope this helps. This is a great thread - I think it exposes some of
the (few) limitations of Rails at this point in time.

Thanks,
Wes

Wes said:

  1. Come up with another, custom scheme, for managing data storage that
    persists across requests. I ended up doing this when I ran into this
    problem (just a month into my Rails development) by implementing my own
    hash and associated management.

The “right” thing to do feels like #2, but the “most possible” thing to
do feels like #3. #1 seems to be a bad choice only because you are
formally storing data that you only need for a few seconds minutes.
Having said that, you could totally do #1 and just be very strict about
how you manage those tables (lots of frequent cleanup, etc.).

I hope this helps. This is a great thread - I think it exposes some of
the (few) limitations of Rails at this point in time.

Thanks,
Wes

Somehow I just can’t agree that this stuff belongs in the database. It
needs to expire and so on just like other session data. Much easier and
more appropriate to use the existing session management stuff to deal
with that.

#3 is what I’ve just finished doing. It works, but I’m left with one
incredibly annoying issue in this implementation…a class defined
inside a helper file DOES NOT have access to the many things one needs
in a helper such as the session hash and helper functions like “h()” and
the form helpers etc. ARGH!

I also don’t know how to get access to other methods defined inside the
helper file but not inside the class definition. I’ve got this:

module ApplicationHelper

def some_method_or_other
yadda yadda yadda form crap yadda yadda
end

class MyHelperClass
   . . .
   def my_method
     # I can't do this
     foo = h(bar_with_nasty_tags_in_it)

     # i also can't do this
     foo = some_method_or_other
     # or this
     foo = ApplicationHelper::some_method_or_other
  end

end
end

Any ideas from anyone on how I can do either/both of these things???

thanks,
jp

Wes G. wrote:

Jeff,

You should be able to use “include” to include the helper modules that
you want in your class, and then you don’t even have to define the class
inside the application_helper.rb file. You could put it in lib or
wherever.

Wes

Thanks Wes. That turned out to be pretty useful. I put the whole thing
in “lib” and used include to make it act more or less like a helper.

Talks about pulling teeth! This has been a major exercise in
frustration for me.

I guess that’s another way of saying “I learned a lot” while doing this.

One of the things I learned is that there is no such thing as an object
oriented helper in Rails…and there really should be.

I wound up manually saving three different pieces of object state info
in the session hash and re-instantiating a new “helper object” on each
request cycle. It’s working well and goes a very long way towards
drying up my app.

thanks to all who chimed in,
jp

Jeff P. wrote:

I wound up manually saving three different pieces of object state info
in the session hash and re-instantiating a new “helper object” on each
request cycle. It’s working well and goes a very long way towards
drying up my app.

Jeff,

Would you share how you did “manually saving three different pieces of
object state info in the session hash and re-instantiating a new “helper
object” on each request cycle”?

Thanks,
Wes

Jeff,

You should be able to use “include” to include the helper modules that
you want in your class, and then you don’t even have to define the class
inside the application_helper.rb file. You could put it in lib or
wherever.

Wes

Wes G. wrote:

Would you share how you did “manually saving three different pieces of
object state info in the session hash and re-instantiating a new “helper
object” on each request cycle”?

Thanks,
Wes

Hi Wes,
It is a little convoluted, but it boils down to using the session keys
for the object’s data as input params to the new method for the object.
To simplify it, I’ll demonstrate with just one session object instead of
the three I wound up using…

in a “lib” file
module MyFancyHelper
class MyFancyHelper
include ‘a butt load of stuff Active stuff’

def initialize(data_name)
@data_name = data_name
@data = session[@data_name]
yadda yadda yadda
end

def something_that_modifies_the_instance_data
@data += something or other
session[@data_name] = @data
end
end

Then in the first place I instantiate this particular object:
session[‘this_data_name’] = some initial value
@my_fancy_helper_instance = MyFancyHelper.new(‘this_data_name’)

And in the view:
<%=
@my_fancy_helper_instance.make_some_beaurtiful_html_with_fancy_css_tags
%>

And then again in each and every place where I need to use this
“instance” of the object I do
@my_fancy_helper_instance = MyFancyHelper.new(‘this_data_name’)
@my_fancy_helper_instance.something_that_modifies_the_instance_data(‘foobar’)

So the long and short of it is that whenever the object changes the data
it has to update the session, and in each action method where you need
the object you create a new one and tell it the name of the key you are
using for that one in the session hash.

Remember that an object in the session hash can be any type of object
including a hash. That hash can have many keys, each of which stores a
different type of data object. So even with just one such key name
passed in you could save any and all of your data. In my case, three
worked out better because I have three types of stuff: 1) a reference to
the primary data the object is dealing with (i.e. the stuff it is
responsible for drawing 2) some configuration data that tells this
instance how it differs from other instances (i.e. how this one draws)
3) and an instance variable that tells the object what page of the data
is currently being displayed.

This is a difficult thing to put into words. Forgive me if this is
utter hogwash. All I know is it is working well and is very
versatile…and would have been a whole lot cleaner if Rails included
the concept of a “Helper Class” in a helper module.

And a disclaimer…I’m sure there are at least a dozen better ways to do
this, but nobody offered any up and I needed to get this done and move
on. :slight_smile:

best,
jp

Jeff,

Thanks - this makes sense. If I understand correctly, you are simply
storing the internal state of each helper object into the session and
making sure that changes are updated correctly.

It would be interesting to find out what would be required to allow you
to just store the entire class.

Wes

Wes G. wrote:

Jeff,

It would be interesting to find out what would be required to allow you
to just store the entire class.

Wes

Yeah, I’m sure it could be done with the right ‘require’ and ‘include’
lines in the proper files, but I need to move on and get other things
done. I forgot to mention, BTW, that I also had to pass the session
into the constructor. Probably another ‘include ActiveSomethingOrOther’
would have obviated that nuisance, but I don’t know what part of Rails
the session stuff comes from.

cheers,
jp

Jeff wrote:

Wes, I just re-read your reply. Sorry, mis-read it the first time. I
tried non-AR model, and the problem is that a non-AR model lacks the
magic that makes an AR model serializable. That is needed in order to
save the object in the session. Without that you just get an error
about trying to stick an unknown object type in the session.

Wes wrote:

Jeff,

A couple of points. One, thanks for helping me realize something that
happened to me almost a year ago. I put a non-AR object into the object
graph of an object that I was attempting to store in session and the
session marshaling failed. I always thought that it was because of the
size of that object, but now I am forced to consider/realize that it was
because AR provides that serialization for us. So thanks for that. If
you have any additional info. about how this actually works in AR, I’d
appreciate.

Just to correct this on the record. Jeff, I don’t what the hell I was
saying agreeing that a non-AR model can’t be stored in the session.
It can, and I’ve done it several times. For some reason, I guess I was
feeling easily influenced :).

Perhaps there was something else about your helper that made it “not
marshalable” and kept it from being stored. I don’t know. But I am
sure that you can store a non-AR descendent in session. I believe the
restrictions on being able to store an object in the session are

  1. is the object serializable (no procs, etc.)
  2. is it smaller than some threshold value based on the session storage
    scheme (PStore, DB, etc.)

BTW, I just finished reading chapter 10 of The Ruby Way by Hal F.,
and it has a great discussion of marshaling and various alternative
storage schemes (DBM and Kirbybase to name a couple). This book is
incredible and is making me into a better Ruby programmer with each
chapter.

Thanks,
Wes

Wes G. wrote:

Just to correct this on the record. Jeff, I don’t what the hell I was
saying agreeing that a non-AR model can’t be stored in the session.
It can, and I’ve done it several times. For some reason, I guess I was
feeling easily influenced :).

Wes

Don’t know. I’m starting to think I just had bad Ruby mojo working that
day. Nothing, and I do mean nothing wanted to work. Probably should
have just drank a beer and watched it rain. Would have been just as
productive.

Anyway, Ruby threw my stuff out the window, so I picked it up and moved
on. Maybe she’ll let me back in tomorrow. :slight_smile:

jp