KISS and DRY? Not even close!

Hi all,

After working on my first rails app and having handed over some very
sophisticated coding from a lot of you guys. My app does what I want it
to
do, but I’m no where near the end yet, and it seems that the KISS and
DRY
objectives already went down the drain.

It could be lack of knowledge, only doing Ruby on (and) Rails for a
month, but
I’m stuck on the following:
Keeping my code simple seems to influence the ease of use of my
application.
There is functionality in my application that’s very handy and makes you
not
have to repeat yourself with filling out forms. The down side of this is
(seems) that there are pieces of code, duplicated and some statements
are
very complex. And this is not what Rails is about. Does this sound
familiar
to anybody out there?

I’m not going to include my whole app but below is one method that made
me
come to the formentioned dilema.

The models:
company (hm) <-> (bt) contacts
project (ho) <-> (bt) project_contacts

The guilty actions (new_project and create_project) are shown below:
(They’re workin, but I don’t even wanna think about the edit/update
actions)

def new_project
@project = Project.new
@contacts_list = Contact.find_by_sql(‘select t1.name, t2.first_name,
t2.last_name, t2.id from companies AS t1, contacts AS t2 where t1.id =
t2.company_id order by t1.name’)
@contacts_list = @contacts_list.collect { |c| [ c.name + " - " +
c.first_name

  • " " + c.last_name, c.id ] }
    render_action ‘new_project’
    end

def create_project
@contact = Contact.new
@project = Project.new(@params[‘project’])

@project_contact = ProjectContact.new(@params['collect'])
@project.project_contact = @project_contact

contact = Contact.find(@params['id']['collect'])
@project.project_contact.first_name = contact.first_name
@project.project_contact.last_name = contact.last_name

if @project.save
	flash[:notice] = 'Project was successfully created.'
		redirect_to :action => 'show_project', :id => @project.id
else
	render :action => 'edit_project'
end

end

Should I have a clean go. Or shape up, go at it and simply continue?

Regards,

Gerard.

P.S. Thanks a lot for your time if you made it al the way down here!!


“Who cares if it doesn’t do anything? It was made with our new
Triple-Iso-Bifurcated-Krypton-Gate-MOS process …”

My $Grtz =~ Gerard;
~
:wq!

Define a specific problem you have here that is as small as possible.
That will help. Your problem is very vague

Why do you have a relationship between project and project_contacts?
What is project_contacts? Why don’t you have a relationship between
project and contacts? Your project isn’t referencing contact, it’s
duplicating them.

Can each contact belong to only 1 company? If so, you can just get all
contacts and access each contact’s company with contact.company. I
don’t see what exactly your handmade sql query is for.

I think this:

def new_project
@project = Project.new
@contacts_list = Contact.find_by_sql(‘select t1.name, t2.first_name,
t2.last_name, t2.id from companies AS t1, contacts AS t2 where t1.id =
t2.company_id order by t1.name’)
@contacts_list = @contacts_list.collect { |c| [ c.name + " - " + c.first_name

  • " " + c.last_name, c.id ] }
    render_action ‘new_project’
    end

can be written like this:

def new_project
@project = Project.new
@contacts = Contact.find :all, :include => :company
end

and if you add this into your Contact model:

def name_and_company
“#{company.name} - #{first_name} #{last_name}”
end

then you can do your view like this:

    <% @contacts.each do |contact| %>
  • <%= contact.name_and_company %>
  • <% end %>

Does that help any?

Douglas

Mike,

Replies inline … (hope it’s not to long)

On Wednesday 11 January 2006 00:04, Mike H. tried to type something
like:

Define a specific problem you have here that is as small as possible.
That will help. Your problem is very vague
So is my feeling about this … :slight_smile:

