The stories page

Before starting on the story creation code, i went back and update the domain model with the proper variable names:

domain-model.jpg

Today i’m going to be focusing on just the story, so lets update the models and the database schema: We need a model for a Story, Movie and Picture.

Added the models at the command line, now lets set the relationships in the model definitions:

Article
has_many :stories

User
has_many :stories

Story
belongs_to :article
belongs_to :user
has_many :movies
has_many :pictures

Movie
belongs_to :story

Picture
belongs_to :story

Next, migrate the required tables into the database: My migration looked like this:

class CreateStories < ActiveRecord::Migration
def self.up
create_table :stories do |t|
t.column :title, :string
t.column :summary, :string
t.column :tags, :string
t.column :body, :text
t.column :added_at, :datetime
t.column :points, :integer
t.column :article_id, :integer
t.column :user_id, :integer
end
end

def self.down
drop_table :stories
end
end

class CreateMovies < ActiveRecord::Migration
def self.up
create_table :movies do |t|
t.column :title, :string
t.column :summary, :string
t.column :tags, :string
t.column :content_type, :string
t.column :filename, :string
t.column :binary_data, :binary
t.column :story_id, :integer
t.column :user_id, :integer
end
end

def self.down
drop_table :movies
end
end

class CreatePictures < ActiveRecord::Migration
def self.up
create_table :pictures do |t|
t.column :title, :string
t.column :summary, :string
t.column :tags, :string
t.column :content_type, :string
t.column :filename, :string
t.column :binary_data, :binary
t.column :story_id, :integer
t.column :user_id, :integer
end
end

def self.down
drop_table :pictures
end
end

Then, a simple command line call
rake db:migrate

And that’s it – it’s all in there! Fantastic.

While writing these, i decided to make a couple of changes to the movie and picture schemas – they no longer have points: only the Story has points, and they go with a story, so them havng points as well seemed, well, a bit pointless. They do store the id of the user who added them though. So, the story model looks like this:

story_model.jpg

A story will be edited as a big text field, with the ability to insert movies or pictures at the current cursor location. These will be shown either in thumbnail or full size on the page: in either case, if the viewer clicks on them they will be taken to a seperate view page.

I’m not sure how the best way to do the text is: if i do it as html then i can insert tags for pictures and movies pretty easily, and also let the user copy their own html in. On the other hand, it would be simpler to just store text, movies and pictures.

A nice and simple way to do it might be to have a pictures section, a text section, and a movies section, seperate on the page, so that each is just a list of that story’s items. Same paradigm as we have with the show page really. I’m going to do that i think.

So, my current controller plan for stories looks like this:

add_story-controller-flow.jpg

so, let’s design the “edit_story” html first. I’m going to use dreamweaver to create the template, then replace components with ruby code as we go along.

OK – -the html template looks like this. It’s basically a ton of nested tables, which is kind of horrible. Maybe that’s just how websites are made. Anyway:

 

Pictures Add picture button

Picture list

item

item

Movies Add movie button

Movie list

item

item

Picture/movie viewer
 

Ok, that’s in now. Realised two things:
a) i forgot a submit button,
b) the form looks REALLY CRAPPY: the border style is horrible, the layout is all over the place, and it’s all too wide.

Second attempt (copied from rails this time):

Note: I did have a table here but it was breaking wordpress! Half of my blog post was appearing outside the editable iframe thing. So, i deleted the table. Hopefully that will fix it.

Still looks crap but it’s a bit better and at least all the components are there. Need to redo it to look nicer at some point. Ask aaron maybe?

OK – so, i’ve got that in and working with just stubs for all the button actions, and not much in the ‘edit_story’ action either.

The question i’m struggling with now is how to keep the story components while we jump around various screens and actions. I think the simplest way might be to make ‘edit_story’, ‘add_picture’ and ‘add_movie’ all take a @story parameter, and then make sure we pass over the current story when we go to one of them. That means that ‘edit_story’ becomes a singleton method – if it doesn’t get a story out of params then it makes a new one. If it does it uses that. ‘add_picture, ‘add_movie’ and ‘submit’ should all break if they don’t get a story. Another alternative is to put the story into the session hash, but this implies that a session can only have one story at a time, which might be true but i don’t want to get caught out by that later. I’ll try the first option first.

