Database applications and OOness

forgive me for jumping in late in this thread…
I have recently been working on a persistence layer for our product. After
spending some time looking through the projects available on RAA I was not
entirely satisfied with any of them - so we rolled our own that meets our
current needs

I can do things like:

allPeopleAsAnArrayOfPersonObjects = store.get(Person) # select * from persons
peopleOlderThan30 = store.get(Person, :age, :>, 30) # select * from persons
where age > 30
peopleBetween30And60 = store.get(Person, :age, :>, 30, :age, :<, 60) # select

  • from persons where age > 30 and age < 30
    or - store.get(Person, :age, :between?, [30, 60])
    peopleWithNameStartingWithFre = store.get(Person, :name, :=~, “Fre”) # using
    the =~ operator is misleading here - it currently does “LIKE ‘#{arg}%’” :slight_smile:
    …yada yada…

The store object takes care of creating an SQL query. While the interface is
still pretty ugly, it sure beats embedded sql code everywhere, and is less
verbose.

The code only does the bare minimum to satisfy my needs at the moment and is
being actively developed, but there is capability for:

  • relationships between tables (although i do make assumptions about how
    tables are related). For example, if a Widget contains a single Part, then
    we assume a table of widgets and a table of parts - where the widget table
    has a “part-id” field When saving, we save the part, then the widget. If a
    widget contains an array of parts then the relationship is reversed - the
    part table has a “widget-id” field, etc etc.
  • returning data as arrays of hashes and/or as objects.
  • arbitrary query execution using get(field, operator, value(s)) - although
    this is fairly limited at the moment.

I have avoided the issue of joining tables when running queries - we simply
create a view which joins the tables as required and then run a query on
that.

While what we have done is certainly not robust enough - nor generic enough
for general purpose use, I’d be happy to post some (barely) working code if
anybody is interested.

Cheers,
Martin

···

On Thursday 08 January 2004 06:00, Tim Bates wrote:

What would you like it to return? Should it construct a class on the fly,
with fields ‘name’ and ‘customers’? And should it use its own query
language to do so, or parse the sql query?

I don’t know. Probably an array of arrays or an array of hashes - which,
I know, is exactly what DBI would return for such a query. The point I’m
trying to make is I’d like the system to be able to handle this sort of
query as_well_as the “SELECT * FROM table WHERE property = ?” type
return-a-list-of-objects query. I don’t know of any system that can do
that, or even if it could be done neatly. Possibly such a system would
have to accept two sorts of query and handle both separately, but that
introduces its own ugliness.


Martin Hart
Arnclan Limited
53 Union Street
Dunstable, Beds
LU6 1EX
http://www.arnclanit.com

I write a lot of code that provides some sort of web based view or
application to data stored in a database, and I find myself following a
repeated model where I define a Struct that represents a row of data.
This is very convenient for my manipulation fo the data, but there are
complications that arise.

The first is that if I change the database schema, I also have to then go
change the struct code to match, and the second is that I write a lot of
the exact same sort of SQL to query data with the same Ruby around it to
put that data into my struct, and when I change the database schema, I
then need to go change all of the SQL that is affected.

What I need is a layer that, given a db table, will generate a Struct that
encapsulates that data and provides a few convenience methods for querying
data from the table into one or more objects, and for pushing the data
from the objects back to the db.

So far as I have been able to determine, there’s nothing on RAA that does
this. Am I overlooking something, or is this something that I need to
write myself?

Thanks,

Kirk Haines

···

On Thu, 8 Jan 2004, Hal Fulton wrote:

For example, you might wrap a query in a method that returned
a Struct whose member names were the database field names.

As somebody who’s done some of this work (I wrote Lafcadio), I
strongly recommend that you consider extending somebody else’s work
before doing it yourself. When you enter this problem domain there are
a lot of little wrinkles you run into, and the best solution isn’t
always necessarily obvious from the start.

Absolutely, I wouldn’t want to write yet another database interface if I
could find one that suited me, with or without some modification. The
interface is not the point of this project.

Now, I’ve only used Lafcadio, but I have the feeling that right now
the Ruby OR-mapping tools out there are either 1) strong on mapping
rows to objects, or 2) strong on ad-hoc querying, but not both.

This is my experience also, which is why I started this thread.

I’d put Lafcadio and Vapor in category 1, and Criteria in category 2.

Criteria doesn’t do any of category 1 at all, so yes.

Either requirement is pretty big; both are twice as big. In the
long-term, my feeling is that any library that sticks around in this
space will have to satisfy both needs.

Uhuh. But none of them have quite cracked it yet.

In the short-term, I can tell you what you’d have to do for Lafcadio
to get it do what you want. First, Lafcadio was written first for
MySQL, and although it should support Postgres with minimal changes, I
have yet to implement or test such a change.

Vapor, to give an example, is very targeted at Postgres and uses a lot of its
more advanced features (in particular it integrates Postgres’s
transaction model quite closely into its behaviour). Criteria, to give
another example, is running into problems because of subtly different
query syntaxes between different databases (MySQL’s “RLIKE” vs
Postgres’s “~”, for example). Does Lafcadio suffer from either of these
constraints? In particular I’d like to have a fair bit of control over
when and where transactions are used - it’s particularly critical in
cases like

