Rails: Models design for a questionnaire application - ruby-on-rails

I am very new to rails. I am currently working on this questionnaire application that open once every few months. Responses given by the users will be saved in the database for a few survey rounds and then be archived and deleted afterwards.
Administrators are able to view the responses, and modify the existing list of survey questions for the next round of survey. Administrators have a fixed user account under users table as I am currently using devise for authentication.
The part that I can't wrap my head around is how do I make it such that modifying questions can be possible without affecting the existing responses of past questionnaire rounds. I have thought of duplicating the questions but I'm not too sure on how to implement it due to my lack of my understanding of rails.
This is the current model I have came up with:
class Questionnaire < ApplicationRecord
has_many :responses
end
class Response < ApplicationRecord
belongs_to :questionnaires
belongs_to :users
belongs_to :questions
end
class Question < ApplicationRecord
has_many :responses
end
class Users < ApplicationRecord
has_many :responses
end

#zhenbin I would suggest you to use something as act_as_versioned gem which keeps the version with every change in questions . So when ever new user gives survey latest question is pulled but when survey responses are pulled its pulled with its corresponding question

Why do you want to modify questions? If you make the next round of surveys, just create new ones.
Furthermore, in your model you can't write belongs_to with pluralized names as you will get an error, refer to http://guides.rubyonrails.org/association_basics.html#the-belongs-to-association for more details.

I would suggest exporting the questions and answers from the database after each survey round, that way you can always modify questions at a later date without having to worry about maintaining older question/answer relationships.
It could (for example) be exported to a database dump file, or a CSV file (marketing departments love those). Exporting to a file in ruby isn't very complicated, and I think you'll be able to find many resources for doing so. For a CSV export, for instance, check out the CSV class.
Good luck!

Related

Rails Nested Form and dependent relationships

I currently have a few models, users, clients, addresses and contacts.
I used scaffold to create my client model which only contains name, leadsource and DBA. It is currently working just fine.
My address and contact was created separately as a client can have many addresses and contacts and they require a client_id to set up the relationship.
Assuming I am using your standard crud operations, is there any way to set up a single nested form that will allow me to add all 3 at once?
Sure, the standard way is fields_for.
But normally you also want to dynamically add and remove associated objects (i.e. addresses) and that's a bit more advanced.
There are gems to help you. i.e. cocoon and a great railscast: episode 403.
Unfortunately it's a pro episode, but investing the $9 is well worth it, as the railscasts really are a source of inspiration (at least for me).
I assume, you already have the associations or create them:
rails generate migration AddClientReferenceToAddress client:references
rake db:migrate
and in models:
class Client < ActiveRecord::Base
has_many :addresses
end
class Address < ActiveRecord::Base
belongs_to :client
end

How do I associate a Clone to it's Parent in Rails?

I have a model of say Apps and each App contains a bunch of users, and a reference to the store type, and has the properties, name, app_id, version, and price.
I want to be able to create clones of this app, which has all the same values, save for version, and store type.
I'd like to do this by adding a property/column to my app called "Is_clone", and I'd like a second table called AppToClone, which has two columns App and Clone.
In each row, I'll have say App:1 , Clone:[2,5,7,26]
This way I can edit each clone as its own app, but at the same time I can apply any changes in an app to all its clones.
I've set up the database for AppToClone but I'm stuck on how to create this association within the App Model itself. Examples I've found so far such as the gem rails_lookup_tables seems to only allow a single value rather than an array in the AppToClone table.
Currently in my App model I have the following line:
:has_many :apps, through: :appstoclones
But this doesn't feel correct to me, and I'm not sure how to build that database over time.
Feeling kinda stupid here, but I'm stuck.
So the has_many and belongs_to association stores the data in a different way than the question indicates. I would use single table inheritance, having a Clone model inherit from the App model.
class App < ActiveRecord::Base
has_many :clones
end
class Clone < App
belongs_to :app
end
You would need to have an app_id and type on your app table (strange I know), so your clone can be associated. What you would be able to do then is query your clone as an App.
Clone.last.is_a? App
-> true
What I think is closest to what you actually want though, is outlined in section 2.10 Self Joins of the Ruby on Rails Guides.
class App < ActiveRecord::Base
has_many :clones, class_name: "App",
foreign_key: "parent_app_id"
belongs_to :parent_app, class_name: "App"
end
You need to create the foreign_key in a migration.
To implement the is_clone:
def is_clone?
!!self.parent_app_id
end
To implement the AppToClone:
def clone_ids
self.clones.all.map(&id)
end
# Check it in the console:
App.last.clone_ids
-> [2,5,7,26]
The biggest concern here is thinking that a table will have a row with one column holding an array. That is not normalized! Normal associations work by having the foreign keys as seen in these examples, one per row. If you have a complex has_and_belongs_to_many relationship, the table that holds keys has two columns, each with one id.
Per the request in the comment below (self join update):
def clone_me
self.clones << self.clone
end
> new_cloned_app = App.last.clone_me
The reference for using has_many is found: In the rails guide here. Feel free to use a different helper function. Also, the returned value must be .saveed
I would say you are looking for a has-many belongs-to relationship.
For the has-many part: http://guides.rubyonrails.org/association_basics.html#the-has-many-association
For the belongs-to part: http://guides.rubyonrails.org/association_basics.html#the-belongs-to-association
One model would be the app, the other clone, where the clone table would store the the same fields an app has, as well as the additional fields needed for a clone instance. By inheriting the clone model from the app model which inherits from ActiveRecord, one can use a clone in the code where an app is used.

