Assignment 3: Standalone ActiveRecord

October 21st, 2009 Leave a comment Go to comments

Extension: Now due before 11:59 PM EST, Wed., Oct. 28


Errata

  1. If you try to save a game with the same name as an existing game, delete the saved game, and all of its associated Pylons and Players. You do not need to provide a warning or ask the user if this is ok.
  2. I converted the bit below on validation into a list to make it easier to understand.
  3. The download is slightly revised (e168-assignment3-jgn-1.0.004.zip), and fixes two bugs. To fix the bugs by hand:
    1. In game_persister_file.rb, add a $ to the end of each regular expression that identifies Pylons or Players in the file. This is not the greatest fix, but it will do. Thus:
      if s =~ /pylon (.+?) '(.)' '(.)' ([\-\d]+?)\s+([\-\d]+?)$/
        # . . . . . .
      elsif s =~ /player (.+?) '(.)' '(.)' ([\-\d]+?)\s+([\-\d]+?)\s+([\-\d]+?)\s+([\-\d]+?)$/
      

      Thanks go to Ron.

    2. The other problem is more serious. In the Rakefile, remove this line from the package task:
      t.package_files.exclude("#{db_dir}migrate/**/*")
      

      Thanks go to Gabriel.


The purpose of this assignment is to give you some experience with ActiveRecord. You’ll define migrations for tables support ActiveRecord models, and run the migrations. You’ll be doing all of this “by hand.” When we get to Rails, you’ll see that there are a lot of convenient scripts and shortcuts for generating models and tables, but with AR, my experience has been: no pain, no gain. This is also why the schema for this assignment is very simple. As well, there are no tests. The problem with tests for “declarative” code (which is what much of ActiveRecord is), is that the tests reveal the solution.

There is a Firefox addon that allows for easy perusal of your database: I recommend that you install this addon: https://addons.mozilla.org/en-US/firefox/addon/5817

To do this assignment, download the ZIP (e168-assignment3-jgn-1.0.004.zip), and unzip it. Copy your student_solution.rb (and supporting files, if any) into the lib/world/ directory. Verify that your code works in this new context. We made some small changes which are documented in readme.rdoc. If your code doesn’t work, check it over very carefully; if it still doesn’t work, ZIP up your project for #3 and e-mail it directly to John.

What you are going to do is write a subclass of a new class called GamePersister. To give you the flavor of this, we provide a GamePersisterFile, which saves the state of the GameManager and any Pylons or Players to a text file. This is not the most elegant solution and is not very DRY, because we want the code to be as obvious as possible.

Now, if you’ve been reading the book and have been studying the Assignment 2 code, you may be thinking that we want to have the GameManager and Entity classes be ActiveRecord models. Or you may think that GameManager, Pylon, and Player ought to be models. We’re not going to do this. The reason is that to have Pylon be a model, we would have to subclass ActiveRecord::Base. But we are already committed to a class hierarchy. Therefore, we are going to create new models called SavedGame, SavedPylon, and SavedPlayer. The only reason our models for Pylons and Players have “Saved” in their names is to differentiate them from our existing classes. If you were were starting a project from scratch and knew that you were going to save data to a database, you might very well have subclassed ActiveRecord::Base from the start. [If we use this assignment next year, we'll handle the persistence with modules. But that requires changes to the core classes that we don't want to make at this time.]

Here’s a diagram of the tables/models you must implement:

assn3_schema-small
(click to see larger version)