“BEGIN;
UPDATE accounts SET balance = balance - 100 WHERE accnum = 1743;
UPDATE accounts SET balance = balance + 100 WHERE accnum = 329;
COMMIT;”

If I were to do a similar thing in Ruby code, I’d need to be sure that
the account balance that I was using in the calculation was up-to-date
(otherwise the effects of someone else’s transaction might get wiped)
and also that if the second UPDATE in the example above failed for some
reason then the first one would get rolled back. Can Lafcadio do, or be
made to do this?

(At RubyConf Ryan Davis told me he was very interested in helping add
Postgres functionality, and I moved the library to DBI to enable that,
but I suspect that, like everybody else, Ryan’s pretty busy.)

I am also interested in getting it to work with Postgres, if it solves
my problem. I’m pretty busy too, but I have time set aside for this
project that I can’t really start on until I deal with this issue.

Second, Lafcadio’s support for querying works, but I’ll be the first
to admit that it’s verbose and ugly as hell. Query inference is very
high on my list, but then, I’m busy, too. So Vapor or Criteria might
be a better choice for you, depending.

I wouldn’t consider Criteria to be in quite the same basket as Lafcadio
and Vapor. It doesn’t have any OO abstraction at all (on the data side -
the project is all about OO abstraction of queries) I think Lafcadio (or
Vapor) could take a lot of ideas (and/or code) from Criteria for their
own query interfaces. I had independently thought of some of the
concepts behind Criteria when I discovered that somebody had already
written it, and in a much cleverer way than I would have thought of, but
it’s not primarily a database interface library, it’s a query
construction library. A project that combined the best aspects of the OO
data interfaces and the OO query interfaces that we already have would
be exactly what I was looking for.

Perhaps I’ll start looking into how to a) get Lafcadio to work with
Postgres and b) get Lafcadio or Vapor to work with Criteria.

Tim Bates

···

On Thu, Jan 08, 2004 at 04:56:40AM +0900, Francis Hwang wrote:

tim@bates.id.au

# => SELECT salesperson.name, COUNT(DISTINCT
# transactions.customer_id) FROM salesperson LEFT OUTER JOIN
# salesperson ON (salesperson.id = transactions.salesperson) ORDER
# BY customers desc

Granted, it doesn’t generater column headings, and you currently have
to use a literal string hack to get the SQL function. This should
hopefully be fixed before long.

