On Aug 24, 2010, at 9:07 , Lars Olsson wrote:
>
> Which renders (a very ugly) form. I guess there are tons of options
> you can add, but since I prefer to code my forms manually I haven't
> looked to closely.
Felipon, I started by trying to use BlueForm myself, and have scrapped it pretty much for Lars' reason: the form pops out in a big lump, with no way to organize the pieces.
When I first started working with Ramaze, I spent quite a bit of time scratching my head and beating on things with a big code stick. I'm happy to report that, at least for now, things have gotten much smoother.
OTOH, a lot of what I've been doing, I think, is just bypassing large chunks of Ramaze. I've deleted the 'layout' directory entirely. My app is down to "/controller," "/model," "/public," and "/spec." (I have no idea what /spec is for. {shrug})
This past week, I believe I've been merrily desecrating the whole MVC paradigm, since I've been teaching my Sequel data objects to format themselves for web page forms. I spent quite a bit of time trying to find a replacement for BlueForm, but everything I found fell far short of what I expected, and am used to. I figured that the previous system I'd used, which I started working with in 1998, would have been totally superseded by what I could get now, but that doesn't appear to be the case. I blame this "MVC" thing. I'm used to data that is context-aware. If I assign a 2-dimensional array to another variable, I get, well, obviously, a copy or reference to a 2-d array. But if I put that same array into the middle of a web page, the contents would appear in the browser automatically wrapped in <table<tr><td>... formatting.
So here's what I ended up building for myself....
Let's say I have a Product. A particular product has a color, which is actually chosen from the Color table.
p = Product.new
p.description = "Fleece Hoody"
p.color = Color[:description => "blue"]
If I want to let somebody edit the Product data, p.name needs to become
<input type="text" name="description" length="30" value="Fleece Hoody">
and p.color should be a pop-down selection menu:
<select name='color'>
<option value='1'>Red</option>
<option value='2'>Orange</option>
<option value='3'>Blue</option>
</select>
Currently, I handle this as follows:
[in model.rb]
class Product < Sequel::Model
include FormHTML
many_to_one :color
end
class Color < Sequel::Model
def formHTML
out = pit(:select){self.class.select(:id, :description).all.collect do |row|
option = pit(:option){row[:description]}
option.add_attribute("value", row[:id].to_s}
if row[:id] == self[:id] then option.add_attribute(REXML::Attribute.new("checked")) end
option
end}
out.add_attribute("name", self.class.to_s}
end
end
[in my controller]
tbl = table{[
['Product Description ', p.formHTML(:description)],
['Color', p.formHTML(:color)]
]}
tbl.class="list"
pit(:form) do [
{:method => :post, :action=>"save"},
p.formHTML(:id),
tbl,
pit(:input){{:type=> :submit, :value=>"Save Changes",:style=>"margin-right: 4em; margin-top: 1em;"}}
] end
"pit" is short for "page item", and it creates a REXML object: "REXML::Element.new(name.to_s) <= yield(contents)" The "<=" is something I taught REXML; it means 'examine the stuff on the right, and figure out an appropriate 'add_' method to insert it into the left.' 'Table' expects an array of arrays in its block, which it then parcels into the appropriate <td>s and <tr>s. The output (I'm going to simulate this by hand, so it might not be spot-on) is basically
<form method="post" action="save">
<input type="hidden" name="id" value="43253">
<table class="list">
<tr><td>Product Description</td><td><input type="text" name="description" length="30" value="Fleece Hoody"></td><tr>
<tr><td>Color</td><td><select name='color'>
<option value='1'>Red</option>
<option value='2'>Orange</option>
<option value='3'>Blue</option>
</select></td></tr></table>
<input type="submit" value="Save Changes" style="margin-right: 4em; margin-top: 1em;">
</form>
If I tell a data object I want the formHTML for a field that's a primary key, then it gives me the HTML for a hidden input item. Char and Varchars become <input type="text" length="30">, while integers become <input type="text" length="5">. "Color" isn't a field of the Product object's table, it's an association, so my Product asks the Color for *it's* form code, and passes that back.
MVC would suggest I not be teaching my models about how they're supposed to look, but it's simply impossible to programmatically build decent forms without deep understanding of the model, and having the controller isolate them means pages and pages of inefficient 'glue' code, I think. Even the Ruby Object/Sequel Model doesn't really have enough information. It presents "char," "varchar," and "text" types as a Ruby String, but if the underlying database type is "text," then my formHTML is going to be a <textarea> not an <input type="text">, for example.
This is still a work in progress. I think I'll be able to replace the hand coding for list tables like Color with an automatic assembler like Product uses, but I haven't worked on that part yet, for example.
--
You received this message because you are subscribed to the Google Groups "Ramaze" group.
To post to this group, send email to ram...@googlegroups.com.
To unsubscribe from this group, send email to ramaze+un...@googlegroups.com.
For more options, visit this group at http://groups.google.com/group/ramaze?hl=en.
> Layouts are pretty important if you don't want to be repeating headers and footers in every single page. Here's something I wrote a while back:
>
> http://steamcode.blogspot.com/2009/01/using-pagexhtml.html
>
> on this topic.
>
> Scott
Aha.
I created class WebPage to hold the repeating material. I might go back and look at layouts at some point, but my original problem was the amount of programmatically determined material I use. For example, the title of the page as it appears in the head. Simple assignment to a variable, embed the variable. Ok, fine. Except then I wanted the same thing to appear in a header on the page, with formatting. Now I have to embed some code in the template to show the title in that manner. Many of them made this complicated and cluttered. I think the straw that broke the camel's back was when I need to present *pre-formatted* text, and HAML fought me tooth and nail by escaping my text; thus destroying the formatting.
(If this is unclear, I'm talking about data that includes Linnean names, which need to have parts of them italicized. This means the string that comes from the database looks like "<i>Bryonia cretica</i> L <i>ssp dioica</i>" I've fired three different layout schemes and two different XML-handling gems for aggressively escaping my data, instead of believing me when I would tell them 'It's ALREADY XML data, quit screwing with it.' I finally managed to beat REXML into submission, mostly. Libxml, for example, was far too anal-retentive in this regard.)
So now I'm doing layout by manipulating some arrays and some instance variables of an instance of a web page. I can add stuff to the top of the page at the bottom of my code, or vice versa, I can analyze and/or modify stuff I've already "written" to the page, and don't have to jump back and forth between two files to figure out what my page will look like. I embed the HTML in the Ruby Code, rather than vice versa. I find it much easier to read, although that's in part because I'm minimizing how much HTML has to BE visible in my code in the first place because most of it comes out of my data and goes onto the page with a minimum of glue.
> The possible "views" of your
> data is endless. You *can* tell your data how to look in specific
> situations (ie a web page) but that what happens when you want to
> expose your data in a new enviroment? You have to teach your *data*
> new tricks all over again...
It is, of course, still also just data. I don't *have* to activate the self-formatting behaviors. However, very few of these 'new environments' are relevant. Please recall that the focus of my effort has been teaching my data how to present itself as part of an HTML <form> . . .</form> environment, which is far more complex. Knowing that it's a String or an Integer isn't remotely adequate to correctly construct the editing field object, especially since many of the pieces of data are actually nothing but id codes providing foreign key linkages to other tables.
> Let me share an example from my own site.
>
> I run a blog. The blog contains blog posts. Depending on what role I'm
> currently having (author or reader) I want to have a different view on
> the data. If I'm he author, I should be able to edit the blog post and
> change settings for it. If I'm the reader I should not be able to edit
> the post but be able to add a comment. Is it really the responsibility
> of the *data* to decide what to display?
My answer would be "that depends." In this case, the data is very simple, the different forms in which it appears are each required on very few pages, and Ruby contains built-in commands that can apply the necessary formatting with very little code, so no, not for this blog.
>> Lots of code for transforming data to HTML
>
> I don't think your code violates MVC in any way. You're now teaching
> your model how to display itself, your teaching the controller/view
> how to display the data. So you're actually doing it the MVC way, even
> if you don't think so... ;)
I don't mind being wrong about that. {chuckle} I don't really feel like I have a solid understanding of that paradigm in the first place.
> I *know* my blog has a title. I *know* my blog has a tagline. Why
> should I need to have a whole library figuring out on how to display
> the data. I already know how I want to display it.
I think the key difference here is, your data is really simple. Your title is an ASCII (or Unicode) string. Your tagline is a string. Ruby has readily available tools for formatting strings.
> I guess what I'm trying to say is that even though BlueForm
> (,SequelForm, whatever) might have its use cases, I find the most
> common case to be that you want to display data that you already know.
Aha, and hmm. I have not taught my data how to simply display itself. What I've been teaching it, is how to wrap itself in the HTML required *for a form*. And some of my data is so much more complex than a string that even the Sequel library can't handle it. (Specifically, i've managed to eliminate at least four linking tables by taking advantage of the fact that Postgres supports an "array" type for a ***column*** of a table, which Sequel returns inadequately as a concatenated string, not as an actual array.)
Here's an actual "object" I'm working with. The company makes botanical medicine. When they made a batch of some particular stuff, they'll take some herb or plant, do things to it, and put the results in jars. This is a Batch in the database. The batch has intrinsic properties like when it was made and how many jars there were. It also gets a human-assigned code number. But that code number isn't very helpful; when the staff interacts with a batch, they're thinking in terms of what plant was used to make it. More accurately, in terms of the standard nomenclature for the plant used, which parts of that plant, and characteristics of the results of processing it.
However, the plant used is NOT intrinsic to the batch. The batch is, in effect, an instance of class Extract, the 'recipe' for that batch. The plants used in that batch came from a Lot, a big box of plant stuff shipped from a supplier. The Extract represents the process used to turn certain PARTS of the plant into medicine. Finally, even the plant does not intrinsically have a name. It has MANY names (current Latin, common name, Family name, USDA code name, obsolete name, misnomers, and more).
So when I display information about a Batch on the Batch information page, one of the things I include is the Extract that this is a batch *of*. To give them some useful way to identify the extract, my list includes the extract's code number, and a textual description, for example, "Tincture of fresh _Crataegus monogyna_ flower and leaf". I will present that same text on the Extract page itself as the title of the page, on the "_Crataegus monogyna_" page as part of the list of extracts made from it, and probably on some report pages yet to be written.
But that information does not exist in that form in the database. The extract could contain multiple herbs, with each plant being used fresh or dried, and with only certain parts used. So there's a linking table between the extracts and the herbs, which in turn has a linking table to the table that lists the possible plant parts that could be used. "Tincture" is a property of the extract (it's a tincture because alcohol is used as the carrying solvent.) "Fresh" is a property of the plant<-->extract link. "_Crataegus monogyna_" is defined by following the plant<-->extract link to the plant, then going to the plant names table, and THEN to the plant name *categories* table to find out which of the plant names is the Primary name. The name may or may not have a formatted version available. The common name of this plant is "hawthorne," and the common name doesn't have a formatted version, because it doesn't need it.
So when I ask the Extract to give me its "name," the model pulls together all that data from all those tables. It formats the 'part' information depending on how many parts there are. If there is a "leaf" part and a "flower" part, I'll see "flower and leaf." But if there's a leaf, a flower, and a fruit, then I get "leaf, flower, and fruit."
Finally, if the Extract's name includes a Linnaean Latin plant name, then instead of a string, myextract.name returns a REXML "span" object that contains the other info.
Now think back to the Form of this extract, which happens to be "Tincture." An Extract has a form, and batches have forms. Products that are made from mixtures of batches also have forms ("Tincture," "Glycerine," "Cream," "Oil," and so on). I have multiple site pages that allow editing these things, and all of them need to let the user choose what form that thing is. THIS is where teaching my data how to self-format is tremendously helpful. I simply include someExtract.plants.each do plant . . . plant.form.html . . . end. The data itself is simply a uuid for that form in the Forms table. But when I ask it for it's editable HTML representation, it returns a REXML <select> object containing <option> objects for EVERY row of the Forms table, with the specific entry that matches the current object sporting the "selected" attribute.
That's a fairly large pile of code to construct the drop-down form component for setting the form, and 'form' is a property of a number of different primary classes in the database.
When I ask for someExtract.plants.parts.html, instead of a <select> object, I get a <table> object that contains a bunch of <checkbox> objects, since the number of parts used is one or more.
In a nutshell, the data that I'm working with aren't things that can be represented by Ruby's standard types. These are relational attribute sets that are properties of multiple classes of objects in the database, and it's *much* easier for me if they can format themselves rather than making me do it over and over again.
=======
I learned a lot from writing this response, not the least of which is that I now have a much better understanding of why Rails was such a frustrating disappointment when I tried to use it for this job. The idea that I could construct my schema from within Ruby (whether using Sequel or ActiveRecord or whatever), and/or *migrate* it to some other arbitrary database, still fills me with mirth.
I do wish that there was an object-oriented database with the maturity of Postgres, SQLServer, Oracle, et. al, but so it goes.
> Your job seems fascinating :) I can agree to that you're having to
> deal with quite complex objects, but there is still a part that I
> disagree to.
Excellent, and thank you for disagreeing. Or, rather, thank you for taking the time to understand my wordy explanation, and offer thoughtful criticism. I'm sure, one way or another, my code will improve as a result of your comments.
> you are still facing the same job if you want
> to display the data on a web page: Transforming the data to strings.
True.
> What I'm simply suggesting is this. Move the generation of HTML to the
> view. Just pass your data object (however complex) to the view and do
> the generation there. Your controller would simply do:
>
> mycontroller.rb
> ---------------
> class MyController < Ramaze::Controller
> def display(id)
> @data = get_insanely_complex_data_by_id(id)
> end
> end
Hmm. So currently, when my controller wants the data to transform into a series of strings suitable for an HTML form, it calls a method on the Model object. With your snippet, I would instead have the controller call its own 'display' method, and pass, er, pass an ID? Is this functionally similar to just passing the Model object?
So, one way or another, I 'get' my Model object representing some agglomeration of information from the database. This gets assigned to @data.
> If you need some library to transform your data variable to HTML you
> can still do it in the view. Everything thats available to the
> controller is also available in the view:
>
> display.html
> ------------
> <html>
> <head>
> <title></title>
> </head>
> <body>
> <?r transform_my_data_to_pretty_html(@data) ?>
> </body>
> </html>
Hmm. Well, one trivial change would be
> class MyController < Ramaze::Controller
> def display(id)
> @data.push(get_insanely_complex_data_by_id(id))
> end
> end
I'm displaying an indefinite number of objects, depending on the page.
But the heart of the matter is that the reason I put this code in the Model in the first place is because, no matter WHERE I put it, I can't see any way to make it work without crossing the Model/View barrier.
When I ask a Model to give me the formHTML for method :fewbahr, it first has to determine what kind of method :fewbahr is.
def formHTML(field=nil)
if field then
case
when self.class.foreign_keys[field] then
nil
It is possible to ask for the field that contains the ID used by an association. It doesn't make any sense to use it, so no HTML is returned. To reference my earlier example, a Batch has an .extract_id, but normally I would ask for the .extract itself, which is a method Sequel creates as a result of having "many_to_one :extract" in the definition of a Batch.
when self.class.associations.include?(field) then
if self.send(field).respond_to?(:formHTML) then self.associations[field].formHTML end
So if :fewbahr refers to a method representing an associated object, I determine this by asking my Model's class for the list of associations, then asking the associated object if *it* responds to an un-parameterized formHTML command. If it does, then I call it.
when field==self.primary_key then
pit(:input){{:type=>"hidden", :name=>field.to_s, :value=>self[field]}}
I'm using UUIDs instead of the traditional sequential integers as keys for the data in my database. By the time Ruby sees a key, it's just a string.
This is why I didn't see any point to putting this code in the view instead of the model. The view can't possibly hope to format it properly unless it is prepared to pull a lot of information out of the Model. It has to understand Models intimately in order to do this. If I understand MVC correctly, what *ought* to be going on is that the Controller would get the core data from the model, including such information as which parts are primary keys, which are associations, and whether the associated objects should be seen on the page in some way, transform it, then pass it to the view, which would take information like "this string should be hidden, that one should be regular text, this one should appear as part of a group of radio buttons," and in turn, construct the appropriate HTML to instantiate those principles.
If I did do it that way, then, indeed, it would be a pretty clean process to swap out "html" and swap in "Access," or maybe "PDF," or some other front end that could let a user fill out and submit a form back to the Controller. But the cost of that flexibility is having to write the code twice; once to convert the Model's structure to something abstract, and then do it again to make HTML from the abstracted form, and creating forms for this data is currently a huge percentage of the total code body. I just don't expect we're going to need or want alternative front ends for this, and the time required to design the intermediate stage, and write and debug double the code (roughly), seemed far too high for the benefit.
Back to the code, briefly. If :fewbahr turns out to be the name of one of the actual columns/fields of this table in the database, then we need a couple more 'case' statements to figure out what to do.
when self.db_schema[field.to_sym] then
case self.db_schema[field.to_sym][:type]
when :string then if self.db_schema[field.to_sym][:db_type]=="text" then
Ruby calls all the text data "Strings," but the database cares about the difference between short strings, which are represented by "char(n)" and "varchar" types, and large blocks of text, which are type "text". If the database is using a "text" column, then the HTML almost certainly needs to be a <textarea>, instead of an <input type='text'>.
pit(:textarea){ [{:name=>field.to_s, :cols => 80, :rows => 8}, self[field], " "] }
else
pit(:input){{:type=>"text", :size=>30, :name=>field.to_s, :value=>self[field]}}
end
when :integer, :decimal then pit(:input){{:type=>"text", :size=>5, :name=>field.to_s, :value=>self[field].to_i.to_ss}}
I'm probably going to add code that again looks back at the database's original data type. "Numeric" includes a definition of how big the number can be, and it would make sense to make the entry field on the form that size as well.
when :float then pit(:input){{:type=>"text", :size=>15, :name=>field.to_s, :value=>self[field]}}
when :boolean then
ffield = pit(:input){{:type=>"checkbox", :name=>field.to_s}}
if self[field] then ffield.add_attribute("checked", "")end
ffield
when :datetime, :date, :time then pit(:input){{:type=>"text", :size=>12, :name=>field.to_s, :value=>(self[field])?self[field].strftime("%Y %b %d") : "" }}
This code is also likely to change drastically. There *is* some data in my database where the time of day is relevant, although I haven't started coding against that part of the database yet. I think I'd like to make this be drop-down menus for picking the month and day, and possibly the year, at least in some situations, but then I have to convert the bits of data that come back from the form and re-assemble it into a Date.
Last, but certainly not least, is the fact that there's a custom enumerated data type used in the database. The 'hydro' type can have the value "fresh," "dried," or "?". Again, by the time it reaches the Controller, it's just a string. If I were going to make this code universal, I would need to check every field's native database type, and if the result wasn't on a list of known types, try to get the database to tell me if it's an enumerated type, and if so, what the legal values are.
else
case self.db_schema[field.to_sym][:db_type]
when "hydro" then
pit(:select, {:name=>"hydro"}){['fresh', 'dried', '?'].collect do |hy|
pit(:option, {:value=>hy}, (if hy==self.hydro then Selected end)){hy}
end}
end
end
I know. It's a 'case' statement with just one lousy case. The reason it isn't just an ordinary "if" statement is that I'm expecting at some point to add a few more special cases to the list.
end
end
end
Another piece of the puzzle that neither the Model nor the database itself yet understands is that there are basically three different kinds of tables. The ones holding 'real' data, the linking tables used for many_to_many relationships, and the "list" tables that are, in effect, a custom Enumerable data type. Adding, say, "baked" to the list of legal types for "hydro" would require executing SQL code against the database to redefine the type. Adding a new plant part to the list of plant parts available is just a standard insert into the appropriate table. If a table is being used to store a list, then it does not get the above code included in it. Instead, it current gets its own private custom definition of formHTML, The 'forms' table doesn't really have any way to know that other objects in the database can only exist in one form, just as the plantparts table doesn't know that an extract can use multiple parts. As a result, (again, currently, and subject to improvement), there's a custom formHTML method defined on the Form model so that an instance of this table can return HTML that includes the entire table, but with the line that represents itself selected. With plantparts, on the other hand, formHTML is defined on the *class*, because I have to pass it a list of the parts that should be checkmarked in its HTML representation.
> Hello!
>
> I feel that this discussion has started to drift away quite a bit from
> the original question. Maybe we should start a new thread?
Well, I've already renamed it once. I think you're right, so I'll rename it again. Renaming vs. starting a new thread means people who are trying to follow it, or trying to NOT follow it, can continue to do so. (For example, if/when this whole thing gets archived on one of those sites, and somebody clicks "next message in thread.")
[definition of MVC elided]
Yea, I completely agree with that.
> Now on to the question on how these definitions fit on Ramaze.
>
> (Model)
> =======
> Ramaze really does not have any predefined model.
Right. This definitely threw me when I first tried to replace Tango. Tango had an IDE, and embodied the database/ODBC connector, the general processing language, and handled rendering. Apache handed a query to Tango, and got back HTML to send to the user. I was rather dismayed when I found that all of the 'modern' Ruby-based tools were more like an old-fashioned Chinese menu. "Select an item from column A, one from B," and so on.
Eventually I figured out I needed to pick my data-object-widget (ActiveRecord, Sequel, etc.), my overall managing framework (Rails, Ramaze, Nitro, Wee, Sinatra, IOWA, and on and on and on), my templating engine (HAML, or, geez, I don't even remember all the ones I looked at), and possibly other bits.
OK, in the end, I have to agree this is probably a good thing. It was just a real head-scratcher to start.
> I *really* prefer the approach Ramaze is taking. Let *me* decide on
> how the data should be modeled.
I am willing to bet that YOU do not appreciate Ramaze's model-neutrality as much as *I* do. {laugh} Even Sequel has made some assumptions about my data that aren't true, and it is *so* much less prone to assumptions than pretty much every other data object modeler I looked at. {shudder}
> (V)iew
> ======
> Here's a little secret: Ramaze has no views! Well, actually it does,
> but if you look in the source code you will see that Ramaze::View does
> not do a lot by itself. The main work is instead done by the different
> "engines" that Ramaze provides (Etanni, Haml etc). And the engines are
> tied to the controller, not the view. Views aren't really independant
> objects in Ramaze IMO.
I'm not entirely sure I understand this. M, V, and C are conceptual models, not physical ones. I mean, it's all in Ruby in the end. What makes a particular pile of code more or less in line with the MVC paradigm is the degree to which the final appearance of the data is separated from the management from the storage. Maybe it depends on what you're defining as "the view?" I suppose one *could* use the image presented in a browser window, for example. I tend to work backwards, and include the stream of HTML that is ready to be shipped across the network as the *product* of the 'view'.
> What Ramaze really does when a view is rendered is this:
> 1) The controller calls the action (public method in controller, ie
> index) . Now the controller has the data (stored in instance
> variables).
> 2) The controller calls the engine with the collected variables, a
> layout (if defined) and the desired template name.
> 3) The engine compiles the variables, the layout and the template into
> a string
> 4) The string is returned to the browser (after it goes through the
> rest of the Rack middleware)
>
> So, in Ramaze the View part is actually performed by the controller.
{scratch head} I think there's still an implied view "place" that doesn't match mine.
Both models and views almost require transformations. My "model" is both what's in Postgres, and what's in my "model.rb" file. The model.rb file, which mostly makes calls to things in the Sequel library, transforms the data in Postgres. It rearranges it, reorganizes it, changes the types, and such. I still include all that in the "M" section, not the "C" section.
Ideally, the Controller would never have to *do* anything, as such, to the data from the models. It would look at it, and based upon information in the model, and information coming from the view (e.g. what link somebody clicked on), it would decide which parts of the Model to pass to the View, possibly with additional semantic information. In other words, "Here are five blog entries. The first one is the featured item, and the other four should be considered comments on it."
The view then has more transformations to handle. A blog entry has a title, and an author, and a body, and whatever else. If it's a main one, maybe there's some bolding, or it goes at the top, or it gets a green back ground. Whatever. So it might throw away some of the data passed to it, create a bunch more information (probably markup related), and rearrange some of the rest, then ship THAT off to Acrobat, or the web server, or a printer.
> At first this was very confusing to me, but then I realized that it
> really was quite a clean solution. The controller already has the data
> and a separate view object really isn't necessary. This of course
> violates the MVC paradigm a bit, but since it all happens behind the
> scenes you don't need to care as an application developer.
Hmm. Yea, I'm still not sure what "a separate view object" would look like. It seems to me that the engine, and the stylesheet, embody the view.
> People
> (including myself) tend to do things in the controller that really
> should be done in the view (template). Like I said before, in Ramaze
> those two are technically the same object, but that does mean
> everything goes "conceptually". To be more specific, methods that
> "render" stuff should ideally be in the template, not the controller.
> IMO, the only reason *not* to place rendering in the view is when you
> don't use a template (like generating an image via RMagick).
Methods that render stuff should be in the view. Which pretty much means that any method that's doing controller-y things shouldn't also be doing view-y things. I don't see any problem with having a single class with both the controller methods and the view methods, as long as they're separate methods. If you could an OtherView module into the class and complete change the appearance and output of the data without altering the controller logic, then the Controller and View are indeed separate, even though the code for them both is in the same class in the same file.
The view might or might not have some kind of template, or Template. A view has to have *some* kind of structural skeleton into which the data will be assembled, but that doesn't necessarily mean it's going to go looking for a particular file in the /templates folder, and use it like some kind of 'fill in the blank' guideline. If that's what you mean when you say 'template,' then I would assert that you should NEVER have methods in the template. The data should be transformed by the view before the view inserts it into the template. I have never seen a scheme for mixing them together that didn't rapidly become illegible.
That is no small part of why I abandoned using a templating engine as part of my view. As much as possible, I'm trying to put the "view" into the CSS file. I'm still doing things like putting <table><tr>...</table> text around data in non-view areas of my code, but I'm NOT saying <table align="center" border="1" color="green"><tr size="+20%">... Thus, a lot of the code to create the final appearance is in the CSS file. It's a major part of my view
I completely agree with your conclusion, in general. Dividing a program into a Model, a Controller, and a View makes it much easier to replace or expand the program's function in the long run. And that's why when I posted my reply to, oh, heavens, whoever asked about Ajax way back when, I said "but I'm pretty sure my solution isn't MVC compliant."
My contention has never been that there aren't very good reasons to use the MVC model. My contention has been that, in my particular case, it's too expensive for the benefit.
If I were to enforce strict isolation between M and V in my situation, then the only way that the View can properly build a form for editing a Batch is if the data passed to it included all the data of the extract that the Batch is a batch of, the plants represented in that extract, the parts of the plant used for each plant, *and the complete list of all possible plant parts*, because with out that last item, the view cannot render a selection list for the user to add or subtract parts.
Remember, what parts of a plant are NOT being used is not part of the Sequel object, but my Model code is "closest" to being able to get that information, because it already has objects at hand that can pull other data from the database. The View, on the other hand, does not (and really should not) have any direct access to Postgres.
The Controller could, I suppose, acquire some of this information from the Model and provide it to the View as part of the initialization of the whole system. "Dear View, here's some data you'll need later on for rendering stuff." But I really want my final output to be fresh and up to date, so what I would have to give my View was an object that embodied a live view into the database.
At this point, it's gotten so preposterously complicated, that I can't imagine actually gaining more benefit than the cost for maintaining MVC separation.
Now, I'm *not* overriding any of the model's regular methods. So just as currently the Models have gained a .formHTML method, they could also get a .formPDF method, or a .formCocoa method (if I wanted to create a native OSX app). And, a lot of the code for .formHTML is in my FormFields module. If I can figure out how to move all of the code into the module, then I would maintain that I have, in fact, effectively maintained MVC isolation. I could replace the code in my Formfields module and completely change the way forms are presented without needing to rewrite any of the controller or model code. The fact that the Formfields module is being included into Model objects instead of some other object is a red herring.
You almost got it right :)
Controller doesn't handle rendering anymore, that's been moved into
Action a few years ago.
http://doc.rubyists.com/ramaze%2binnate/Innate/Action.html
http://github.com/Ramaze/innate/blob/master/lib/innate/action.rb
http://github.com/Ramaze/innate/blob/master/lib/innate/node.rb#L368
--
Michael Fellinger
CTO, The Rubyists, LLC
>>
>> Remember, what parts of a plant are NOT being used is not part of the Sequel object, but my Model code is "closest" to being able to get that information, because it already has objects at hand that can pull other data from the database. The View, on the other hand, does not (and really should not) have any direct access to Postgres.
>
> Are you saying that it is not possible to alter the model so that it
> only contains the relevant data? A Sequel::Model is only a object, if
> necessary you can always wrap it in another object that returns "only"
> the data you want. Data can be molded any way to create the model
> *you* want.
It's not a matter of "only." I have to access *extra* information in order for a view to be able to work. It's not enough to know that this particular component uses the leaves and flowers of a plant. "Leaves" and "flowers" are two members of the set "parts of a plant," and my view needs to know *every* member of that set in order to construct a form.
>> The Controller could, I suppose, acquire some of this information from the Model and provide it to the View as part of the initialization of the whole system. "Dear View, here's some data you'll need later on for rendering stuff." But I really want my final output to be fresh and up to date, so what I would have to give my View was an object that embodied a live view into the database.
>
> I'm not sure I understand this. The controller pulls data "live" from
> the model. Why does it need to be pre-initialized?
Not the controller. The view. The view would need to be given information like "the complete membership of the set of plant parts," and "the complete membership of the set of product forms," and so on.
>> It's not a matter of "only." I have to access *extra* information in order for a view to be able to work. It's not enough to know that this particular component uses the leaves and flowers of a plant. "Leaves" and "flowers" are two members of the set "parts of a plant," and my view needs to know *every* member of that set in order to construct a form.
>
> Then the whole set *is* your model.
Yes, but I don't generally want to have to hand the *entire* model to the view every time something happens.
>>> I'm not sure I understand this. The controller pulls data "live" from
>>> the model. Why does it need to be pre-initialized?
>>
>> Not the controller. The view. The view would need to be given information like "the complete membership of the set of plant parts," and "the complete membership of the set of product forms," and so on.
>
> I'm sorry, but I still don't get it. It doesn't matter in which part
> the rendering occurs, the data would still be "live" anyway. Why would
> the view need to know the data long before it is rendered?
Let's create a model of people. There are lots and lots of people. Somebody wants to get information about one of them. So my controller takes data about Fred, and passes it to the view. The data includes his name, his eye color, and what he's wearing.
Now, if the view's job is to create a form for editing details about Fred, then a text entry point is fine for the name. But in order to allow editing about eye color, just knowing Fred's eye color is not enough. The view has to know every member of the set Eye Color, in order to construct the pop-down menu. Likewise, it needs to know the full membership of Clothing, or it can't give the user the option to add a hoodie to the pants that Fred has on.
If I were going to go back and rewrite my code, after this discussion, I might do it so that an instance of a primary object Model like a person, with the attached secondary object instances like Clothing and Eye Color, would also include methods for determining the full membership of the set to which a secondary instance belonged. That would let the View do the actual HTML conversion. Well, basically, that's what I did, except that the new methods don't return an abstracted list of objects for the View to use, they return REXML objects intended to be directly included on the web page.
Hmmm.
I know when I was looking at the various templating engines, Haml impressed me as the only one that seemed to actually be *doing* anything significant. All the rest were various trivial variations of the old-fashioned server side include. Still, looking back at it now, what Haml does is, for my purposes, 'too little, too late.' It provides an elegant way of describing a page of HTML, but I expect my view to build the d*** page on its own, without me having to tell it when to use a list, and what form fields to use for which objects.
So if I were going to implement my current system as a more general-purpose, and more MVC-compliant, tool, I would have to provide extensions to Sequel to simplify which database tables were set lists (Sequel already has tools for identifying linking tables), and would have to construct some kind of transformation module for the view side to convert those abstract objects into display code, whether that code was HTML, or REXML objects, or Haml, or whatever.
Well, I definitely do not understand the 'view' side of Ramaze well enough to try that yet, and it isn't clear to me if anybody else would really care even if I did. {chuckle} As Lars noted a few days ago, it would seem that the data structures I work with are quite a bit more complex than what most people are dealing with; maybe simple templating is entirely adequate for most people's needs. {shrug}
--
You received this message because you are subscribed to the Google Groups "Ramaze" group.
To post to this group, send email to ram...@googlegroups.com.
To unsubscribe from this group, send email to ramaze+un...@googlegroups.com.
For more options, visit this group at http://groups.google.com/group/ramaze?hl=en.
> Maybe I'm missing something, but let's look at your Fred, eye color, clothing example. I'm going to assume that there's a people table in the database where you'd do something like:
>
> @fred = People.find(:name => 'fred')
>
> and an eye color database and you'd want all of the eye colors:
>
> @eye_colors = EyeColor.all
>
> and a Clothes table:
>
> @clothes = Clothes.all
>
> then all three variables with all the information you need is available for the view. The above would of course be done in a method in the controller.
>
> Am I missing something here?
Hmm. I'm not sure.
Well, for one thing, if EyeColor is a Sequel::Model (in which case, it would have to be Eyecolor), then "Eyecolor.all" will get the contents of the table, probably as an array of hashes. So if somebody adds a new eye color to the database, nobody's going to see that until Ramaze is restarted. That's why I mentioned 'live data' earlier.
But let's assume
@eye_color = Eyecolor
Now if we do something like "@eye_color.each {|color| blahblah}" we'll get live data from the database.
Let's see. What happens next?
myView.stuff_you_need_to_know_to_make_a_form(@eye_colors)
myView.add_form_for(@fred)
Actually, it would have to be more like
myVew.add_exclusive_list(@eye_colors)
myVew.add_nonexclusive_list(@clothing)
Hmmm.
One thing to keep in mind is that to make this example correspond more accurately to my data, we have to add Doll and Puppy objects, which also have .eyecolor and .clothing properties. Recoding for editing eye color on multiple pages isn't very DRY.
So I *could* have a partial template for eye color that was something like
<select name="eyecolor">
@eye_color.each do |ec|
<option value="ec.id" if @currentObject.eyecolor == ec then "selected" end >ec.color</option>
end
</select>
[Obviously, I have mixed Ruby and HTML willy-nilly here, in a way that would never work with a real templating engine, but hopefully you get the idea.]
Then my main template would look something like
<form method="post" action="enter">
<input type='hidden' name='id' value='@currentObject.id'>
<input type=text name="name" value='@currentObject.name'>
#include eyecolor-form
#include clothing-form
</form>
Hmm. Interesting.
--
You received this message because you are subscribed to the Google Groups "Ramaze" group.
To post to this group, send email to ram...@googlegroups.com.
To unsubscribe from this group, send email to ramaze+un...@googlegroups.com.
For more options, visit this group at http://groups.google.com/group/ramaze?hl=en.
> If someone adds a new eye color to the table, the next time you hit a controller method that requests it, you should pick it up and display it (unless I'm missing something here).
Yes, but that happens ONLY if @eye_color = Eyecolor, NOT if @eye_color=Eyecolor.all, unless I include that not in an initiialization routine, but in the main body. That seems verbose and inefficient to me.
> Warning: Sequel voodoo ahead! Ramaze will only be a bysander in this
> post.
>
Wow. Thank you, Lars and Scott, for taking the time to do some coding for this.
It looks as thought I still might not be communicating very well, though.
[Lars's code for initializing table 'eyes' deleted for brevity]
> # Create a model that maps to the previously created table
> class Eyes < Sequel::Model; end
>
> # Try getting some data out
> all_eyes = Eyes.dataset
Interesting. I would have just done
all_eyes = Eyes
The following code works the same in either case:
> # Print results
> all_eyes.each do |ec|
> puts "#{ec.id}\t#{ec.color}\t#{ec.description}"
> end
> As shown above, Sequel models are actually very flexible.
Oh, yes, I know. I've already extended mine to provide better support for the UUIDs that I use as primary keys, and rudimentary support for Postgres's 'array' data type.
Now...
SQLite3::Database.new('bodyparts.db') do |db|
db.execute("CREATE TABLE IF NOT EXISTS people (
id INTEGER PRIMARY KEY ASC,
name TEXT,
eyecolor INTEGER)")
I'm not sure how to create a foreign key relationship from within Ruby/Sequel, and IIRC, SQLite doesn't support them anyway, but we don't need it defined on the database for this example.
class Person < Sequel::Model;
:many_to_one :eyes
end
newguy = Person.new
newguy.name = "Fred"
newguy.eyecolor = Eyes(:color => "blue")
newguy.save
Now, it's time to get serious, and jump over to Scott's code. I will assume there is a template for "index" that iterates through an instance variable called @everybody, and lets you click on one to display the details. This links to "website/details?id=", so we can find out more about them.
class MainController < Ramaze::Controller
def index
if not @everybody then
@everybody = Person.dataset
end
end
def details
@somebody = Person[request['id']]
end
def changedetails
@somebody = Person[request['id']]
end
def savechanges
# code for transferring the POSTed form back into the appropriate object
end
end
The template for "changedetails" presumably includes some code that is the functional equivalent of
<form method="post" action="savechanges">
<input type='hidden' name='id' value='@somebody.id'>
<input type=text name="name" value='@ somebody.name'>
*********
</form>
Those asterisks? That's where the code for choosing an eyecolor goes. It would look something like this:
<select name="eyecolor">
@eye_color.each do |ec|
<option value="ec.id" if @currentObject.eyecolor == ec then "selected" end >ec.color</option>
end
</select>
And when I write the code for the part of the site that handles dolls, and make some more templates, then I get to code that part *again.*
And when I do puppies? I get to code it *again.*
"Code it again" is, IMHO, the wrong answer.
But wait, there's more. This example is still too simple. Some of the objects I'm working with have another layer of associations in them. So, let's model another layer...
class Mall < Sequel::Model
many_to_many :people
many_to_many :dolls
end
class Family < Sequel::Model
many_to_many :people
many_to_many :puppies
many_to_many :dolls
end
(Yes, this is completely reasonable. The company receives shipments of material from a supplier. Each shipment has multiple expenses (people) associated with it, and each expense belongs to a particular category (eye color).)
class FamilyController < Ramaze::Controller
def index
if not @families then
@ families = Family.dataset
end
# et cetera
end
def details
@family = Family[request['id']]
end
def changedetails
@family = Family[request['id']]
end
end
Now my /family/details template has to look something like
<form method="post" action="savechanges">
<input type='hidden' name='id' value='@family.id'>
<input type=text name="surname" value='@family.surname'>
@family.members.each do |person|
<input type=text name="#{person.id}-name" value='person.name'>
*********
end
@family.puppies.each do |puppy|
<input type=text name="#{puppy.id}-name" value='puppy.name'>
*********
end
@family.dolls.each do |person|
<input type=text name="#{doll.id}-name" value='doll.name'>
*********
end
</form>
So that original five lines of code for selecting an eye color is now appearing eight times in five different templates.
And what happens if I change the model? Maybe it turns out that I need to add prescription information for people eyes, so they have to become ".lefteye" and ".righteye" I now have a lot of templates to change.
To me, this seems ridiculous.
Instead, my code has moved to something much more like
<form method="post" action="savechanges">
@family.to_form
</form>
So a Family object knows how to do do fields for its own columns, and it asks associated models to provide THEIR form-ready fields, which it modifies if necessary to support multiple instances of a model in the same form. Those models, in turn, can call THEIR associated models, and so on.
If I make a change to the database schema and add a field to something, I often don't have to make any changes to my Sequel Models, because Sequel's very smart about building a model from the database schema. Likewise, with the code I have now, I often don't have to make any changes to any OTHER parts of my code, because the forms are constructed programmatically within the models.
--
You received this message because you are subscribed to the Google Groups "Ramaze" group.
To post to this group, send email to ram...@googlegroups.com.
To unsubscribe from this group, send email to ramaze+un...@googlegroups.com.
For more options, visit this group at http://groups.google.com/group/ramaze?hl=en.
Lots of places happily tell me I can just enter "$ramaze start". Yes, of course. From a command line I have opened, and after "cd /my/site/root/directory". But "deployment" means NOT typing stuff at the command line. It has to be launched as a cron job, or by some task-launching daemon, not by me. I have not found any clue how to do "ramaze start-from-that-directory-over-there-not-the-one-we're-in-now."
Which would help with the problem that Ramaze needs to get itself up in the morning and get to work without me having to start it by hand. THAT is the absolutely essential part that I cannot find anybody even mentioning in passing, never mind actually providing an example of some kind to show how it could be done.
The wiki says that 'Mongrel is the recommended way of deploying a Ramaze app.' OK, sure. And that means . . . something so obvious it goes without saying, I guess. {sigh} There's an Apache reverse-proxy configuration. Nice, but I have that part working just fine. I need the part BEFORE that.
Or, maybe my production server is brain-damaged, and one of the things I've already tried *should* have worked. I don't like the fact that "ramaze" isn't in my path for no obvious reason.
$cd /my/laptop/test/directory
$ramaze start
works fine . . . on my laptop. But on my Ubuntu server, I have to provide a full path to Ramaze.
Well, fine, whatever, I can do that. In fact, if I type it in from a command line, everything works just fine . . . until I tear down the ssh pipe to the server. That tears down the Mongrel/Ramaze task as well, and poof! the site is gone. Well, that's not exactly unexpected, after all. That's why I use watchdog or launchd or cron on my XServe for tasks that need to stay up and running. Ubuntu doesn't have launchd or watchdog. It does have cron. Cron is willing, under extremely specific circumstances, to launch ramaze, for about 80 milliseconds. Cron is killing the tasks the instant they finish launching. Or maybe they're just dying the instant they finish launching.
I have no idea.
So if anybody feels up to telling me what (hopefully) incredibly obvious simple thing I'm supposed to know about this, I would be most appreciative.
I experienced almost exactly the same frustration a couple years ago. I
also spent hours trying to figure out how to get my ramaze site working
as a daemon in production, after getting it working on an interactive
command line. I posted some questions but nobody really seemed to
understand what I was asking. I finally figured it out, and I'm sorry I
didn't find the time and place to share what I learned. Here is my
attempt to rectify that.
Part of the problem is that there are a lot of different ways to do it,
depending on what infrastructure you choose. It is much easier if you
use apache, which I don't because I find it to be huge and complex.
On my ubuntu server, I need to host multiple domains, so I chose pound
to handle that part. I won't discuss that here. Some of my sites are
static, so I use nginx for those. For my dynamic (ramaze) site(s) nginx
delegates to thin. If your only site is a ramaze site, you can probably
skip pound and nginx and just go straight to thin.
My thin yml file for my ramaze site looks like this:
---
pid: /tmp/pids/thin-example.com.pid
log: /var/log/thin-example.com.log
max_conns: 1024
timeout: 30
#port: 8100 # port not used--overridden by start.rb
chdir: /var/www/example.com
environment: development
max_persistent_conns: 512
#address: 127.0.0.1 # address not used--overridden by start.rb
#daemonize: true # daemonize probably ignored by thin
require: []
After setting that up, I tested by running thin on the command line, and
saw that it launched ramaze properly. Once that was working, I used
thin's daemon mode by saying something like:
sudo /etc/init.d/thin start
Presto magico, the site will now start in the background (persisting
after you terminate your ssh session), and it will automatically restart
whenever the server is restarted.
I hope that helps, and if so, perhaps you can carry this forward and
submit some patches to get it included in the ramaze documentation.
Kevin
> Dave,
>
> Don't get me wrong but your attitude feels somewhat negative based on the way your Email is written.
Well, I don't *think* I'm 'getting you wrong,' and oh, yes, I'm sure it does. What I really *wanted* to write involved lots and lots of bad language and disparaging comments about Unix's parentage. However, that would have been much less likely to get me any helpful responses at all. {chuckle} I tried to not spew buckets of bile, but I'm not surprised if a bit of it leaked through.
> Ramaze can be deployed using many different forms such as Capistrano, Vlad or Git (my personal favorite). When it comes to webservers it really depends on how you deploy it.
Well, yes, and Li/U-nix offers ever so many alternatives, I'm sure. I was just rather piqued that I was unable to find even a single actual example of somebody doing so . . .
> What I use for deployment is Git (like mentioned before).
Ah. I certainly have friends to adore Git. At the moment, I don't really have (at least as far as I can recognize) any other good uses for Git, so it would involve installing, integrating, and learning what appears to be a fairly large and potentially elaborate new tool. Since it's just one web site, and just one programmer, I'll probably see if I can get something simpler working first.
I wouldn't be surprised, though, if somebody else Googled up this thread at a later date and said "Oh, hey! Yea, Git would do it, and we're already running that!" {smile}
> Hi Dave,
>
> A few options for you:
> 1) (My personal choice) The passenger apache mod: http://www.modrails.com/ - point a vhost at your public/ dir and it'll do the rest. You need to set up persistent caching etc in your start.ru file if this is necessary for your application.
I believe that this would assume that I was actually running Apache on my Ubuntu box, then? In my particular instance, Apache is running on my XServe, which handles most of my web sites, but it is reverse-proxying this particular bit over to the Ubuntu box, which isn't (at least so far).
> 2) GNU screen: http://www.gnu.org/software/screen/ - run your ramaze start command inside a screen session and detach.
Based mostly on the name of this, I'm guessing that wouldn't help with having the Ramaze site auto-launch on a reboot, though?
> 3) Prevent your terminal hangup from killing ramaze: nohup ramaze start &
Hmm! I'd been trying the & with cron to try to make it let go of Ramaze so it could keep running, but I've never heard of 'nohup' before.
> With regard to the ramaze command not being in your $PATH, I suspect this is an ubuntu/debian problem.
Wouldn't surprise me. I *was* a bit surprised that "gem install ramaze" still didn't get Ramaze into my path, but only a bit.
> With regard to the ramaze command not being in your $PATH, I suspect this is an ubuntu/debian problem.
Although I'm not sure yet, it looks like this will be the key clue to fix the problem.
I know there's kind of a religious war about whether gems should be installed with gem or with the Debian package tools (apt-get or aptitude). I've also found that, for all of aptitude's attitude about how you should never try to end-run around the Debian package manager, upon closer inspection, it became clear that it had *completely* botched the installation of gems.
$irb
irb(main):001:0> require 'rubygems'
=> true
irb(main):002:0> require 'ramaze'
LoadError: no such file to load -- ramaze
Gimme a break.
So I downloaded rubygems myself, installed it, and asked it to start installing gems. I was not surprised to find that it was completely unaware of all the gems that had already been installed. Clearly, the Debian-installed RubyGems was being told to install everything where nobody would ever be able to find them. Nice.
I'll follow up this message if/when I've got something else to report.
try the following commands:
1/ gem env
already at this stage you should know if something is wrong with your
system... look out for gem paths
2/ gem list
obvious, once you've the above ok, to check out what is installed or
not (at least, you've a list)
>
> So I downloaded rubygems myself, installed it, and asked it to start installing gems. I was not surprised to find that it was completely unaware of all the gems that had already been installed. Clearly, the Debian-installed RubyGems was being told to install everything where nobody would ever be able to find them. Nice.
>
> I'll follow up this message if/when I've got something else to report.
>
>
side question to the rest of the list: can bundler work with ramaze
like it does with rails ? I googled for this but I could not find
it...
--
Christian
>
> On Feb 8, 2011, at 3:27 , Nick Robinson-Wall wrote:
>
>> With regard to the ramaze command not being in your $PATH, I suspect this is an ubuntu/debian problem.
>
> Although I'm not sure yet, it looks like this will be the key clue to fix the problem.
And it was.
It's funny that nobody ever posted (that I could find) on the web that the simplest way to deploy Ramaze is just giving cron
cd /web/site/directory/here; ramaze start
to execute on system boot. Which is what I tried at the very beginning, but since I was unaware that the gems were completely flabligated from a non-login shell, I had no idea why it didn't work.
I am, actually, using a slightly fancier approach. Instead of "ramaze start" I'm using "ruby start.rb" and added some code to start so that it first checks to see if the site is already up, running, and responding normally, and calls Ramaze if it isn't. Then cron is instructed to re-execute my command every, oh, ten minutes at the moment. That way, if buggy code or some such causes the site to go down, it should restart within ten minutes.
Yea, I know, there are much fancier/better/more sophisticated ways to do this. ("Thin" I believe was suggested earlier in this thread.) And I promise I'll look into them soon. But this way requires nothing new to be installed.
Thanks, all.
did you ever type 'ramaze -h' ?
>
> I am, actually, using a slightly fancier approach. Instead of "ramaze start" I'm using "ruby start.rb" and added some code to start so that it first checks to see if the site is already up, running, and responding normally, and calls Ramaze if it isn't. Then cron is instructed to re-execute my command every, oh, ten minutes at the moment. That way, if buggy code or some such causes the site to go down, it should restart within ten minutes.
>
nothing wrong in using 'ruby start.rb' when you're developing the application.
> Yea, I know, there are much fancier/better/more sophisticated ways to do this. ("Thin" I believe was suggested earlier in this thread.) And I promise I'll look into them soon. But this way requires nothing new to be installed.
wrong. you need to install 'thin' gem and this requires OS specific
extensions (without a toolchain, you will not be able to install it).
you should get a more decent return on time investment in looking at
jruby + warbler + tomcat instead. Though I've not tried this yet with
ramaze, it was working fine with rails apps. No command line involved
once the war file is ready for deployment: you're doing this using
tomcat web interface.
My 2 cents
--
Christian
I'm not 'this new' in the ruby area, so I already know this of course
and using it with rails. But thanks anyway :-)
What I truly meant was (and sorry again if I wasn't clear enough): how
to tweak ramaze internals so that bundler is called when gems are
missing ?
For rails 3, it's working out of the box. For rails 2.3, the following
instructions are needed: http://gembundler.com/rails23.html
I'm looking for similar instructions for ramaze...
--
Christian
Christian
> On Wed, Feb 9, 2011 at 1:24 AM, Dave Howell
> <groups...@grandfenwick.net> wrote:
>
>> Yea, I know, there are much fancier/better/more sophisticated ways to do this. ("Thin" I believe was suggested earlier in this thread.) And I promise I'll look into them soon. But this way requires nothing new to be installed.
>
> wrong. you need to install 'thin' gem and this requires OS specific
> extensions (without a toolchain, you will not be able to install it).
I wasn't clear. Using *cron* requires nothing new be installed. "Thin" of course would, which is why I didn't try using it right away.
> you should get a more decent return on time investment in looking at
> jruby + warbler + tomcat instead.
Hmm. OK, recommended choices include "thin" with my existing ruby, or those three. Excellent!
I'll have to look into those. Something's not copacetic with what I have. My script is leaving me a log trail; every ten minutes Cron fires it off, and it does a test of the site by using "curl" to retrieve a special diagnostic test page. Every 1-2 hours, the test fails, and my start script has to retrieve the PID, kill it, and restart Ramaze. I don't know how to get Ramaze to log errors, or maybe I do, but whatever's causing the fault isn't creating a log trail. Since the failures are occurring even at three in the morning, I don't think it's something that users are doing to the site; it's just . . . stopping.
Suggestions for finding useful diagnostic clues?
http://god.rubyforge.org/
http://mmonit.com/monit/
They can launch, monitor, ping, restart, watch memory consumption,
email you statuses and all that good stuff. Well worth the investment
to learn one or the other.
Michael
> --
> You received this message because you are subscribed to the Google Groups "Ramaze" group.
> To post to this group, send email to ram...@googlegroups.com.
> To unsubscribe from this group, send email to ramaze+un...@googlegroups.com.
> For more options, visit this group at http://groups.google.com/group/ramaze?hl=en.
>
>
I had a Ramaze app in production for about a year or so, using jruby +
warbler.
I had some trouble getting everything "just so", but once running it was
amazingly solid.
My issues were primarily related to Glassfish confguration (like Tomcat,
but lighter), and Warbler being written as if all Rack apps were Rails
apps.
However, nowadays I run Ramaze apps in one of two ways. The prefered
way is to use it with Apache + Passenger Phusion. "It Just Works (tm)!"
(Note: I'd look at nginx if I had more free time; Apache works OK for
me, but people speak highly of nginx, especially if you are not needing
any Apache-specific modules or features.)
Or, I ssh to the server, start a screen session, and run the app from
the commandline using WEBrick or Mongrel, and ctrl-D out of screen. I
do this for smallis stuff when I want a simple service for some perioid
of time. I don't think I'd want to run a site like that full-time.
OTOH, that's exactly how ruby-doc.org was running for quite a few years.
:) (However, it required regular checking and bouncing.)
If you go the phusion way, then Capistrano can help if you don't want to
be manually ssh'ing to the server or any of that stuff.
James
--
jamesbritt.com - Playing with Better Toys
neurogami.com - Smart application development
azhackers.com - Feed your head. Hack your world.