2

I'm developing an e-learning in Rails and I want to save a set of Arrays to the database, with the aim of tracking a user's progress through the various sections of the e-learning.

I've come across this question and answer:
Storing arrays in database : JSON vs. serialized array
...which sounds like it might be useful, but I can't figure out how to integrate it into the Rails project I'm developing.

Given that I'm pretty much a Rails noob, could someone explain to me, in plain English (or alternatively plain code) how I would:
a) save to a database an Array which holds a sequence of 'false' Booleans when a user initially signs up.
b) retrieve and update that Array from various pages throughout the e-learning.

I would set out what I have tried, but it's just been wild stabs in the dark and I don't really know where to start. Should the Comment class be saved in the controllers folder? Or is it, in my example, actually the User class (which, similarly, extends ActiveRecord)?

Once again, any help (ideally explaining where the code belongs) much appreciated.

UPDATE

I've been asked to be more specific, so I will try:
I have a user table in the database, which (predictably) has several fields, including email, username, etc. I'd like to add 5 more fields to the user table, each storing an Array to track the user's progress through each of the 5 sections. I'm envisaging, when a user opens a piece of content, the Array would be updated and the appropriate index would be updated to 'true' to flag that THAT part of the section is complete.

I hope that helps...

RE-UPDATE

When a user first signs up I want the Arrays to be set with a sequence of 'false' Booleans. From which file would I set the Arrays? Is that done in User? At the moment I'm trying this, but I don't think the values are being set - although it's not throwing an error.

class User < ActiveRecord::Base
  # Include default devise modules. Others available are:
  # :confirmable, :lockable, :timeoutable and :omniauthable
  serialize :leadership_styles_progress
  serialize :skills_progress
  serialize :safeguarding_progress
  serialize :tools_progress

  before_create :set_progress_vars

  devise :database_authenticatable, :registerable,
         :recoverable, :rememberable, :trackable, :validatable
  validates :age, presence: { :message => ": Please choose your age" }
  validates :section, presence: { :message => ": Please choose the section you belong to"}
  has_many :posts

  private

  def set_progress_vars
    self.leadership_styles_progress = [false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false]
    self.skills_progress = [false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false]
    self.safeguarding_progress = [false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false]
    self.tools_progress = [false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false]
  end

end

I have also checked which routes are mapped where, via 'rake routes'; users/sign_up is mapped to devise/registrations/new. So does the serialize code, suggested below, go there?

Community
  • 1
  • 1
moosefetcher
  • 1,841
  • 2
  • 23
  • 39
  • Can you try and explain a bit clearer? A user has a course which has many pages and you want to store the pages they have visited? Ideally some form of simple db structure with only the relevant tables would be useful – j-dexx Sep 18 '14 at 15:16
  • Do you (or will you ever) need to query this array? If so, then `serialize` is your enemy and you'd be better off using separate columns or a separate table for your list. – mu is too short Sep 18 '14 at 15:37
  • When a user signs in (as opposed to signs UP) I will need to deserialize the Array and load it into a local variable (I assume). When they view new content, I will want to update the Array on the database. Is 'serialize' OK for that? – moosefetcher Sep 19 '14 at 13:47

1 Answers1

1

You mentioned the Comment and User class extend ActiveRecord. These are models and they go in the app/models folder.

Saving arrays to the database is very straightforward as explained in the answer you linked to. All you have to do to integrate it into your project is declare which attribute you want to store Arrays in and they will be automagically serialized upon saving or updating and deserialized when you want to read them back.

You should definitely post any code you have already. I'll assume the attribute you want to serialize is in the Comment model and it's called choices (but of course it can be anything) and it is a text field (not a string field as this might be too short if your arrays grow more than 255 characters when serialized):

# app/models/comment.rb
class Comment < ActiveRecord::Base
  serialize :choices
end

That's it! Now when ever you assign a value to that attribute @comment.choices = [false, false] it will be automatically serialized as YAML when you save and then it'll be deserialized when you read it:

class CommentController < ApplicationController
  def create
    @comment = Comment.new choices: [false, false, true, 'stuff', 'whatever']
  end

  def show
    @comment = Comment.find params[:id]
    @comment.choices # returns [false, false, true, 'stuff', 'whatever']
  end
end

To explain what's going on "under the hood" when you save the record with an array value assigned to the choices field ActiveRecord knows that you want to serialize it since you declared serialize :choices in your Comment class. It will take that array value and serialize it like this:

YAML.dump(choices)

this will convert the array into this string:

"---\n- false\n- false\n- true\n- stuff\n- whatever\n"

That is the YAML representation (serialization) of the array. To read it back in (when you call comment.choices) ActiveRecord will take the serialized string and deserialize it:

YAML.load(choices)

That will then convert the YAML string back into a Ruby array.

To summarize:

  • Add a text field to your model e.g. choices:text when you create the migration.
  • Declare serialize :choices in your model.
  • Use as normal.

Hope that helps.

DiegoSalazar
  • 13,361
  • 2
  • 38
  • 55
  • So if I have a table of users - and a User model - should the 'serialize :choices' go in the User model? I appreciate the answer, but I still don't know where to put the code. How do I ensure the Array is saved to the correct User (current_user) if there's no reference to the user? – moosefetcher Sep 19 '14 at 07:47
  • I also still don't quite follow why :choices is defined as a symbol but then referred to as a method. Also why is it initially preceded by, and thereafter followed by, a colon? Is there a sensible explanation for that, or is it just one of Ruby's quirks? Sorry. I'm probably getting a bit frustrated. – moosefetcher Sep 19 '14 at 12:46
  • I can't really answer some of you questions since they're up to you to decide. `choices` in the User model? I don't know, is that how you defined your schema? Where do you want to save that data? It's up to you. For example, say you did add a field to the User table called `choices`, you declare that you want to serialize that field with `serialize :choices` within your User model. You refer to that field with a symbol because its the name of the field but it's also an attribute on the User model (because its a field in the User table) – DiegoSalazar Sep 19 '14 at 14:40
  • ...and therefore it will be gettable and settable via a method of the same name on user instances: `user = User.new; user.choices = ['stuff']; user.choices # returns ['stuff']`. In the part where it's followed by a colon: `Comment.new choices: []` This is just Ruby's new hash notation, it's equivalent to: `Comment.new :choices => []`. So it's just a hash of attributes being passed into the model to mass assign them to the instance you are `new`ing. – DiegoSalazar Sep 19 '14 at 14:42
  • I want to save 4 Arrays, per user, in the user table, so each User has 4 Arrays. As indicated in my code above, I have added the serialize :leadership_styles_progress... and so on, in the User but they don't appear to be accessible via that method name, ie: current_user.leadership_stlyes_progress. Any idea why? Also WHERE would I send those initial values from? As in, which bit of code? Thanks. – moosefetcher Sep 19 '14 at 15:16
  • Just to be clear: I can't find where a User is 'instantiated'. If I knew that, I'd know where to add the Array-initializing code. I'm using Devise; I don't know if that means the User-creation is hidden somewhere out of 'public' view. If you DO know, I'd be interested to hear. Thanks again. – moosefetcher Sep 22 '14 at 08:44
  • OK! I figured it out. I still had the fields in the database defined as Arrays! I changed them to strings, and it works. – moosefetcher Sep 22 '14 at 09:52