Well this doesn’t actually work, because the ORDER BY clause references
a column name (“customers”) that doesn’t exist, and also in
Criteria-1.1a at least when I tried this it failed with
NoMethodError: undefined method join' for :customers:Symbol from /home/tim/ruby/ln/criteria/sql.rb:243:in _mkorder’
from /home/tim/ruby/ln/criteria/sql.rb:287:in `select’

I realise that these are minor things. If I can help fix them, I will -
tell me what needs to be done. I am keen to get this working.

I’m not sure what Vapor is, but—and I know you’ve mentioned it so
you’re aware of it—you may take a look at Mephle, since I think it
does what you want:

I did look at Mephle, but I seem to remember you telling me not that
long ago that it wasn’t what I wanted. :stuck_out_tongue:

There are a few reason I still serialize and index instead of using
tables exclusively:

1.  Some objects are large, and you don't want to index everything
2.  Looking up attributes every time is slow
3.  Not all ruby things can be represented as SQL things

(4. SQL isn’t guaranteed to be the storage mechanism, either)

I’m open to suggestion though. It’s conceivable that there’s a
“CachedTableObject” class or something where you give up the ability
to write into a live system, and you avoid point 2, for objects that
work. Of course, you still need to be able to load that object later,
and using a unique OID for every object in the system is nice, so
you’d still have something in the object table. I’m not sure what you
gain by this one other than a few extra bytes, but it could be done.

I’m not sure I quite follow you here. The approach that Vapor (and I
think Lafcadio) takes is to look up an attribute only when the object is
loaded or explicitly refreshed, and just keep the value in memory, so if
the same accessor method is called multiple times in succession only one
database query is made. I think this is what you mean by “giving up the
ability to write into a live system”.

Also, I’m not entirely clear on what you mean by indexing in this
context. I am very likely to set up the database tables and SQL indexes
manually, so those columns that will contain a lot of data will
naturally not be indexed. I’m not sure if you’re referring to SQL
indexing though, I haven’t looked that closely at Mephle.

Points 3 and 4 are rather moot. Point 3 because in my case I will be careful to
only put into the database that which can be represented in SQL. I don’t
really want a library that hides all the SQL from me, I know what I’m
doing in that regard. Point 4 because my project will only ever store
stuff in SQL, although I realise that this is not a reason for you to
abandon that approach as I’m not the only one who may use your library.

Tim Bates

···

On Thu, Jan 08, 2004 at 05:55:23AM +0900, Ryan Pavlik wrote:

tim@bates.id.au

This article has a point that I’ve seen before, namely that object
models don’t necessarily have a 1-to-1 mapping to relational models:
“[Object-relational mapping] adds a huge amount of complexity to a
project, and with dubious benefits: when you’re not tracking down
bugs in the mapping framework or obsessing about performance, you’re
chopping toes off your object model so you can shoehorn it into a
relational schema.”

The OO relational-query model he describes looks very much like
Criteria, and I think if I were to go along that route I would
definitely use Criteria and DBI. But some inner Ruby demon is constantly
telling me that I’d really like to represent my data as first-class
objects when_possible, which Criteria doesn’t even try to do.

Essentially, now, I think I want Lafcadio on Postgres with Criteria (or
Criteria-like) queries and transaction support. I like the way (and
correct me if I’m wrong about this) Lafcadio uses the RDBMS the way it
was meant to be used - unlike Vapor (which adds extra tables and columns
for metadata and generally wrests control of the RDBMS out of the hands
of the user) or Mephle (which basically only uses the RDBMS as somewhere
to write data to). I don’t like Lafcadio’s query model (which I
haven’t tried to use, but the author himself assures me is “ugly as
hell”) and I do really like Criteria’s query model, so the best of both
worlds there would just about be enough to satisfy me.

I’m pretty fixed on using an SQL back-end, because it outsources much of
the calculation and concurrency issues to another program which is built
for it, but just so I can say I have considered all the options can
someone tell me if there’s a non-SQL OO solution out there which, most
importantly, doesn’t store all its data in memory all the time - some of
my data needs to be kept for a long time but will very seldom be
referenced, and I don’t want to have to manually write it to disk. I’ve
seen things like Madeline and other Prevayler-type things, and they seem
to want to keep everything in memory and only write snapshots and
logging to disk. A neat query model and some in-built concurrency
support (even if it’s just read/write locking) would be added bonuses.

Tim Bates

···

On Thu, Jan 08, 2004 at 05:03:41AM +0900, David Naseby wrote:

I liek the look of ROE
(http://www.cincomsmalltalk.com/userblogs/avi/blogView?showComments=true&ent
ry=3246121322) on Squeak. Don’t know if its what you’re looking for of
course, since even you don’t know what you’re looking for :slight_smile:


tim@bates.id.au

Hi,

I’m a Perl -> Python -> Ruby convert and new to this mailing list.

I like Ruby very much except for one thing: all those “end” tokens scattered
everywhere. For my style of coding, they are superfluous and, in my opinion,
only serve to clutter the code. I very much like Python’s indent blocking
style and would like to use it with Ruby. Indenting does everything I need it
to do in terms of establishing structure and enhancing readability. Once you
get used to this style (I’ve been using it for just about all languages and
for longer than Python has existed), delimiter tokens become unnecessary and
unwanted.

I’m sure the topic of Python-style blocking in Ruby has been brought up
before. Is there a general consensus on this in the community? Before I
totter off to write my own Ruby preprocessor, does such a thing already
exist? Thanks.

···


Pete Yadlowsky
ITC Unix Systems Support
University of Virginia

I write a lot of code that provides some sort of web based view or
application to data stored in a database, and I find myself following
a repeated model where I define a Struct that represents a row of
data. This is very convenient for my manipulation fo the data, but
there are complications that arise.

The first is that if I change the database schema, I also have to then
go change the struct code to match, and the second is that I write a
lot of the exact same sort of SQL to query data with the same Ruby
around it to put that data into my struct, and when I change the
database schema, I then need to go change all of the SQL that is
affected.

What I need is a layer that, given a db table, will generate a Struct
that encapsulates that data and provides a few convenience methods for
querying data from the table into one or more objects, and for pushing
the data from the objects back to the db.

So far as I have been able to determine, there’s nothing on RAA that
does this. Am I overlooking something, or is this something that I
need to write myself?

I have a system that was developed in house. It is not exactly what
you are asking for. I call it SPOM (Simple Persistent Object
Manager). The keyword is SIMPLE.

It basically has 2 layers. SpomDB is a wrapper over a DBI
connection. It can take an arbitrary sql query and will return an
array of newly created Objects. The objects will have members that
are the same names as the query names (unless they are invalid ruby
names) but they can also be accessed via , like an array would.
This makes it convenient to do an arbitrary query. But it currently
does support modifying and resaving this type of object (would not be
hard to add, but I haven’t needed that).

Above this is SPOM, a manager of sorts. It takes a spomDB and you
can add xml file mappings to it. The xml file mappings allow you to
map between the db and any given object. You can add data type
mappings (ie it is stored as a char in the db but in ruby it is a
boolean). You can also have sub-objects mapped. So you can have a
person object, that has an address object that has a zip code object.
You can then access the ruby object via person.address.zip.code (ie
it creates the sub objects and fills them for you). Please keep in
mind this is a simple one object to one table mapping not
relationships between tables (it is simple).

It is used in house to read in and transfer data between SQL Server,
Informix, Dataflex, and DBF databases. Its primary intent was to
extract data from legacy systems. Using the mapping files, you can
also do basic CRUD operations, but there currently is NO transaction
support, no connection pooling, no object query interface (ie you use
sql), no multi-table mappings or join support. I do plan to add
these things, but it will take time, right not SPOM supports our
basic in-house needs.

If there is an interest in this I could release it next tuesday to
RubyForge. (I would simply need to present this to our executive
group meeting on tuesday, more of a formality, but that’s our
protocol for this) Also note that there is very little documention,
I’ll try to add some if there is interest.

Thanks,

Kirk Haines

Walt

···

Walter Szewelanczyk
IS Director
M.W. Sewall & CO. email : walter@mwsewall.com
259 Front St. Phone : (207) 442-7994 x 128
Bath, ME 04530 Fax : (207) 443-6284


# => SELECT salesperson.name, COUNT(DISTINCT
# transactions.customer_id) FROM salesperson LEFT OUTER JOIN
# salesperson ON (salesperson.id = transactions.salesperson)
# ORDER BY customers desc
> Well this doesn't actually work, because the ORDER BY clause references > a column name ("customers") that doesn't exist, and also in > Criteria-1.1a at least when I tried this it failed with > NoMethodError: undefined method `join' for :customers:Symbol > from /home/tim/ruby/ln/criteria/sql.rb:243:in `_mkorder' > from /home/tim/ruby/ln/criteria/sql.rb:287:in `select'

Hmm. I really need to release 1.2. You can always grab the latest
from svn:

http://svn.mephle.org/svn/mephle/criteria

IIRC, 1.1a had a number of things in need of fixing.

I realise that these are minor things. If I can help fix them, I will -
tell me what needs to be done. I am keen to get this working.

Oh, yeah, you need “COUNT(…) AS customers”. Hopefully this will be
fixed by 1.2 as well.

I’m not sure what Vapor is, but—and I know you’ve mentioned it so
you’re aware of it—you may take a look at Mephle, since I think it
does what you want:

I did look at Mephle, but I seem to remember you telling me not that
long ago that it wasn’t what I wanted. :stuck_out_tongue:

Oh? :wink: Hrm, I probably misunderstood what you were after.

There are a few reason I still serialize and index instead of using
tables exclusively:

1.  Some objects are large, and you don't want to index
    everything
2.  Looking up attributes every time is slow
3.  Not all ruby things can be represented as SQL things

(4. SQL isn’t guaranteed to be the storage mechanism, either)

I’m open to suggestion though. It’s conceivable that there’s a
“CachedTableObject” class or something where you give up the
ability to write into a live system, and you avoid point 2, for
objects that work. Of course, you still need to be able to load
that object later, and using a unique OID for every object in the
system is nice, so you’d still have something in the object table.
I’m not sure what you gain by this one other than a few extra
bytes, but it could be done.

I’m not sure I quite follow you here. The approach that Vapor (and I
think Lafcadio) takes is to look up an attribute only when the
object is loaded or explicitly refreshed, and just keep the value in
memory, so if the same accessor method is called multiple times in
succession only one database query is made. I think this is what you
mean by “giving up the ability to write into a live system”.

Right. You couldn’t have someone else talking to the database at the
same time, at least to write. If you can’t do that, I don’t see what
advantage it provides to store objects this way (instead of just
indexing them).

Also, I’m not entirely clear on what you mean by indexing in this
context. I am very likely to set up the database tables and SQL
indexes manually, so those columns that will contain a lot of data
will naturally not be indexed. I’m not sure if you’re referring to
SQL indexing though, I haven’t looked that closely at Mephle.

Yes, this is pretty much what I do with Mephle. Objects are stored by
serializing (via Marshal) and writing that to an Object table.
Anything I need for quick lookup, I stick into a table using Criteria.

Points 3 and 4 are rather moot. Point 3 because in my case I will be
careful to only put into the database that which can be represented
in SQL. I don’t really want a library that hides all the SQL from
me, I know what I’m doing in that regard. Point 4 because my project
will only ever store stuff in SQL, although I realise that this is
not a reason for you to abandon that approach as I’m not the only
one who may use your library.

Well, it hides the object storage bits, since they’re the same for
everything. Indexing and SQL stuff is abstracted via Criteria of
course, but that’s still “right there”. Viability depends on how you
want things to work, but you might check it out.

···

On Thu, 8 Jan 2004 09:11:16 +0900 Tim Bates tim@bates.id.au wrote:

On Thu, Jan 08, 2004 at 05:55:23AM +0900, Ryan Pavlik wrote:


Ryan Pavlik rpav@mephle.com

“Spinal hazards are hazardous…” - 8BT

Tim Bates tim@bates.id.au wrote in message news:20040107232653.GA32403@bates.id.au

In the short-term, I can tell you what you’d have to do for Lafcadio
to get it do what you want. First, Lafcadio was written first for
MySQL, and although it should support Postgres with minimal changes, I
have yet to implement or test such a change.

Vapor, to give an example, is very targeted at Postgres and uses a lot of its
more advanced features (in particular it integrates Postgres’s
transaction model quite closely into its behaviour). Criteria, to give
another example, is running into problems because of subtly different
query syntaxes between different databases (MySQL’s “RLIKE” vs
Postgres’s “~”, for example). Does Lafcadio suffer from either of these
constraints? In particular I’d like to have a fair bit of control over
when and where transactions are used - it’s particularly critical in
cases like

“BEGIN;
UPDATE accounts SET balance = balance - 100 WHERE accnum = 1743;
UPDATE accounts SET balance = balance + 100 WHERE accnum = 329;
COMMIT;”

If I were to do a similar thing in Ruby code, I’d need to be sure that
the account balance that I was using in the calculation was up-to-date
(otherwise the effects of someone else’s transaction might get wiped)
and also that if the second UPDATE in the example above failed for some
reason then the first one would get rolled back. Can Lafcadio do, or be
made to do this?

  1. Regarding subtle differences in query syntax: If I had a
    comprehensive list of things to look out for I could write tests
    against them. I imagine most of them would be easy to fix.

  2. Regarding transactions: I haven’t done this, but I don’t imagine it
    would be difficult. A Ruby-like style for it might be something like:

objectStore.runTransaction {
account1743.balance -= 100
account1743.commit
account329.balance += 100
account329.commit
}

… and then I’d just write an ObjectStore#runTransaction method like:

def runTransaction
beginTransaction
yield
commitTransaction
end

right? One question: When using DBI/Postgres, do you issue begin and
commit commands as separate lines, or do they need to be mixed in with
your SQL strings?

Second, Lafcadio’s support for querying works, but I’ll be the first
to admit that it’s verbose and ugly as hell. Query inference is very
high on my list, but then, I’m busy, too. So Vapor or Criteria might
be a better choice for you, depending.

I wouldn’t consider Criteria to be in quite the same basket as Lafcadio
and Vapor. It doesn’t have any OO abstraction at all (on the data side -
the project is all about OO abstraction of queries) I think Lafcadio (or
Vapor) could take a lot of ideas (and/or code) from Criteria for their
own query interfaces. I had independently thought of some of the
concepts behind Criteria when I discovered that somebody had already
written it, and in a much cleverer way than I would have thought of, but
it’s not primarily a database interface library, it’s a query
construction library. A project that combined the best aspects of the OO
data interfaces and the OO query interfaces that we already have would
be exactly what I was looking for.

There’s an extremely alpha version of query inference in the last dev
release, 0.3.4. I wouldn’t suggest trying to use it, though, 'cause
the interface is likely to change. I tried doing something similar to
Ryan did with Criteria, but I found that as soon I started seeing
pseudo-Ruby syntactical symbols like &, |, <, >, etc., I instinctively
wanted the entire range of Ruby primitives to play with. Hence my
wierd question here earlier about whether it’s possible to override
unary negation.

The more I think about it, the more this sort of interface makes me
nervous. Maybe Criteria users can pipe in here with their perspective,
but it seems to me that once you tell the programmer that she can
write code like

(tbl.bday == “2003/10/04”).update(:age => tbl.age + 1)

then it’s natural for her to assume she can write the opposite:

(tbl.bday != “2003/10/04”).update(:age => tbl.age + 1)

… but the second case will fail, and probably it will fail quietly
since (I think) there’s no way for a Table object to detect negation
like that. I’m starting to think I’d want a query syntax that trades
some cleverness for clarity, something like maybe

(tbl.bday.equals( “2003/10/04” ) ).update(:age => tbl.age + 1)
(tbl.bday.equals( “2003/10/04” ).not! ).update(:age => tbl.age + 1)

Or maybe that’s trading one sort of ugliness for another. I’m not sure
yet.

One more point of disclosure in the interest of not wasting your time,
Tim: Lafcadio does almost no handling of group functions yet. I
suspect it wouldn’t be too difficult to add on, but I haven’t thought
about the problem much yet so I can’t really be certain of it.

Francis

As a simple workaround, I wrote this little code to hide ends in a row
and collapse their lines in Emacs. Useful for reading but not for
writing:

– fxn

(defvar ruby-ends-hide-regexp-overlays nil “”)
(make-variable-buffer-local 'ruby-ends-hide-regexp-overlays)

(defun hide-ruby-ends ()
“Hide Ruby end keywords.”
(interactive)
(let ((regexp “^\(?:[ \t]*\(?:end\|}\)\n\)+”))
(save-excursion
(goto-char (point-min))
(while (and (< (point) (point-max))
(re-search-forward regexp nil t))
(let ((overlay (make-overlay (match-beginning 0) (match-end
0))))
(overlay-put overlay 'invisible 't)
(overlay-put overlay 'intangible 't)
(overlay-put overlay 'before-string “”)
(overlay-put overlay 'after-string “”)
(setq ruby-ends-hide-regexp-overlays
(cons overlay ruby-ends-hide-regexp-overlays)))))))

(defun unhide-ruby-ends ()
“Unhide Ruby end keywords.”
(interactive)
;; remove all overlays in the buffer
(while (not (null ruby-ends-hide-regexp-overlays))
(delete-overlay (car ruby-ends-hide-regexp-overlays))
(setq ruby-ends-hide-regexp-overlays (cdr
ruby-ends-hide-regexp-overlays))))

(defvar ruby-ends-are-hidden nil “”)
(make-variable-buffer-local 'ruby-ends-are-hidden)

(defun toggle-ruby-ends ()
“Hide/unhide Ruby end keywords.”
(interactive)
(cond (ruby-ends-are-hidden (unhide-ruby-ends)
(setq ruby-ends-are-hidden nil))
(t (hide-ruby-ends)
(setq ruby-ends-are-hidden t))))

···

On Jan 8, 2004, at 8:33 PM, Pete Yadlowsky wrote:

I like Ruby very much except for one thing: all those “end” tokens
scattered
everywhere. For my style of coding, they are superfluous and, in my
opinion,
only serve to clutter the code. I very much like Python’s indent
blocking
style and would like to use it with Ruby. Indenting does everything I
need it
to do in terms of establishing structure and enhancing readability.
Once you
get used to this style (I’ve been using it for just about all
languages and
for longer than Python has existed), delimiter tokens become
unnecessary and
unwanted.

I’m sure the topic of Python-style blocking in Ruby has been brought up
before. Is there a general consensus on this in the community? Before I
totter off to write my own Ruby preprocessor, does such a thing already
exist? Thanks.

I chose not to learn Python because of the crap called syntactically
significant whitespace. Python’s blocking solves the problem of readable
code the wrong way.

(In other words, I’m quite glad that Ruby hasn’t succumbed to that
particular insanity.)

-austin

···

On Fri, 9 Jan 2004 04:33:15 +0900, Pete y wrote:

I’m sure the topic of Python-style blocking in Ruby has been brought up
before. Is there a general consensus on this in the community? Before I
totter off to write my own Ruby preprocessor, does such a thing already
exist? Thanks.


austin ziegler * austin@halostatue.ca * Toronto, ON, Canada
software designer * pragmatic programmer * 2004.01.08
* 15.43.45

Pete Yadlowsky wrote:

I’m a Perl → Python → Ruby convert and new to this mailing list.

Myself also.

ActuallY I am a C → PERL → C++ → Scheme → LISP → Python → Ruby
convert.

I like Ruby very much except for one thing: all those “end” tokens scattered
everywhere. For my style of coding, they are superfluous and, in my opinion,
only serve to clutter the code.

i know what you mean any useless syntax that provides extra visual
clues is a total waste of typing interpreters dont need them and
neither should humans

Almost content free… Nothing to see here, move along people.

-dave

Pete Yadlowsky pmy@virginia.edu wrote in message news:200401081433.10236.pmy@virginia.edu

Hi,

I’m a Perl → Python → Ruby convert and new to this mailing list.

I like Ruby very much except for one thing: all those “end” tokens scattered
everywhere. […]

I’m sure the topic of Python-style blocking in Ruby has been brought up
before. Is there a general consensus on this in the community? Before I
totter off to write my own Ruby preprocessor, does such a thing already
exist? Thanks.

I also followed the Perl → Python → Ruby route, and
I’m afraid I have to tell you that the Ruby way is the
right way. Python’s syntactically-significant
indentation is partly responsible for the reputation
Python has of being “executable pseudocode” which has
lured so many unwary programmers into its coils; but
much I as liked it in my Python days, I know see that
it was a mistake.

Consider what syntactically-significant indentation
means in the context of a vigorous hacking session in
emacs or vim. Hack, hack, hack. Gotta refactor:
cut this here, paste it there…oops! Now the indentation
is all wrong. Pretty soon you find that you are spending
time and mental energy horsing around with indentation
(especially if some of your code contains tabs). This
is not programmer heaven.

Furthermore, syntactically-significant indentation
increases the risk of errors, for several reasons.
Explicit block-termination gives the editing session
valuable redundancy. If you screw up your cutting and
pasting during a Ruby refactorfest, the editor will
make this obvious when you tell it to re-indent. And
if you don’t notice this (maybe you are programming
drunk), you’ll notice it when the program fails to run.
But in an equivalent Python refactorfest, the editor
will not catch the problem by re-indenting, and the
syntax is more likely to be legal even after your blunder.
The program might run, but run incorrectly (this is bad).

I should have been tipped off by the fact that when I
had a Python block which was more than a screenful, I
would end it with an end comment to make the end-of-block
visible, e.g.:

 if foo:
   # 2 screenfuls of code
 # end if

With a little more Ruby experience, I think that you
too will see that the Ruby way is the right way, and
Python will seem like a bad dream.

Happy Rubying! Regards, Bret

Python’s syntactically significant white space is a bore, I don’t mind
the “end” delimeter at all, although a different word may have been
nice, say… PERL ?

Pete Yadlowsky pmy@virginia.edu wrote in message news:200401081433.10236.pmy@virginia.edu

Hi,

I’m a Perl → Python → Ruby convert and new to this mailing list.

I like Ruby very much except for one thing: all those “end” tokens scattered
everywhere. For my style of coding, they are superfluous and, in my opinion,
only serve to clutter the code. I very much like Python’s indent blocking
style and would like to use it with Ruby. Indenting does everything I need it
to do in terms of establishing structure and enhancing readability.

Noooooo! If you really believe that’s a language feature, you really
should stick with python.

My experience is similar in scripting languages:
Basic->Perl->TCL->Perl->Python->Ruby with C/C++/assembler mixed in
along the way.

Over the past 2 years, at a previous employer (one of python’s oldest
companies using the language in the world, which is quoted in the back
of Programming Python), I dealt with LOTS of python code previously
written by other people.
I came to the conclusion that whitespace as a delimiter is a neat
idea, but it truly works only when you wrote that code yourself ( I
also came to the conclusion that python was an extremely powerful
language but that being a legible language down the line is also a
doubtful statement, if you ask me).

The truth is that when picking other people’s code, whitespace as a
delimiter was a complete and absolute evil.

Albeit I was afraid that whitespace as a delimiter would create all
sort of problems when someone tabulated something wrong, the truth is
that I can only recall one occasion in over 2 years when this happened
and lead to a somewhat hard to track bug. I was surprised about it
working so well.
But there were other issues…

Whitespace as a delimiter did act as a way for a lot of people to
avoid learning the language, thou. Why? Because it FORCES people to
learn a good text editor a la emacs or vi. Surely, not a bad idea,
but nobody wants to be learning both things at the same time. You
would not believe how many newbies use things such as notepad, jot or
something silly like that for editing code. Those people plain and
simply could never learn a white space aligned language.

The real issues I saw that turned me off against python’s syntax
overall was when dealing with code longer than a page. Sure, in a
perfect world, refactoring would be a way of life and not a single
routine would ever take over 80 lines. In reality, it does not happen
(particularly, when the code is very dynamic and is being updated by
multiple hands). I also found it hilarious when I saw code (sometimes
written by people with more python experience than myself) with
comments of the form “# end loop1” to indicate where each block ended.

The second issue that turned me off against that syntax was when
dealing with code generated on the fly (for later eval). As an
exercise, when I started learning python, I tried doing something
similar to what I did now with ruby. I picked Damian Conway’s
Getopt::Declare and tried porting it from perl. I literally pulled my
hair out and after two weeks I abandoned the project. Part of this
was the ugly regex of python, but another issue was having to
prototype code on the fly that ALSO had to follow tabulation syntax in
order to eval properly. It was a nightmare experience.
With that, I became convinced that beautifying the code should never
be a language syntax feature.

A third issue that is silly but could never get over was editing
python and how much typing was needed because of ws. An editor
dealing with python can do two things. It can try to be smart and
tabulate automatically for you (so you type if XXX: and then when hit
enter, you are inside the if block). This works badly for me as many
times I write conditions as stubs which I later fill in so the
automatic tab was more of a headache than a benefit. The othre
alternative is that the text editor does not tabulate automatically,
in which case you end typing more in the end (sure, just tabs/ws but
still more characters).

Personally, I’d like to see ruby use {} for normal blocks a la perl or
C, too, but I will not ask for that it. It would indeed create more
headaches for the parser and even for the user for still keeping the
distinction between blocks, too. So… I’d rather set a hotkey like
F1 to enter “end” for me with a macro and do it all as a single key
and less typing.

For all the reasons above (plus regex, plus python’s not that great
OO, plus its lack of speed, etc), I started looking for a better
language.
Only after a week of using ruby (with its relatively poor docs in
comparison to perl or python), I already did something I could not do
as a newbie to either language.
I have already fallen head over heels for ruby.

As some previous poster said succintly before…
ruby with whitespace as a block delimiter will also be a ruby without
me.

I like Ruby very much except for one thing: all those “end” tokens scattered
everywhere. For my style of coding, they are superfluous and, in my opinion,
only serve to clutter the code.

They are not superfluous. You need a tangible block delimiter in order
to be able to chain methods that take blocks. Take, for example, the
following Ruby code:

foo = %w(f e d c b a)
foo.map do |x|
x = rand(10).to_s + x
end.sort

=> [“2b”, “2c”, “4f”, “5a”, “5e”, “6d”]

In Python, there’s no delimiter to signify the end of the block, so you
can’t chain the output of methods that take blocks onto other methods.
In the above case, chaining the map to the sort can’t be done.

I very much like Python’s indent blocking style and would like to use
it with Ruby. Indenting does everything I need it to do in terms of
establishing structure and enhancing readability.

But not in terms of language functionality.

Ian

···

On Fri 09 Jan 2004 at 04:33:15 +0900, Pete Yadlowsky wrote:

Ian Macdonald | “Absolutely nothing should be concluded
System Administrator | from these figures except that no
ian@caliban.org | conclusion can be drawn from them.” (By
http://www.caliban.org | Joseph L. Brothers, Linux/PowerPC Project)
>

I chose not to learn Python because of the crap called syntactically
significant whitespace.

I avoided Python also, for the same reason. Then one day I decided to dive in
and learn it anyway in spite of myself. And I found I rather liked it. The
taboo of significant whitespace turned out to be a big non-issue very early
in my study. I’d been coding that way for years anyway, just with superfluous
block delimiters of various sorts. Python made them unnecessary. It’s my hope
that Ruby might keep them that way. Then it will be perfect.

Python’s blocking solves the problem of readable
code the wrong way.

What’s the right way?

···

On Thursday 08 January 2004 03:46 pm, Austin Ziegler wrote:


Pete Yadlowsky
ITC Unix Systems Support
University of Virginia

  1. Regarding subtle differences in query syntax: If I had a
    comprehensive list of things to look out for I could write tests
    against them. I imagine most of them would be easy to fix.

Okay. I don’t have a comprehensive list, I’d imagine most of them would
be found through testing. The ones I know of off the top of my head are:

MySQL’s “RLIKE” and “NOT RLIKE” correspond to PostgreSQL’s ~ and !~
operators.

PostgreSQL doesn’t like “OFFSET x,y” syntax, it insists upon
“OFFSET x LIMIT y”.

I don’t know what the SQL standard says in either of these cases.

  1. Regarding transactions: I haven’t done this, but I don’t imagine it
    would be difficult. A Ruby-like style for it might be something like:

objectStore.runTransaction {
account1743.balance -= 100
account1743.commit
account329.balance += 100
account329.commit
}

That’s exactly how DBI and Vapor deal with it. That would be ideal.

… and then I’d just write an ObjectStore#runTransaction method like:

def runTransaction
beginTransaction
yield
commitTransaction
end

def runTransaction
@dbhandle.transaction do |dbh|
yield
end
end

Assuming that your DBI::DatabaseHandle is called @dbhandle.

right? One question: When using DBI/Postgres, do you issue begin and
commit commands as separate lines, or do they need to be mixed in with
your SQL strings?

You don’t need to worry about that, DBI handles it for you. :wink: But just
FYI, there’s a “BEGIN;” SQL statement, a “COMMIT;” SQL statement and a
“ROLLBACK;” SQL statement.

The more I think about it, the more this sort of interface makes me
nervous. Maybe Criteria users can pipe in here with their perspective,
but it seems to me that once you tell the programmer that she can
write code like

(tbl.bday == “2003/10/04”).update(:age => tbl.age + 1)

then it’s natural for her to assume she can write the opposite:

(tbl.bday != “2003/10/04”).update(:age => tbl.age + 1)

… but the second case will fail, and probably it will fail quietly
since (I think) there’s no way for a Table object to detect negation
like that.

It does fail, and it doesn’t do it quietly either; watch this:

irb(main):001:0> require ‘criteria/sql’
=> true
irb(main):002:0> include Criteria
=> Object
irb(main):003:0> tbl = SQLTable.new(‘people’)
=> #<Criteria::SQLTable:0x400ea780 @criterion_type=Criteria::SQLCriterion,
@_typemap=nil, @table=“people”>
irb(main):004:0> (tbl.bday == “2003/10/04”)
=> (== :bday “2003/10/04”)
irb(main):005:0> (tbl.bday == “2003/10/04”).update(:age => tbl.age + 1)
=> “UPDATE people SET age = (people.age + 1) WHERE (people.bday = ‘2003/10/04’)”
irb(main):006:0> (tbl.bday != “2003/10/04”)
=> false
irb(main):007:0> (tbl.bday != “2003/10/04”).update(:age => tbl.age + 1)
NoMethodError: undefined method `update’ for false:FalseClass
from (irb):7