Why do you have a relationship between project and project_contacts?
What is project_contacts? Why don’t you have a relationship between
project and contacts? Your project isn’t referencing contact, it’s
duplicating them.
The copy is intentional. If a contact no longer works for a company
(resigned
or changed jobs), the contact info related to a project is gone, and
could
result in nil errors when viewing project data. The project_contacts
table
(belongs_to project) has a few fields of the contacts table copied into
them.
Not al fields are copied because the contacts table (belongs_to
companies is
rather exensive. But I would like to be able to (re)view project data
(even
on closed projects).

Can each contact belong to only 1 company? If so, you can just get all
contacts and access each contact’s company with contact.company. I
don’t see what exactly your handmade sql query is for.
This feeds a dropdown list, in the create new_project view, that holds a
combination of “Company name - Firstname Lastname”. Sorted on company,
and
therein on Name. This was done with the idea that when creating a new
project, while having a customer on the phone, you can quickly select
his
info, type in a few project fields and save the stuff. And i looks very
self
explanatory (wel in the front-end of my app anyway … :slight_smile:

To continue on the ‘feeling’. The code doesn’t look tidy anymore. So one
of
the idea’s that I have is moving the whole "Contact.find_by_sql … "
statement to a model, and simply being able to reference this via an
object
on update or create. Is this possible, and would it be proper MVC
ethics?

The whole thing started with the comments in this link:

http://wiki.rubyonrails.com/rails/pages/HowToReuseEditViewsForNewViews

Having one view (edit_project) for creating new and editing existing
records,
is a great DRY implementation. Even the comment on just having a <%=
auto_form %> in your view made me go through the roof. But (en here it
comes), I’ve got no clue on how to maintain (or end up at) this, with
the
issue at hand. So the ‘feeling’ or question is (are):

  • Is all this to complex and not according to DRY ethics, or is it just
    me?
  • And is it wise (and MVC like) to move the "Contact.find_by_sql … "
    out of
    the controller?

All your insights are very welcome. Thanx a lot!!

Regards,

Gerard.

but I’m stuck on the following:
The models:
t2.last_name, t2.id from companies AS t1, contacts AS t2 where t1.id =
@project_contact = ProjectContact.new(@params[‘collect’])
render :action => ‘edit_project’


Rails mailing list
[email protected]
http://lists.rubyonrails.org/mailman/listinfo/rails


“Who cares if it doesn’t do anything? It was made with our new
Triple-Iso-Bifurcated-Krypton-Gate-MOS process …”

My $Grtz =~ Gerard;
~
:wq!

Douglas,

It’s one o’ clock at night over in the good ol’ Netherlands, so it’s
time for
bed. But your code looks absolutely marvelous. I am going to chew on it
tomorrow. And definitely get back to you on this.

Kind regards,

Gerard -most-of-the-time-wanting-to-much-to-quickly- Petersen

On Wednesday 11 January 2006 00:48, Douglas L. tried to type
something like:

“#{company.name} - #{first_name} #{last_name}”
Does that help any?

Douglas


Rails mailing list
[email protected]
http://lists.rubyonrails.org/mailman/listinfo/rails


“Who cares if it doesn’t do anything? It was made with our new
Triple-Iso-Bifurcated-Krypton-Gate-MOS process …”

My $Grtz =~ Gerard;
~
:wq!

Somewhat OT:

2006/1/11, Kevin O. [email protected]:

Remember, Rails values convention over configuration. If you stray too
far from the convention you aren’t doing yourself or the code
maintainers any favors.

I wonder if there is scope in Rails for adding custom conventions. For
example, if you have a model called “Car”, by convention it would map
to a table called “cars”. Would it be possible to redefine this, so
that “Car” would map to “myapp_cars”, and would also allow “Person” to
map to “myapp_people” without further coding? I assume that this could
be done by extending the ActiveRecord class, and then using that as
theparent for your model classes,though it isn’t something I’ve tried
in practice.

Douglas

Gerard

A couple of ideas here…

I supose you could set up the contacts table so that entries never get
deleted. Add a ‘status’ column and set it to some value if they are no
longer available… then filter that out in your queries OR you could
copy the information just before the contact gets deleted.

If you are worried about storage space, you could set up a script to
archive projects of a certain age. When you do that, you simply check
to see if the user is still active and if not, then you can archive them
too and delete them from the main table.

I find that the trick for staying DRY is not to over do it. If you get
too tricky with your methods for staying DRY, then the code gets
confusing and unmaintainable.

You should always code in such a way that someone else can pick up your
code and understand it.

While reusing the edit view for a new action is clearly DRY, it is also
more complicated and confusing.

My preference is to give the principle of ‘least surprise’ precidence
over DRY.

Remember, Rails values convention over configuration. If you stray too
far from the convention you aren’t doing yourself or the code
maintainers any favors.

_Kevin

Another option which is more similar to what you have:
@contact_list = Contact.find(:all, :include => “company”).collect{
|c|, ["#{c.company.name} - #{c.firstname} #{c.lastname}", c.id])

This will give you what you had before, with out the extra method. If
you think you’ll use this format somewhere else though, go with what
Douglas suggested and add the method to the class.

Also, to avoid duplication of contact info you could switch the
relationships around some. So Project will now belong_to a contact and
you could now do something like this in the view:
<%= select “project”, “contact_id”, @contact_list %>

and then your controller code would be reduced to:
@project = Project.new params[:project]
@project.save

And this will automatically set the contact and save the info for you!

Hope some of this helps!
-Nick

Douglas L. wrote:

Somewhat OT:

I wonder if there is scope in Rails for adding custom conventions. For
example, if you have a model called “Car”, by convention it would map
to a table called “cars”. Would it be possible to redefine this, so
that “Car” would map to “myapp_cars”, and would also allow “Person” to
map to “myapp_people” without further coding? I assume that this could
be done by extending the ActiveRecord class, and then using that as
theparent for your model classes,though it isn’t something I’ve tried
in practice.

Douglas

Yeah, I think there is a way to add a prefix to your table names, but I
haven’t used it.

To throw one more thing your way concerning the active/inactive
status. Take a look as the acts_as_paranoid which adds a deleted field
to your model, and may do what you want with out having to do much
yourself.

It may or may not fit your need, but I think it would be work a look.

-Nick

Hi all,

replies inline …

On Wednesday 11 January 2006 02:20, Nick S. tried to type something
like:

Another option which is more similar to what you have:
@contact_list = Contact.find(:all, :include => “company”).collect{
Didn’t know about the “:include” option (yet), so that’s a good one to
investigate. (although I’m not sure if that still applies with the new
ideas
below).

Furthermore does putting the “def name_and_company” in a model sound as
something I like.

Had a discussion with a datamodelling guru this morning, and I’m walking
around with the following ideas.

  • Giving a contact a status field ‘active / inactive / maybe more’, so
    they’re
    not deleted, but merely not visible in the (active) contacts database.
    Resulting in having the data available for the closed projects.

  • There was talk of an extra table. Roles ( a person can have a role in
    a
    project). But for a 1 man company (and his 1st rail app) this seems
    overkill.

  • In another email in this thread (from Kevin), some nice ideas on the
    simplicity and readability of code. (that you can read there).

So Nick and Douglas, thanks a lot for the inspiration, code and
solutions
presented. I see light at the end of the tunnel … :slight_smile:

Grtz Gerard.

@project = Project.new params[:project]

It’s one o’ clock at night over in the good ol’ Netherlands, so it’s time

end
def name_and_company

Triple-Iso-Bifurcated-Krypton-Gate-MOS process …"


Rails mailing list
[email protected]
http://lists.rubyonrails.org/mailman/listinfo/rails


“Who cares if it doesn’t do anything? It was made with our new
Triple-Iso-Bifurcated-Krypton-Gate-MOS process …”

My $Grtz =~ Gerard;
~
:wq!

Kevin,

replies inline …

On Wednesday 11 January 2006 01:09, Kevin O. tried to type
something > I
supose you could set up the contacts table so that entries never get

deleted. Add a ‘status’ column and set it to some value if they are no
longer available… then filter that out in your queries OR you could
copy the information just before the contact gets deleted.
Very good idea. Thats the way to go!

If you are worried about storage space, you could set up a script to
archive projects of a certain age. When you do that, you simply check
to see if the user is still active and if not, then you can archive them
too and delete them from the main table.
Archiving is not my primary concern, although for accounting purposes,
something will arise for this in the future.

Storage is cheap, and by the time the database exceeds the 10 gigs, I
must
have a very wel running company and will be a gazillionair by that time,
and
got budget to buy extra storage … :slight_smile:

I find that the trick for staying DRY is not to over do it. If you get
too tricky with your methods for staying DRY, then the code gets
confusing and unmaintainable.

You should always code in such a way that someone else can pick up your
code and understand it.
This is THE definition I think. Very good thanx. This would also mean if
you
don’t see your code for a year, that you still can read it back
yourself.

While reusing the edit view for a new action is clearly DRY, it is also
more complicated and confusing.
I agree, and are gonna drop this implementation (Done only once, but
better
now then later). Simplicity is the key!

Very good and clearifying feedback. Thanx a lot for stearing me clear
from
future bumps in my application!

Regards,

Gerard.


“Who cares if it doesn’t do anything? It was made with our new
Triple-Iso-Bifurcated-Krypton-Gate-MOS process …”

My $Grtz =~ Gerard;
~
:wq!

Nick,

I will. Thanx a lot.

Regards,

Gerard.

On Wednesday 11 January 2006 15:40, Nick S. tried to type something
like:

Hi all,

replies inline …

On Wednesday 11 January 2006 02:20, Nick S. tried to type something
like:
Had a discussion with a datamodelling guru this morning, and I’m walking

  • In another email in this thread (from Kevin), some nice ideas on the

Douglas suggested and add the method to the class.
And this will automatically set the contact and save the info for you!

def new_project

Douglas
~
[email protected]
http://lists.rubyonrails.org/mailman/listinfo/rails


“Who cares if it doesn’t do anything? It was made with our new
Triple-Iso-Bifurcated-Krypton-Gate-MOS process …”

My $Grtz =~ Gerard;
~

:wq!


“Who cares if it doesn’t do anything? It was made with our new
Triple-Iso-Bifurcated-Krypton-Gate-MOS process …”

My $Grtz =~ Gerard;
~
:wq!

Nick,

Seems worth trying out. Couldn’t find one how to though. Is it just
installing
the plugin and putting “acts_as_paranoid” in your model?
(and create the deleted_at field in your tables ofcourse … :slight_smile:

TNX!

GrtzG

On Wednesday 11 January 2006 15:40, Nick S. tried to type something
like:

Hi all,

replies inline …

On Wednesday 11 January 2006 02:20, Nick S. tried to type something
like:
Had a discussion with a datamodelling guru this morning, and I’m walking

  • In another email in this thread (from Kevin), some nice ideas on the

Douglas suggested and add the method to the class.
And this will automatically set the contact and save the info for you!

def new_project

Douglas
~
[email protected]
http://lists.rubyonrails.org/mailman/listinfo/rails


“Who cares if it doesn’t do anything? It was made with our new
Triple-Iso-Bifurcated-Krypton-Gate-MOS process …”

My $Grtz =~ Gerard;
~

:wq!


“Who cares if it doesn’t do anything? It was made with our new
Triple-Iso-Bifurcated-Krypton-Gate-MOS process …”

My $Grtz =~ Gerard;
~
:wq!

Gerard P. wrote:

Nick,

Seems worth trying out. Couldn’t find one how to though. Is it just
installing
the plugin and putting “acts_as_paranoid” in your model?
(and create the deleted_at field in your tables ofcourse … :slight_smile:

TNX!

I think it’s a gem. try ‘gem install acts_at_paranoid’