The “crow’s feet” (http://en.wikipedia.org/wiki/Entity-relationship_diagram#Crow.27s_Foot) in the lines between saved_games and saved_players and between saved_games and saved_pylons indicates which item refers to the other. In this case, the foreign keys are on saved_players and saved_pylons. This means that a SavedPlayer (and a SavedPylon) “belong to” a SavedGame. In reverse, Each SavedGame “has many” SavedPlayers and SavedPylons. As you get into this, you should review carefully the naming, case, and pluralization conventions used in code for ActiveRecord tables, models, and association names (in AWDR, see section 17.1, 18.3, and elsewhere; with regarding to associations, you want to watch for language such as this, in 18.3: “The parent class name is assumed to be the mixed-case singular form of the attribute name, and the foreign key field is the lowercase singular form of the parent class name with _id appended.” What this means is that in the SavedPlayer class, you would write “belongs_to :saved_game”, while in the database, the foreign key is going to be saved_game_id).

Now, because the GameManager manages a Pylon (for instance), but our ActiveRecord class is called a SavedPylon, we need some code that can add a Pylon to a GameManager based on the data in a SavedPylon. We have supplied code that will do this in GamePersister. For example:


# Add a SavedPylon to a GameManager
 def add_saved_pylon(game_manager, saved_pylon)
# Remember that a class name is just a constant.
#   game_manager.class.const_get(name) means:
# Find the constant with the given name, as the GameManager's class
# would understand that constant name. So, for instance, if the
# name is ShyPlayer, we will get the constant: ShyPlayer
# Now we can call .new on the constant (that is, on the class)
pylon = game_manager.class.const_get(saved_pylon.class_name).new(game_manager, saved_pylon.x, saved_pylon.y)
pylon.identity = saved_pylon.identity
pylon.appearance = saved_pylon.appearance
 end

There is a similar method to add a SavedPlayer, and there are a couple more methods to add collections of SavedPylons or SavedPlayers.

Another aspect of this that is a bit tricky is that when a SavedPylon or SavedPlayer is saved to the database, we include the class name of the Pylon or Player. So, when we get a SavedPylon out of the database, and want to “new” an appropriate Pylon, we have to utilize that saved class name. See the comments in the quoted code above for how we do this. Arguably, someone might use ActiveRecord’s “single table inheritance”feature to make this happen.

So, here’s what you have to do:

  1. Define migrations in db/migrate to create the saved_games, saved_pylons, and saved_players tables. The attributes must be as given in the diagram above. For the foreign key columns, use the “references” method. In your migrations, you must ensure that none of the column values may be null.
  2. Define ActiveRecord models for SavedGame, SavedPylon, and SavedPlayer. Please put these in the root of the lib/ directory. They should not be in any module.
  3. Once you have the models, define the appropriate validations and associations (has_many, belongs_to, etc. You must decide on the associations that make sense with the diagram). For associations and validations:
    1. when a SavedGame is destroyed, any associated SavedPylons or SavedPlayers should also be destroyed automatically (hint: this requires a modifier on the associations);
    2. the name of each game must be unique;
    3. all non-key attributes, except appearance and identity, must not be “empty”;
    4. the identity and appearance attributes must have length 1;
    5. all non-key attributes that are supposed to be numbers must be validated for numericality, and must only be integers;
    6. the tick number of a SavedGame must be greater than or equal to 0.

    The reason appearance and identity are excluded from the the requirement that attributes be non-empty is because they can be a single space. The Validators consider a single space to be “empty,” so we have to handle them a bit differently.

    Everything up to this point should be pretty easy. We will give a lot of credit for a perfect job on the steps above.

  4. Implement GamePersisterRdb. This should have the same behavior as GamePersisterFile (except that the data is stored to the database).
  5. For this assignment, no writeup is required. If you have notes you want to add, you may, though, put them in readme-student.txt

To load and save games, use example_game_persistent.rb. If you add “rdb” to the command line, it will use the rdb persister; otherwise it will use the file persister:


# uses the file persister
ruby example_game_persistent.rb

# uses the rdb persister
ruby example_game_persistent.rb rdb

For the “file” version, we added a sample game (called “sample.game”). When you enter the game name, leave off the “.game” part.

Questions and Answers:

  1. Q: I added some attributes to my own Pylons and Players, so they don’t get saved or loaded properly.
    A: Saving such Pylons and Players is out-of-scope. There is a fairly reasonable strategy to save Pylons and Players with extra attributes: Keep an eye on the lectures for the “serialize” method for ActiveRecord.
  2. Q: It looks like there’s an opportunity to “share” Pylons and Players across games if they have exactly the same attributes. Should we check if a Pylon or Player is already in the database?
    A: No. The foreign key “saved_game_id” can only point to own game. To do what you’re talking about would require a “join table” in the middle, so as to support has_many :through.
  3. Q: It seems as though there are opportunities for further validations. For example, I could require that the SavedGame’s x_min is less than its x_max; I could require that all SavedPylons and SavedPlayers are within their game boards.
    A: I would advise not doing that. Since the domain code is not too finicky about these details (except that it will cause exceptions to be raised!) it would be premature to lock down such details for the models.
  4. Q: Are there any additional docs (compared to Assignment 2)?
    A: No.
  1. Ron Newman
    October 23rd, 2009 at 00:42 | #1

    I'm getting lots of SQL printed to the terminal window when I save a game, load a game, or run my new tests. How can I suppress this?

  2. October 23rd, 2009 at 08:00 | #2

    @Ron Newman

    To turn off the logging, go into rdb_setup.rb, and change this line from

    ActiveRecord::Base.logger = Logger.new(STDOUT)
    

    to

    ActiveRecord::Base.logger = Logger.new("ar.log")
    

    Or some other file.

    Also:

    Regarding the escape characters in your "less" of the log -- somewhere you have this:

    ActiveRecord::Base.colorize_logging = true
    

    Or, you have failed to set it to false.

  3. Ron Newman
    October 23rd, 2009 at 11:10 | #3

    Thanks. I presume I could also just comment out the Logger.new call, resulting in no log at all?

    Project 3 has colorize_logging set to false, but ccc1 doesn't have any such ActiveRecord::Base.logger calls at all that I can find.

  4. October 23rd, 2009 at 11:15 | #4

    The default in Rails is true, so your logs should be colorized.

    If you want to turn it off, a good place to do it is to put

    ActiveRecord::Base.colorize_logging = true
    

    into either config/environment.rb (applying to all configs) or config/environments/development.rb (for just development).

    As to the colorized logs looking bad in "more": They do when I use more (or less) as well.

  5. Ron Newman
    October 23rd, 2009 at 11:24 | #5

    Thanks. I later found that the log looks fine if I use "less -r" or "less -R" . (but we're getting away from Assignment 3 now, so if I have any more questions about this, I'll be over in the Lecture 8 discussion)

  6. Ken Busch
    October 23rd, 2009 at 14:46 | #6

    I can imagine other things to validate for, e.g., Class names must begin upper casex,y coordinates have to be on the game boarddirection_x, direction_y must impose a velocity less than the speed of lightShould we stay away from validations not listed in 3a - 3f?

  7. October 23rd, 2009 at 14:53 | #7

    @Ken Busch

    Ken -- check out question 3 in the Q&A section: "Q: It seems as though there are opportunities for further validations. For example, I could require that the SavedGame’s x_min is less than its x_max; I could require that all SavedPylons and SavedPlayers are within their game boards."

    Answer: "A: I would advise not doing that. Since the domain code is not too finicky about these details (except that it will cause exceptions to be raised!) it would be premature to lock down such details for the models."

  8. Ron Newman
    October 23rd, 2009 at 18:01 | #8

    Just curious -- can you briefly explain the issue I encountered in my very first comment on this thread? It's not getting in my way, and you don't need to "fix" it, but I'd still like to understand it.

    [quote]
    const_get() seems to have lots of trouble with the BasicPlayer class:
    gm = World::GameManager.new
    gm.class.const_get("World::BasicPlayer")
    results in: NameError: wrong constant name World::BasicPlayer
    gm.class.const_get("BasicPlayer")
    results in: ArgumentError: World is not missing constant BasicPlayer!
    [/quote]

  9. October 23rd, 2009 at 18:40 | #9

    @Ron Newman

    Some of the code uses ActiveSupport, which is an API that comes with Rails. ActiveSupport (its "dependencies" handling) interferes with the classloading process, and the error is, I think, coming from some erroneous assumptions about how/when classes are being loaded. The ActiveSupport dependencies manager will automatically require files when you reference a constant that has never been seen. So, basically, it thinks we're telling it to load something that it says is already loaded. But, really, that's not our intent: We just want the value of the constant. To fix this, we would probably need to reorganize the code, which would be nice for ActiveSupport, but probably bad pedagogically.

    This URL is not about the specific problem, but it will give you some of the spirit of what is going wrong: http://dev.rubyonrails.org/ticket/6272

  10. October 23rd, 2009 at 19:02 | #10

    @Ron Newman

    I'm looking more closely at this, and while I can remove the ActiveSupport error ("World is not missing constant") there is still something funny about the other error ('wrong constant name'). I would assume it is something having to do with the way the scopes are introduced. As time permits, I'll try to figure it out. I suspect that distributing all of the classes into separate files would fix it, but I don't have time to do that, and I wouldn't recommend that you do it, either.

  11. Ron Newman
    October 23rd, 2009 at 19:18 | #11

    I think "wrong constant name" just means "I don't like to see a colon in the argument to this function":

    irb(main):002:0> Object.const_get("Foo.Bar")
    NameError: wrong constant name Foo.Bar
    	from (irb):2:in `const_get'
    	from (irb):2
    	from /Users/ronnewman/.ruby_versions/ruby-1.9.1-p243/bin/irb:12:in `<main>'
    
  12. Ron Newman
    October 23rd, 2009 at 19:19 | #12

    the same error above happens for "Foo.Bar" or "Foo:Bar" or "Foo::Bar" or "Foo/Bar" or "Foo-Bar" or "Foo,Bar" ...

  13. Ben Ho
    October 23rd, 2009 at 21:25 | #13

    @Ron Newman
    I am also experiencing the same problems Ron is getting with validates_presence_of() ignoring :allow_blank=>true. Has this been resolved?

    Also, is there a way of grouping attributes and options into one "validates_presence_of" call?

    So for example, instead of implementing something like this...

    validates_presence_of :firstname, :allow_blank = > true
    validates_presence_of :middlename, :allow_blank = > true
    validates_presence_of :lastname, :allow_blank = > true

    and repeating "validates_presence_of," can you somehow declare it like this?

    validates_presence_of :firstname, :middlename, :lastname (then somehow insert options?)

    Thanks.

    -Ben

  14. Ron Newman
    October 23rd, 2009 at 23:15 | #14

    Yes, you can declare any number of attributes in one statement, followed by the options at the end.

  15. October 24th, 2009 at 03:56 | #15

    @Ben Ho

    I don't know about using :allow_blank => true. For what it's worth, the reference implementation doesn't do it that way.

  16. October 24th, 2009 at 03:57 | #16

    @Ron Newman

    Ah, that could be it. The documentation sure makes it sound as though one should be able to shove any constant in there.

  17. Ron Newman
    October 24th, 2009 at 12:07 | #17

    I think :allow_blank and :allow_nil on validates_presence_of() is an error in the AWDR book (PDF pages 404-405). The actual documentation for this method, such as at http://api.rubyonrails.org/classes/ActiveRecord/Validations/ClassMethods.html#M002164 , does not mention :allow_blank or :allow_nil .

  18. Ben Ho
    October 24th, 2009 at 12:31 | #18

    @john

    OK, to put it in another way, is it reasonable to use validate_length_of for "appearance" and "identity" and checking for length (which in return would check if NIL is present); thus not needing to use validates_presence_of?

    @Ron Newman

    Very interesting - if that's the case, then I would have spent all this time debugging for not!

  19. peter
    October 24th, 2009 at 13:59 | #19

    @Ben Ho
    That's how I interpreted the instructions above. John talks about not validating with presence_of because technically an identity or appearance could be a " " ( space ). So I did just what you did and validated with the length.

    -peter

  20. Mike Bond
    October 24th, 2009 at 14:56 | #20

    As we know, the saved game's name must be unique and can serve as a useful unique identifier for which to update the game's state. We don't throw away the game every time we re-save it, but we presumably update its state instead.

    The following question relates to my decision whether to just delete all existing pylons and players when saving a game's state and starting anew, or trying to find individual pylons and players in preparation for saving a game.

    Officially, the id field in both the pylons and players tables is the primary key. However, it has no real meaning as far as identifying individual entities on the map, so is there another reliable unique identifier that we should use to identify and update the other attributes for the pylons and players? For example, should we be finding by "identity" and updating the entity's other attributes based on finding the specified entity in the DB, while adding other new entities that did not exist in the previous save? Or can we just do a clean sweep of entities from the DB and re-save them as new entities? For argument's sake, I should point out that I cloned players in Assn2 and they were able to share the same identity. As you can tell, I am hoping to delete entities where saved_game_id = x and repopulate them every time, but I wanted to check whether that approach would trip some alarm as far as our assignment rules and best practices.

  21. kasula
    October 24th, 2009 at 15:02 | #21

    In lecture6 (page 46) it says "AR in a Rails app: Do rake -T and notice there are other tasks such as rake db:migrate:redo", but I don't see any of those options. Even if I do db:migrate with assignment 3 the tables don't show up in the database. What am I missing here?

  22. October 24th, 2009 at 15:21 | #22

    @Mike Bond

    Delete the existing game before saving.

    Also, your cloned players are out-of-scope. This is akin to wanting to be ble to save players and pylons with additional attributes -- In the Q/A, we say that such players and pylons aren't supported, and you're not expected to be able to save them. Focus on the players and pylons that were supplied with the project.

    Regarding finding by "identity": Did you read in the Q/A section the following, which is on a related issue?

    "Q: It looks like there’s an opportunity to “share” Pylons and Players across games if they have exactly the same attributes. Should we check if a Pylon or Player is already in the database?

    A: No. The foreign key “saved_game_id” can only point to one game. To do what you’re talking about would require a “join table” in the middle, so as to support has_many :through."

    OK?

  23. October 24th, 2009 at 15:24 | #23

    @kasula

    Assignment 3 is not a Rails app! Right? It's just using the ActiveRecord library by itself.

    The only command we've provided in the Rakefile is . . .

    rake db:migrate

    If your database is up-to-date, this command does nothing. You would need to migration your database down to its initial state:

    rake db:migrate VERSION=0

    Now you can bring it back up:

    rake db:migrate

    Also, it is possible that your "down" migrations are broken or incomplete. If so, you can always delete your development database:

    rm db/development.sqlite3

    Then migrate the database again:

    rake db:migrate

  24. Mike Bond
    October 24th, 2009 at 15:27 | #24

    Thanks, John. I saw that Q/A but got thrown off by the context of the question. I now see that the answer still applies though.

  25. kasula
    October 24th, 2009 at 15:30 | #25

    @kasula
    I am able to see the tables now but I am not able to use db:rollback or db:migrate:redo.

  26. October 24th, 2009 at 15:38 | #26

    @kasula

    KASULA!! READ CAREFULLY!!

    (sorry about the "shouting," but I'm just repeating what I said above.)

    This is NOT A RAILS APPLICATION!!

    THEREFORE, you don't get all of the rake db tasks! OK? No rake db:rollback, no db:migrate:redo -- only db:migrate

    This is a standalone ActiveRecord application, so that you can learn the ActiveRecord basic/baby steps without being drowned in all of the detail/complexity of Rails.

    So . . . Want to get rid of your database? Simply remove it -- delete the file db/development.sqlite3

    OK?

    If you want to migrate your database to a particular migration . . .

    rake db:migrate VERSION=4

    If you want to migrate it all the way down . . .

    rake db:migrate VERSION=0

    If you want to migrate it all the way up . . .

    rake db:migrate

    John

  27. kasula
    October 24th, 2009 at 15:58 | #27

    Sorry John. I haven't seen your previous reply. I haven't refreshed the browser before posting mine. I saw your reply to Mike and I wanted to correct my question. My bad.

  28. October 24th, 2009 at 15:59 | #28

    @kasula

    Ok -- forgive me for the "shouting"!

  29. Ron Newman
    October 24th, 2009 at 17:51 | #29

    Implementing the "deleting a game deletes all saved players and pylons" feature should require only two lines of Ruby code. (Really, only [b]parts[/b] of two lines.)

  30. Lateral Punk
    October 24th, 2009 at 20:30 | #30

    I'm trying to do the references check by providing it as an option to t.integer :saved_game_id inside of say CreateSavedPlayers. I don't want to paste the actual code here, but it's similar to how it's done in AWDR using CONSTRAINT & REFERENCES. The only problem is that I'm not seeing the appropriate results in the SQLite creation logs:

    CREATE TABLE "saved_players" ("id" INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL, "saved_game_id" integer NOT NULL, "class_name" varchar(255)

    I read that it's cause SQLite doesn't support foreing-key constraints. If so, were you only asking us to do the "references" as an exercise?

  31. Lateral Punk
    October 24th, 2009 at 20:36 | #31

    my bad.

    I was doing t.integer :saved_game_id :options....
    instead of
    t.references...

    I assume we don't ahve to worry about foreign key check via execute right?

  32. Lateral Punk
    October 24th, 2009 at 21:21 | #32

    do I need both:
    validates_presence_of :someattr
    validates_uniqueness_of :someattr

    or is it enough to only specify the more *higher* priority of the 2 e.g.
    is it enough just to do
    validates_uniqueness_of :someattr

    I rather be safe and do both of them, but then is that DRY?

  33. Lateral Punk
    October 24th, 2009 at 21:32 | #33

    would the end result of:
    validates_numericality_of :someattr, :only_integer => true
    validates_numericality_of :someattr, :greater_than_or_equal_to => 5

    be that :someattr has to be both an integer && it has to be greater than or equal to 5?

    that is, is validation cumulative. Kind of goes to my previous question.

  34. Ron Newman
    October 24th, 2009 at 21:58 | #34

    ActiveRecord doesn't create foreign key constraints in the database, period. I consider this a serious MIS-feature, but that's how it works.

  35. Ron Newman
    October 24th, 2009 at 22:00 | #35

    You should be able to put both of those attributes on a single validates_numericality_of statement, rather than using two separate ones.

  36. October 24th, 2009 at 22:54 | #36

    @Lateral Punk

    Regarding t.references vs. t.integer :some_table_id

    It's up to you. Both create a column that is used for joins.

  37. October 24th, 2009 at 22:59 | #37

    @Ron Newman

    Ron: You can create all of the foreign key constraints you want: You just have to do it yourself. Since foreign keys are unevenly implemented across databases, AR leaves it to the developer. If you use a good database like Postgresql, you can even get those constraints created for you: http://agilewebdevelopment.com/plugins/foreign_key_migrations . . . http://mediumexposure.com/techblog/sexy-pg-constraints

    It's a hard problem. The ORMs that try to do it drive people crazy.

  38. October 24th, 2009 at 23:00 | #38

    @Lateral Punk

    This question:

    do I need both:
    validates_presence_of :someattr
    validates_uniqueness_of :someattr

    Try it out.

  39. October 24th, 2009 at 23:01 | #39

    @Lateral Punk

    This:

    would the end result of:
    validates_numericality_of :someattr, :only_integer => true
    validates_numericality_of :someattr, :greater_than_or_equal_to => 5

    The validations run one by one, in turn; so: cumulative. They will all run, too, which can be annoying to some end users.

  40. Lateral Punk
    October 24th, 2009 at 23:02 | #40

    How can I test if my Active Record object violates validates_uniqueness_of?

    I can't find anything from Figure 14-1 in AWDR that would do the trick and errors.invalid? definitely doesn't work.

    so how can i test it?

  41. October 24th, 2009 at 23:08 | #41

    @Lateral Punk

    Let's say that you had a User model, and you said

    validates_uniqueness_of :first_name

    Try and create two users with the same first name . . . is that what you mean?

    For instance,

    User.create!(:first_name => 'John', :last_name => 'Norman')
    User.create!(:first_name => 'John', :last_name => 'Smith')

    The 2nd would fail.

    After an attempt has been made to create a row, you can check the errors method.

    E.g.,

    u1 = User.create(:first_name => 'John', :last_name => 'Norman')
    u2 = User.create(:first_name => 'John', :last_name => 'Smith')
    p u2.errors

  42. Ron Newman
    October 24th, 2009 at 23:27 | #42

    re foreign key constraints: I don't see why I need PostgreSQL for this, rather than ordinary MySQL (with InnoDB tables).

  43. October 25th, 2009 at 00:58 | #43

    @Ron Newman

    There may well be a similar gem for MySQL. But the very fact that such gems are commonly database-specific (or, when not db-specific, have proved to be hard to maintain) should suggest why AR doesn't do it itself.

  44. Lateral Punk
    October 25th, 2009 at 11:42 | #44

    @john
    yes that's what I meant. I did't realize that you have to *actually* save the row for such a validation to be tested.

  45. Lateral Punk
    October 25th, 2009 at 11:57 | #45

    john :
    @Lateral Punk
    This question:
    do I need both:
    validates_presence_of :someattr
    validates_uniqueness_of :someattr
    Try it out.

    I tried it out with just the validates_uniqueness_of :someattr and I get this when trying to create a record that doesn't have :someattr:

    "ActiveRecord::StatementInvalid: SQLite3::SQLException: saved_games.someattr may not be NULL: INSERT INTO "saved_games""

    which seems to be the correct behaviour. So I just want to make sense of this. The case is that I'm trying to insert NULL for :someattr which is not unique, and hence the error right?

  46. kasula
    October 25th, 2009 at 12:16 | #46

    For all RoRers, I find this link useful for ruby, rails and rspec api docs:
    http://apidock.com/

    Couple of things I really like about it are: 1. You can view documentation in different versions without going back and forth. 2. There are some user comments and examples too.

    Just thought of sharing this piece with everybody. :)

  47. Lateral Punk
    October 25th, 2009 at 12:21 | #47

    Lateral Punk :

    john :
    @Lateral Punk
    This question:
    do I need both:
    validates_presence_of :someattr
    validates_uniqueness_of :someattr
    Try it out.

    I tried it out with just the validates_uniqueness_of :someattr and I get this when trying to create a record that doesn't have :someattr:
    "ActiveRecord::StatementInvalid: SQLite3::SQLException: saved_games.someattr may not be NULL: INSERT INTO "saved_games""
    which seems to be the correct behaviour. So I just want to make sense of this. The case is that I'm trying to insert NULL for :someattr which is not unique, and hence the error right?

    More research into this, and it seems that it's probably best to include both presence and uniqueness. If you only include the latter, than if you forget to not allow NULL in the migration, then the Model is valid, and inserted into the table. So it's probably a good idea to ensure that you have presence since it reminds you of the fact that the attribute shouldn't be NULL.

  48. Lateral Punk
    October 25th, 2009 at 12:48 | #48

    john :
    Note that your test database may be, between runs, junked up with random objects you've created during tests; you really want the same environment for each test. So you may have to do something creative in your setup/teardown methods in your tests (see the docs for Test::Unit).

    I know this assignment isn't about testing, but man I'm really enjoying testing now :) Anyways, could you please elaborate on how to do the setup/teardown for recreating the database all the time? Currently this is my workflow:
    PROJECT_ENV='test' rake db:migrate VERSION=0
    PROJECT_ENV='test' rake db:migrate
    PROJECT_ENV='test' ruby test/saved_game_test.rb

    between each test! it gets repetitive and erroneous (as one of the posters previously noted).

    How do I all those rake commands from within a rb file? Should I launch an external process?

  49. Lateral Punk
    October 25th, 2009 at 12:55 | #49

    I guess a small shell script will suffice for now:

    #!/bin/bash
    PROJECT_ENV='test' rake db:migrate VERSION=0
    PROJECT_ENV='test' rake db:migrate
    PROJECT_ENV='test' rake test

  50. October 25th, 2009 at 13:01 | #50

    @Lateral Punk

    It seems that you've figured out answers to your questions -- anything you still want me to intervene on?

    The management of the test database is handled for you in Rails.

Comment pages
  1. No trackbacks yet.