So basically it just gives up. :slight_smile: This is because Ruby translates it to
!(tbl.bday == “2003/10/04”)
and (tbl.bday == “2003/10/04”) is true, therefore the expression
evaluates to false. There is really no way around this other than
altering the query syntax, as you suggest next.

I’m starting to think I’d want a query syntax that trades
some cleverness for clarity, something like maybe

(tbl.bday.equals( “2003/10/04” ) ).update(:age => tbl.age + 1)
(tbl.bday.equals( “2003/10/04” ).not! ).update(:age => tbl.age + 1)

Or maybe that’s trading one sort of ugliness for another. I’m not sure
yet.

It’d need some more thought. Once you start doing this sort of thing, I
start wondering why I shouldn’t just write raw SQL, because this is
looking a lot like SQL crammed into Ruby syntax, rather than an abstract
Ruby query-specification syntax which happens to translate to SQL rather
nicely, which is what Criteria was in the first place. I’m of the
opinion that if I’m going to write something that close to SQL, I may as
well write SQL itself. If I’m going to write Ruby, I want to think in
Ruby, not SQL. I want to write it without stopping to think how this
translates to SQL and whether that’s really what I want.

One more point of disclosure in the interest of not wasting your time,
Tim: Lafcadio does almost no handling of group functions yet. I
suspect it wouldn’t be too difficult to add on, but I haven’t thought
about the problem much yet so I can’t really be certain of it.