Rails: should I use STI?

I want to present my case, and know whether or not I should use STI solution.
I am creating a message-board website and so far I have couple of Models: User, Topic, Post..
to make it clear: Post is like a comment for a Topic. Topic has title and content. Post has only has content.
Now, the User has the option to Like/Dislike a Post or a Topic.
I thought about three options:
Topic and Post don't have a connection (each Model has "num_of_likes", "num_of_dislikes")
Topic inherit Post.
Topic and Post inherit from a Base Model which can be called LikeableObj for example.
Which of those three options is the most suitable for my needs?
Is there a fourth option I didn't think about?
What if I'd like in the future to have a third Model which can be Liked?
I assume you'll want to keep track of whether a user has liked a certain post or topic, so I would make a join model for likes that connects a user to either a post or topic.
class Like < ActiveRecord::Base
belongs_to :user
belongs_to :liked_obj, polymorphic:true, counter_cache:true
end
Since the liked_obj is polymorphic, it can be a post or a topic. You can then put has_many :likes on those models and a column likes_count, which will be updated automatically as a counter cache.
If you have any code that deals with likes that is common between Post and Topic, put it in a module Likeable and include it in both classes.

How should approach allowing users to create notes with revisions?

I'm working on a Rails project where I want to allow users to create individual notes, which are really just text fields at this time. With each note, the user can edit what they have previously written, but the old version is kept in a revision table. I'm trying to figure out the best way to approach this.
My initial thoughts are to have the following relationships:
class User < ActiveRecord::Base
has_many :notes
end
class Note < ActiveRecord::Base
has_many :note_revisions
belongs_to :user
end
class NoteRevision < ActiveRecord::Base
belongs_to :note_revision
end
The Note model will only contain a timestamp of when the note was first created. The NoteRevision model will contain the text, as well as a timestamp for each revision. This way, every time a new revision is made, a new entry is created into the NoteRevision table which is tracked through the Note table. Hopefully this makes sense!
First, does this look like a good way to do this? If so, I'm having trouble figuring out how the controller and view will present this information in one form. Are there any good tutorials or has someone seen anything similar that can point me in the right direction?
Thanks in advance!
I'd check out paper trail which will handle all the versioning for you and help you easily return the most current version. Behind the scenes it does something similar to what you're attempting to do but has been thoroughly tested and used by many people. It also benefits from active development.
Another option is the vestal_versions gem.
Railscasts.com has an episode on using it: http://railscasts.com/episodes/177-model-versioning

How should Rails models containing database and non-database datasources be broken up?

So I'm working on a Rails app to get the feeling for the whole thing. I've got a Product model that's a standard ActiveRecord model. However, I also want to get some additional product info from Amazon ECS. So my complete model gets some of its info from the database and some from the web service. My question is, should I:
Make two models a Product and a ProductAWS, and then tie them together at the controller level.
Have the Product ActiveRecord model contain a ProductAWS object that does all the AWS stuff?
Just add all the AWS functionality to my Product model.
???
As with most things: it depends. Each of your ideas have merit. If it were me, I'd start out this way:
class Product < ActiveRecord::Base
has_one :aws_item
end
class AWSItem
belongs_to :product
end
The key questions you want to ask yourself are:
Are you only going to be offering AWS ECS items, or will you have other products? If you'll have products that have nothing to do with Amazon, don't care about ASIN, etc, then a has_one could be the way to go. Or, even better, a polymorphic relationship to a :vendable interface so you can later plug in different extension types.
Is it just behavior that is different, or is the data going to be largely different too? Because you might want to consider:
class Product < ActiveRecord::Base
end
class AWSItem < Product
def do_amazon_stuff
...
end
end
How do you want the system to perform when Amazon ECS isn't available? Should it throw exceptions? Or should you rely on a local cached version of the catalog?
class Product < ActiveRecord::Base
end
class ItemFetcher < BackgrounDRb::Rails
def do_work
# .... Make a cached copy of your ECS catalog here.
# Copy the Amazon stuff into your local model
end
end
Walk through these questions slowly and the answer will become clearer. If it doesn't, start prototyping it out. Good luck!
You can use the composed_of relationship in ActiveRecord. You make a regular class with all the attributes that you manage through AWS and specify that your Product-class is composed_of this class. ActiveRecord will handle the delegation of the mapped attributes to and from this class.
See the documentation of composed_of
#Menno
What about using ActiveResource for the AWS-attributes class?
If you are retrieving data from two completely different sources (ActiveRecord on one hand and the Internet on the other), there are many benefits to keeping these as separate models. As the above poster wrote, Product has_one (or has_many) :aws_item.

Resources