Design question and opinions

I'm just working through the Agile web dev book (which is great) and i'm at the point i'd like to put together something to test my understanding. Luckily I have a project that will help my gaming community organise games on modern warfare 2. It also doesn't map onto the depot like for like which is good :slight_smile:

The basic idea is you have a list of game modes and a list of maps. These lists can be added to. They can't be removed but they can be set to inactive.

Now a game consists of a map and a game mode. A match consists of one or more games (specified by the user). The games for a particular match get chosen at random - the one rule being that the a map and game mode may only appear once in the match.

The application should allow the user to enter the number of games they want to play and generate a match. this match is given a short description (e.g. Team 1 v Team 2) and gets stored so that people playing the match can see what games they will be playing.

So far i've got the models i think i need:

- map(s) - game_modes(s) - game(s) - match(s) - match_game(s)

I've created the map, game_model and match using scaffold as i want to be able to maintain them through the UI.

I've created game and match_game as pure models as they are part of the match creation.

So far, am i on the right track based on good practice and design?

One consideration i did have was whether, based on the fact that the maps and game_modes tables will always have a relatively small number of entries, i should just autogenerate the rows (20 in each table = 400 rows. random generate a number between 1 and 400 and it picks the game). If i was to do this where would i embed the method to autopopulate the game table? (in the game model itself or a helper - i'm not sure on best practice). This could also be called if the admin decided to update the table with a new entry (i.e. it would create the extra matches needed).

At this stage i haven't even thought through how the actual UI will build all the bits and put them together but i'm thinking this would be the job of my match controller which creates a match object, x game objects and sets the 'games' property of the match object before committing the changes to the DB and letting rails do some magic to save it all (i'm not looking for someone to tell me as i'd like to work it out but if i'm way off track here then a pointer or 2 would be appreciated).

Thanks for taking the time to read and i appreciate any comments or opinions you can offer this newbie :slight_smile:

ooh found 'has_many_and_belongs_to' in ActiveRecord that takes care of the match->match_game<-game relationship nicely!

The solution i think i'm going to go with is to pre-load the game table and add some methods to the game model to return a game or games in the various ways i'll need them e.g.

get_random_game get_random_active_game(exclude_these_games) get_all_active_games

My match controller can then use these as part of the logic that builds the match before committing it to the db.

If someone updates the maps or game_modes models then i'll trigger an 'update_list_of_games' on the Game class to populate any new game modes as a result of the change.

Not sure what people feel about this but it seemed right in my head lol.

Jon Cox wrote:

I've created the map, game_model and match using scaffold as i want to be able to maintain them through the UI.

I've created game and match_game as pure models as they are part of the match creation.

So far, am i on the right track based on good practice and design?

If you have written your Cucumber stories and/or RSpec examples before all this work, using them to drive your design, then you are on the right track to good practice and design.

One consideration i did have was whether, based on the fact that the maps and game_modes tables will always have a relatively small number of entries, i should just autogenerate the rows (20 in each table = 400 rows. random generate a number between 1 and 400 and it picks the game). If i was to do this where would i embed the method to autopopulate the game table? (in the game model itself or a helper - i'm not sure on best practice). This could also be called if the admin decided to update the table with a new entry (i.e. it would create the extra matches needed).

The latest version of Rails has a convention for this, and an associated rake task. The feature is called "Seeds." You'll notice there will be a db/seeds.rb file for putting your Ruby code to populate your seed data. Then you can run rake db:seed after running rake db:migrate to execute the data seeds.

At this stage i haven't even thought through how the actual UI will build all the bits and put them together but i'm thinking this would be the job of my match controller which creates a match object, x game objects and sets the 'games' property of the match object before committing the changes to the DB and letting rails do some magic to save it all (i'm not looking for someone to tell me as i'd like to work it out but if i'm way off track here then a pointer or 2 would be appreciated).

I would think that the controller would simply handled the request to "create" a new game:

matches_controller.rb

The convention is for the HABTM table name to be alpha ordered and plural, so in this case it would be games_matches. Then you will not have to explicitly name it using :through.

If you have not seen it already you may find the ActiveRecord Associations guide at http://guides.rubyonrails.org/ useful. Also the other guides there of course.

Colin

Jon Cox wrote:

ooh found 'has_many_and_belongs_to' in ActiveRecord that takes care of the match->match_game<-game relationship nicely!

The current trend in Rails is toward has_many :through instead of has_and_belongs_to_many (HABTM). The has_many :trough is more flexible, and often more RESTful.

Imagine what happens if you need to store something that relates to a particular game of a match. You can't store that in either the matches nor the games tables. You would instead need access to the joining table match_games.

class Match << ActiveRecord::Base   has_many :match_games   has_many :games, :through => :match_games end

class Game << ActiveRecord::Base   has_many :match_games   has_many :matches, :though => :match_games end

class MatchGame << ActiveRecord::Base   belongs_to :match   belongs_to :game end

Again write your stories and use them to drive your design. This is opposed to trying to build your application from the perspective of the data model. Letting your acceptance criteria drive your design will develop into a better user experience. You'll be concentrating on what the user sees, and how they interact with your application. I think you'll find that the model almost takes care of itself as you write code to make your stories (and specs) pass.

Feature: player starts a new match   As a player   I want to start a new match   So that I can have fun fragging my friends

  Scenario: starting a match     Given a new match     When I start the match     Then I should <fill in what you expect to see (or happen)>     And I should <fill in other things you expect when starting a match>

  Scenario: <continue writing your acceptance criteria and let that drive your design>     Given ...     When ...     Then ...

Robert Walker wrote:

Jon Cox wrote:

ooh found 'has_many_and_belongs_to' in ActiveRecord that takes care of the match->match_game<-game relationship nicely!

The current trend in Rails is toward has_many :through instead of has_and_belongs_to_many (HABTM).

Says who? I tend to start with HABTM and refactor to has_many :through if necessary.

The has_many :trough is more flexible, and often more RESTful.

More flexible, yes. But it's not the Simplest Thing That Could Possibly Work [TM] in this case.

HABTM is a special simplified case. If your data fits the special case, it makes sense to take advantage of it.

As for REST, that's a routing issue, not an associations issue.

Best,

Marnen Laibow-Koser wrote:

Robert Walker wrote:

Jon Cox wrote:

ooh found 'has_many_and_belongs_to' in ActiveRecord that takes care of the match->match_game<-game relationship nicely!

The current trend in Rails is toward has_many :through instead of has_and_belongs_to_many (HABTM).

Says who? I tend to start with HABTM and refactor to has_many :through if necessary.

Says, DHH in his initial promotion of the decision to move to a RESTful design. I did not, however, say that that this was right, just that it has been proposed by the person who developed the idea.

The has_many :trough is more flexible, and often more RESTful.

More flexible, yes. But it's not the Simplest Thing That Could Possibly Work [TM] in this case.

I concede this point.

HABTM is a special simplified case. If your data fits the special case, it makes sense to take advantage of it.

As for REST, that's a routing issue, not an associations issue.

Again I concede. But, this was just a misuse of the terminology. I meant to say that having the joining model can lead to more RESTful routes in many cases. Again this was another point in DHH's initial talk on the move to REST. At that time, however, all this REST stuff was experimental. I'm sure we've learned a lot since that initial presentation on the subject matter.

Robert Walker wrote:

If you have written your Cucumber stories and/or RSpec examples before all this work, using them to drive your design, then you are on the right track to good practice and design.

Thats great advice and was on my list of 'when i've got the basics down i need to go and learn about....' list. Sounds like I should probably make it a priority. the later examples you gavce helped reinforce this for me.

The current trend in Rails is toward has_many :through instead of has_and_belongs_to_many (HABTM). The has_many :trough is more flexible, and often more RESTful.

I did pick up on this during some reading i did this morning (Colin's suggestion to read the ActiveRecord Associations guide helped me better understand). As this is a very simple system that i have no plans to extend (famous last words hehe) i felt HABTM fitted the bill.

The latest version of Rails has a convention for this, and an associated rake task. The feature is called "Seeds." You'll notice there will be a db/seeds.rb file for putting your Ruby code to populate your seed data. Then you can run rake db:seed after running rake db:migrate to execute the data seeds.

Brilliant! Thank you for the pointer.

This is often referred to as "skinny controller, fat model design," and is typically promoted in Rails as a "best practice."

aha (penny starting to drop here)

Thanks for taking the time to read this and i really appreciate all the responses and advice.