Again, the trick would be to make it Ruby, rather than just tacking SQL
group functions onto it like Criteria does by accepting literal SQL
strings at the moment.

I very much agree with your earlier comment that those libraries which
continue to exist in this problem domain are going to have to deal with
all these issues sooner or later. I think Lafcadio and Vapor have many
of the answers to one aspect of the problem, and Criteria has answers to
another; a marriage or cross-pollination of the two would make a lot of
sense.

Tim Bates

···

On Fri, Jan 09, 2004 at 12:46:44AM +0900, Francis Hwang wrote:
from :0

tim@bates.id.au

Austin Ziegler wrote:

I’m sure the topic of Python-style blocking in Ruby has been brought up
before. Is there a general consensus on this in the community? Before I
totter off to write my own Ruby preprocessor, does such a thing already
exist? Thanks.

I chose not to learn Python because of the crap called syntactically
significant whitespace. Python’s blocking solves the problem of readable
code the wrong way.

(In other words, I’m quite glad that Ruby hasn’t succumbed to that
particular insanity.)

-austin

austin ziegler * austin@halostatue.ca * Toronto, ON, Canada
software designer * pragmatic programmer * 2004.01.08
* 15.43.45

Stuart Feldman - author of make on syntactically significant whitespace.

Why the tab in column 1? Yacc was new, Lex was brand new. I hadn’t tried
either,
so I figured this would be a good excuse to learn. After getting myself
snarled up
with my first stab at Lex, I just did something simple with the pattern
newline-tab.
It worked, it stayed. And then a few weeks later I had a user population
of about
a dozen, most of them friends, and I didn’t want to screw up my embedded
base.
The rest, sadly, is history.

I hate whitespace that means anything other than token separation.

Ralph

···

On Fri, 9 Jan 2004 04:33:15 +0900, Pete y wrote:

FWIW, vim has wonderful Ruby support, and there’s macros that Do The Right
Thing when I type “def”, “when”, “case”, etc.s

-austin

···

On Sat, 10 Jan 2004 02:01:40 +0900, GGarramuno wrote:

So… I’d rather set a hotkey like
F1 to enter “end” for me with a macro and do it all as a single key
and less typing.


austin ziegler * austin@halostatue.ca * Toronto, ON, Canada
software designer * pragmatic programmer * 2004.01.09
* 22.36.47