Made these two actions:

def edit_story
#check if we’re given one first
if params[:story]
@story = params[:story]
else
@story = Story.new
@article = Article.find(:first, :conditions => [“id = ?”, params[:article_id] ])
@story.article_id = @article.id
end
end

def create_story
@story = Article.find(params[:article_id]).stories.create(params[:story])
#@comment = Comment.new(params[:comment])
@story.added_at = DateTime.now.to_s
@story.user_id = session[:user].id
if @story.save
flash[:notice] = “Added your story.”
else
flash[:notice] = “Sorry, your story was not able to be saved.”
end
redirect_to :action => “show”, :id => params[:article_id]
end

Edit story sets up a new story, and create puts in a few automated details and saves them to the db. I checked, and nothing is going into the db till the submit button is pressed, so that’s good.

It seems to work – we have one story in the database anyway. Realised that the show page isn’t actually listing the article’s stories in the “Stories” section. Easy to fix, I’ll just copy the links partial:)

Ok, that’s done. Doing that reminded me that we’re going to need a ‘view story’ page as well. Think i’ll do that next, then worry about the multi-page form business. I’m going to do a basic one that doesn’t worry about showing pictures and movies for the moment, just to get the structure up and running.

Ok, that’s in. It’s having problems with displaying the story’s points. That’s because points_to_s is a member method of Article. I should think of a DRY way of sharing the method between all classes….hmm. What about if i make a new class called newspipe_component which extends ActiveRecord, and then extend that for all my other classes? Then i can put methods in there. Seems drastic though.

Wait – what about ruby mixins? They should work.

OK. Mixins work with modules, so i’ve created a module: a file in libs/ called model_extensions.rb. In it is

module ModelExtensions
public
def points_to_s
if self.points == nil
return “ERROR: Can’t find points”
else
ret_string = “#{self.points} point”
ret_string += “s” unless self.points == 1
return ret_string
end
end

end

This method should be called in the same way as it is already, eg:
<span><%= @article.points_to_s %></span>

But the difference is that i should be able to say @story.points_to_s as well, or anything else that has a ‘points’ field.Next step is to actually mix it in: for this, i had to look on the net, and found this handy example. The author suggests mixing it into the base ActiveRecord class, which makes sense since the other model classes are extended from this. The way we do this is to do it via config/environment.rb, which has a load of config options in it. I added this line to the end:

ActiveRecord::Base.send(:extend, ModelExtensions)

Here goes: i’ve deleted the method out of article to test whether it still works….load the show page…

undefined method `points_to_s' for #<Article:0x466683c>

Hmm, guess not. Let’s try restarting the web server in case that’s required to get the environment settings.

Nope. Still broken. ah wait – ths line was meant to go in environment.rb as well, before the other:

require ‘model_extensions’

Added that. Nothing. Restarted web server – aha! the web server’s crashed trying to read environment.rb. So i guess we do need to restart the server to pick up environment config changes. Seems reasonable. Actually, on closer inspection it specificaly states at the top of environment.rb to restart the web server after changing it :P)

Got this sorted now – the first problem (that was causing it to crash) was the use of ’ instead of ‘ or “. This is a bugbear for me with word processing packages. Word especially is terrible – it will replace instances of ” with instances of so-called (anything but!) “smart quotes” like these: ˝. It’s a different character! Word should not do this! Makes me crazy. Anyway, the second problem was a bit more meaningful and subtle: the use of “:extend” in this line –

ActiveRecord::Base.send(:extend, ModelExtensions)

means that the Class took in the methods of ModelExtensions and made them class methods. Apparently if you want them to be instance methods (which i do as i call them on “self”), you use :include instead. Did that and it worked. Hoorah! And on that high note i’ll leave it for this post.

Advertisements

Leave a Reply

Fill in your details below or click an icon to log in:

WordPress.com Logo

You are commenting using your WordPress.com account. Log Out / Change )

Twitter picture

You are commenting using your Twitter account. Log Out / Change )

Facebook photo

You are commenting using your Facebook account. Log Out / Change )

Google+ photo

You are commenting using your Google+ account. Log Out / Change )

Connecting to %s