Rails equivalent of Delphi SetRange()

I am currently converting a large Delphi webbroker application to RoR.
The Delphi application makes extensive use of client datasets and
setrange which can limit the range of records returned to a client
dataset in an SQL call without the overhead of making another SQL call.
The setrange call can be issued in a loop to repeatedly change the range
of records viewable in the dataset.

My app frequently issues the setrange command to a dataset in a loop in
order to test for previous game time bookings in a sports application.
Is there any way I can do this in Rails or do I need to repeatedly make
SQL queries to return the data subset?

There are a few ways to do this.

One would be using a named_scope in Rails 2 or just plain scope in
Rails 3

class Bookings < ActiveRecord::Base
  named_scope :discount, :conditions => { :discount > .5 }

I like to model class methods for pulling specific information from
the database

  def self.inactive
    find( :all, :conditions => ["inactive = ?", 1], :order
=> :last_calibrated_on )
  end
And then in the controller:

@inactive = Item,inactive

In Rails 3 there is a new query interface (
http://m.onkey.org/2010/1/22/active-record-query-interface )

The neat thing here is that it seems to map well to what you're doing.
You start off creating a relation that you can then manipulate, but
the query doesn't fire until you want it too.

Getting those datasets or updating the limits could be done through
javascript on the view page, periodically_call_remote or an observer
on a field.

Thanks for your reply. A couple of further comments / question. I am a
noob in Rails.

I like to model class methods for pulling specific information from
the database

  def self.inactive
    find( :all, :conditions => ["inactive = ?", 1], :order
=> :last_calibrated_on )
  end
And then in the controller:

@inactive = Item.inactive

This looks like it will still fire off a query which is what I want to
avoid.

In Rails 3 there is a new query interface (
http://m.onkey.org/2010/1/22/active-record-query-interface )

Model scoping looks like it might do the job. My problem is that the
loop may reset the client dataset range (its not really the client as in
the browser, the ISAPI sits on the server and the client dataset is a
query result) many thousands of times while testing for a court booking
or a game draw which is not a close repeat of a previous instance.
Making a new query would be very detrimental to performance.

In the above link you provided there is a paragraph on model scoping and
the following example seems to do the job:-

cars = Car.scoped
rich_ppls_cars = cars.order('cars.price DESC').limit(10)

It appears that the call to cars fires the query but the call to
rich_ppls_cars only works on the resultant dataset and does not fire a
new query. Would that be correct? For example, I could then say
poor_ppls_cars = cars.order('cars.price ASC').limit(10) and I would get
different data without a new query?

Mark Horrocks wrote:

I am currently converting a large Delphi webbroker application to RoR.
The Delphi application makes extensive use of client datasets and
setrange which can limit the range of records returned to a client
dataset in an SQL call without the overhead of making another SQL call.
The setrange call can be issued in a loop to repeatedly change the range
of records viewable in the dataset

This is probably not a great way of doing things. If you have 1000
records that you're paging through 10 at a time, there is no reason to
fetch all 1000 if you only want to read 10. Certainly you should avoid
extra queries, but you should also avoid unnecessarily large results.
In the example I gave, I think separate queries would be the better
solution. If you can describe your use case in more detail, it might
help.

Marnen Laibow-Koser wrote:
If you have 1000

records that you're paging through 10 at a time, there is no reason to
fetch all 1000 if you only want to read 10.

There aren't anywhere near that many. Its just that I change the view of
the records returned many times while testing for a game time that
hasn't been booked by the two teams for the longest period of time while
re booking fixtures. The use case is that game fixtures may be redrawn
and then require new court bookings part way through the season. Here is
some code excerpts.

while not cdsGamesToBook.Eof do
    begin

    teamno := cdsGamesToBook.FieldByName('teamA').Value;

    cdsTeamsToBook.SetRange([teamno],[teamno]);

    if cdsTeamsToBook.RecordCount = 0 then
      begin

      cdsTeamsToBook.CancelRange;

      cdsTeamsToBook.Insert;

      cdsTeamsToBook.FieldByName('teamno').Value := teamno;

      cdsTeamsToBook.Post;

      end;

    teamno := cdsGamesToBook.FieldByName('teamB').Value;

    cdsTeamsToBook.SetRange([teamno],[teamno]);

In this case the code is just iterating through a list of teams so the
max interations might be around 16 here for 16 teams. The use of
setrange saves querying the DB 16 times. This loop would in turn be in a
separate loop which would issue setrange on the previous games played in
order to find the longest time either team has not previously been
booked to play at a particular time. The result will be that several
small datasets will have setrange applied maybe a dozen times each but
the tests may be many. The actual number of queries performed on the DB
will be very few.

Mark Horrocks wrote:

Marnen Laibow-Koser wrote:
If you have 1000

records that you're paging through 10 at a time, there is no reason to
fetch all 1000 if you only want to read 10.

There aren't anywhere near that many. Its just that I change the view of
the records returned many times while testing for a game time that
hasn't been booked by the two teams for the longest period of time while
re booking fixtures. The use case is that game fixtures may be redrawn
and then require new court bookings part way through the season.

I'm still not completely sure I understand exactly what you're doing,
but it looks to me like you're running onto trouble because you're
trying to have the app do queries that should be done on the DB side.
Can you have the database do the calculations and return only the
records you actually want?

Marnen Laibow-Koser wrote:

I'm still not completely sure I understand exactly what you're doing,
but it looks to me like you're running onto trouble because you're

If I want to re book courts after a game redraw part of the way through
a competition, its necessary to check the previous game time bookings
and find a time which has not been played before or find the time which
has not bee played for the longest time.

So, I will get a list of possible times to book and the id numbers of
both teams. If I check through many past games I will normally find that
all times have previously been booked in past games. Say 8 weeks have
previously been played for 6 teams and there are 4 game times available.
Odds are that every time has previously been booked at some time. So the
next iteration, I will reduce the number of past games visible and test
each time for both teams.

Eventually, by reducing the number of visible past games on each
iteration, I will find a time that has not been booked for either team
for the greatest number of weeks and this will be the best time to book
the future game being tested. Because I have 2 teams and many future
games to book I will repeat this process on a dataset of past games many
times.

It doesn't make sense to get that list of past games many times.
SetRange allows me to reduce the number of past games visible on each
pass of a dataset and then return it to the full set for the next future
game to be tested or the next team to be tested, or the next time to be
tested. This way I can query the past games only once, and then change
the range of visible games on it many times while looking for optimal
times to book each future game to be booked.

I can't find this ability in Rails.

Marnen Laibow-Koser wrote:

Huh? A Rails "recordset" is simply an array. You can take a piece of
it just as you would from any other Ruby array. There's no explicit
SetRange because you can just use array slicing.

Thanks! I guess this is just what I am looking for. Setrange gives the
ability to slice the array based on the data contained within it rather
than just by the index number. I am hoping i can find a way to do this
in Rails. As I mentioned, I am new at Ruby on Rails but have made
several simple applications already. I am now ready to tackle a far more
complex app.

It might help you a bunch to think of it as Ruby and not Rails. Most
of what your doing is writing Ruby and in many situations you will
need to know how. Look through the Array methods here and you should
get an idea of what can be done;

http://ruby-doc.org/core/classes/Array.html

Rails also injects some of it's own methods into Array;

http://api.rubyonrails.org/
under: ActiveSupport::CoreExtensions::Array

You should find everything you need by doing some googling. Also I
would try to come at it from different angles to find the best
solution. Sometimes when you rewrite an app you try to copy it
verbatim when what is needed is rethinking of the problem.

> And then in the controller:

> @inactive = Item.inactive

This looks like it will still fire off a query which is what I want to
avoid.

The query would only fire (once) prior to rendering the view. For
example, if you set the variable in the index action of the controller
the query would only be run after calling the index action.

cars = Car.scoped
rich_ppls_cars = cars.order('cars.price DESC').limit(10)

It appears that the call to cars fires the query but the call to
rich_ppls_cars only works on the resultant dataset and does not fire a
new query. Would that be correct? For example, I could then say
poor_ppls_cars = cars.order('cars.price ASC').limit(10) and I would get
different data without a new query?

In Rails 3 the cars example above, you are creating a relation. The
query is fired when you call rich_people_cars.first or .all

Looking at some of the other posts here I agree there could be some
misunderstanding of what you're trying to do.

If it's 16 teams * 16 games that's only 256 results. You can query all
the records and then logically view the remainder in instance
variables.

Also, if you're looking at the bookings for holes then perhaps you set
the query to return records based on the date and venue. There are
only so many dates and venues available after all.

Interesting problem though and a good way to learn Rails.

Bb Serviss wrote:

Many thanks to everyone who replied, this is a great forum. Delphi has
been kind to me with the (visual component library) client data set
which allows me to make a single query, then scope the remaining records
in any way I see fit and loop through them, breaking out of the loop
anytime I see fit. I can also use summary functions on the cds to return
sum and count, etc, all without any further queries. Anyway, enough of
Delphi, the requirement to use Windows servers has finally killed Delphi
for me.

The basic tasks I need to learn in Ruby are

1. create an array of hashes (records). I guess this is the result of a
query. (cars = Car.all) but I also need to create them by hand
sometimes.

2. Loop through the array, reading each record (I figure I can test each
record rather than limit the records in the apparent dataset ala
SetRange and perform some processing.

3. Breaking out of the loop anytime a condition has been met (e.g. the
game has been booked).

I am sure there are plenty of material available to learn from. What I
really get from forums like this are hints as the the *best* way to go
about stuff. So far I am really enjoying RoR and have been playing with
it for over a year now and have made a couple of applications similar to
the Depot sample in the excellent book Agile Web Programming.

Mark Horrocks wrote:

Bb Serviss wrote:

Many thanks to everyone who replied, this is a great forum. Delphi has
been kind to me with the (visual component library) client data set
which allows me to make a single query, then scope the remaining records
in any way I see fit and loop through them, breaking out of the loop
anytime I see fit.

Delphi has been unkind to you, then. That's a bad way to interact with
a database. In general, it is much better practice to have the DB
narrow down the data as much as possible and loop through as few records
as possible on the app side.

I can also use summary functions on the cds to return
sum and count, etc, all without any further queries.

Your database can already do this. Please learn about SQL aggregate
functions, which are the correct way to achieve this. (Rails abstracts
these in the Calculations module.)

Anyway, enough of
Delphi, the requirement to use Windows servers has finally killed Delphi
for me.

Nobody's made an open-source Kylix clone?

The basic tasks I need to learn in Ruby are

1. create an array of hashes (records). I guess this is the result of a
query. (cars = Car.all) but I also need to create them by hand
sometimes.

2. Loop through the array, reading each record (I figure I can test each
record rather than limit the records in the apparent dataset ala
SetRange and perform some processing.

3. Breaking out of the loop anytime a condition has been met (e.g. the
game has been booked).

No. You're going about this entirely the wrong way. You seem to be
trying to copy your Delphi application line by line. If that is even
possible, it will lead to unidiomatic Rails.

What I would recommend instead is this: learn the Rails framework's
basics (from the Rails Guides or elsewhere), then reimplement the
*functionality* of the Delphi app in Rails, not its *architecture*.

Remember to do all development test-first (I recommend RSpec and
Cucumber). In fact, if you can write Cucumber stories for your Delphi
app, you'll know when you've copied the functionality properly.

I am sure there are plenty of material available to learn from. What I
really get from forums like this are hints as the the *best* way to go
about stuff.

Hint: don't try to write Delphi in Rails. If you really want to do
that, I'm sure you can find a Pascal Web framework...

So far I am really enjoying RoR and have been playing with
it for over a year now and have made a couple of applications similar to
the Depot sample in the excellent book Agile Web Programming.

Then you should already have a basic idea of how a Rails app works, and
you should try to use that structure instead of what you're used to from
Delphi.

Bonus tip: your lack of familiarity with the Array class suggests that
you need to get more familiar with basic Ruby, apart from Rails.

Good luck! Please don't hesitate to ask if you have further questions.

Best,

Marnen Laibow-Koser wrote:

Mark Horrocks wrote:

Bb Serviss wrote:

Delphi has been unkind to you, then. That's a bad way to interact with
a database. In general, it is much better practice to have the DB
narrow down the data as much as possible and loop through as few records
as possible on the app side.

Given that the returned records were relatively few (less than 200) and
that I needed to progressively narrow the view of those records down
then repeatedly restore the original view and go through the process
again with different teams and times, I had thought it was more
efficient to do this on the app side.

Nobody's made an open-source Kylix clone?

Even if they had it would not enjoy the same large user base and support
that RoR has.

No. You're going about this entirely the wrong way. You seem to be
trying to copy your Delphi application line by line. If that is even
possible, it will lead to unidiomatic Rails.

That was my original intention but I will take your advice on this. It
was always apparent that there was going to be a great deal of
refactoring in any case.

What I would recommend instead is this: learn the Rails framework's
basics (from the Rails Guides or elsewhere), then reimplement the
*functionality* of the Delphi app in Rails, not its *architecture*.

Bonus tip: your lack of familiarity with the Array class suggests that
you need to get more familiar with basic Ruby, apart from Rails.

That's for sure and I have been doing a lot of reading of late.

Good luck! Please don't hesitate to ask if you have further questions.

Thanks! I appreciate the feedback I get from this forum as a noob.

Mark Horrocks wrote:

Marnen Laibow-Koser wrote:

Mark Horrocks wrote:

Bb Serviss wrote:

Delphi has been unkind to you, then. That's a bad way to interact with
a database. In general, it is much better practice to have the DB
narrow down the data as much as possible and loop through as few records
as possible on the app side.

Given that the returned records were relatively few (less than 200) and
that I needed to progressively narrow the view of those records down
then repeatedly restore the original view and go through the process
again with different teams and times, I had thought it was more
efficient to do this on the app side.

Well, if you're really retrieving a small recordset and doing lots of
different operations on it, then yes, it *might* be more efficient do to
this in the application. Maybe.

[...]

No. You're going about this entirely the wrong way. You seem to be
trying to copy your Delphi application line by line. If that is even
possible, it will lead to unidiomatic Rails.

That was my original intention but I will take your advice on this. It
was always apparent that there was going to be a great deal of
refactoring in any case.

Hmm. You *could* do a line-by-line port, then refactor to a more Railsy
architecture -- but it will be a herculean refactoring. Rewriting to
the new architecture from scratch will probably be easier.

Best,

Marnen Laibow-Koser wrote:

Hmm. You *could* do a line-by-line port, then refactor to a more Railsy
architecture -- but it will be a herculean refactoring. Rewriting to
the new architecture from scratch will probably be easier.

I will do it from scratch. The algorithms for game draws and court
bookings are quite complex.

In the case of the game draw, there may be previous games played and the
draw is a redraw for part of the season due to the addition or
removal/transfer of teams. I need to ensure that the teams (up to 16
teams) have not played each other for the longest time. Its a matter of
testing each future week in the draw template to find the best week to
start.

In the case of court game time bookings, I need to ensure that neither
team has been booked at the time and court combo being tested for the
longest time. The teams will complain that another team has had x early
games while they have had x late games. Further, the system must support
clash teams where a team playing volleyball will not want to booked at
the same time as a team playing basketball if some players play in both
teams. Teams may also have times when they can't turn up to play.

My customers are commercial social sports centers with potentially fluid
competitions as teams may come and go during a season or there may be
team regrades after several weeks if a team is either much better or
worse than the other teams in its league. As you can see the temptation
to port to what works already is very compelling but it seems the best
way forward is to refactor from scratch. Pity RoR wasn't around in 2001
when I originally wrote it. The website is sportdata.com.au

I use Delphi (Lazarus) at work and I constantly miss ruby features :slight_smile:

I can only echo Marnen's remarks about Rails, Delphi does some weird
things and I've found that doing it the "Rails" way is usually simpler
and much more efficient.

As far as the bookings, you should do a bottom up review by starting
off with what would be the perfect view for you. Something by venue
that not only shows the two teams but their win/loss percentage and
amount of late/early games which would aid in scheduling. Make
something that makes sense to you.

Clashes though - shouldn't that be the player's responsibility?

Also, don't forget unit tests and a revision control system (like git)
as both of these will help you in many ways.

Bb Serviss wrote:

I use Delphi (Lazarus) at work and I constantly miss ruby features :slight_smile:

I still use Delphi 7 because later versions don't like some of the com
stuff in the app exe. Its a nightmare just to refactor that. I have
numerous third party dependencies like patches to Internet Express and
some of these won't work in later versions either due to unit location
changes and the like. Not only, but the Internet Express javascript has
i.e. extensions which won't work in mozilla browsers. That said, I like
the Internet Express grid as it has multi record browsing, editing and
updates in a single trip to the web server. Probably not so much of a
problem anymore with faster internet but its a nice feature.

I can only echo Marnen's remarks about Rails, Delphi does some weird
things and I've found that doing it the "Rails" way is usually simpler
and much more efficient.

Thanks, I have resolved to go that way.

As far as the bookings, you should do a bottom up review by starting
off with what would be the perfect view for you. Something by venue
that not only shows the two teams but their amount of late/early games which would aid in scheduling. Make something that makes sense to you.

That was the original plan long ago but with unknown numbers of courts
and game times, what is a late game? I found that its better to simply
distribute the courts and game times evenly, starting with the game time
and then the courts.

Clashes though - shouldn't that be the player's responsibility?

No, the sports centre allocates the game times and courts.

Also, don't forget unit tests and a revision control system (like git)
as both of these will help you in many ways.

I have git organized, tests are high on my list. The stock test all bomb
because of the PK / FK relationships. I need to construct the fixtures
accordingly. Right now I am still sorting out the easy part which is the
CRUD on the organizations / sports / leagues / teams | games / players
and members, holidays, courts, etc.

Next I will attempt a game draw. Expect further questions :slight_smile:

Thanks for the input.

Mark