I am currently working on a Ruby on Rails app which will function in some ways like a site-specific social networking site. As part of this, each user on the site will have a profile where they can fill in their contact information (phone numbers, addresses, email addresses, employer, etc.).
A simple solution to modeling this would be to have a database column per piece of information I allow users to enter. However, this seems arbitrary and limited. Further, to support allowing users to enter as many phone numbers as they would like requires the addition of another database table and joins.
It seems to me that a better solution would be to serialize all the contact information entered by a user into a single field in their row. Since I will never be conditioning a SQL query on this information, such a solution wouldn't be any less efficient.
Ideally, I would like to use a vCard as my serialization format. vCards are the standard solution to storing contact information across the web, and reusing tested solutions is a Good Thing. Alternative serialization formats would include simply marshaling a ruby hash, or YAML. Regardless of serialization format, supporting the reading and updating of this information in a rails-like way seems to be a major implementation challenge.
So, here's the question: Has anyone seen this approach used in a rails application? Are there any rails plugins or gems that make such a system easy to implement?
Ideally what I would like is an acts_as_vcard to add to my model object that would handle editing the vcard for me and saving it back to the database.
vCard is good for an API, but for the actual database I would use a 1-many design. Each person can have many phone numbers, addresses, email addresses, past employers. For current employer, you could do a 1-1 relationship. I think your aversion to joins is misplaced. With proper indexes, performance should be fine. Implementation will be much simpler than if you are constantly serializing and deserializing a denormalized string representation. You won't have to reinvent the wheel as you're contemplating.
1) if you do want to go the serialization route rails has built in support for storing a hash
from http://api.rubyonrails.org/classes/ActiveRecord/Base.html
class User < ActiveRecord::Base
serialize :preferences
end
user = User.create(:preferences => { "background" => "black", "display" => large })
User.find(user.id).preferences # => { "background" => "black", "display" => large }
if you use this technique I would put the serialized field on its own model/table object, otherwise it will be included in every User find call, which is not ideal, maybe
class User < ActiveRecord::Base
has_one :contact_info
end
class ContactInfo < ActiveRecord::Base
has_many :users
serialize :data
end
# ...
user.contact_info.data[:phone_numbers] # => ['999 999-9999', '000 000-0000']
2) or if you want to go the noSql route rails has support for mongodb, you would basically embed the contact info into the User model/document
http://www.mongodb.org/
http://railstips.org/blog/archives/2009/06/27/mongomapper-the-rad-mongo-wrapper/
https://mongohq.com/home
3) or just go with the additional tables, it's not as bad as it seems, rails migrations can help a lot here with changing requirements
Related
I have designed a very simple web application which associates authors, books and ratings.
In each of the respective models
Author
has_many :books
Book
belongs_to :author
has_many :reviews
Review
belongs_to :book
Model attributes
Author : title, fname, lname, DOB
Book : ISBN, title, publish_date, pages
Review : rating(1-5), description
I am wondering if I completely validate all of these attributes to my liking in the models, 1 attribute for example
validates :ISBN, :only_integer => true, length: { is: 13 }
do I need to worry about validations for data elsewhere?
I know that validations for the model run on the server side so there may need to be some validation on the client side (in JS). I am trying to ensure that there are no flaws when it comes to asserting data correctness.
As is so often the case: it depends.
In a simple Rails application, all models will be updated through a view request to a controller which in turn fills in the params into the models, then tries to save the model and any validation errors that occur are rendered back to the server.
In that scenario, all your code will have to do is to react to failed calls to #save but you can sleep soundly knowing that everything in your database is how it is supposed to be.
In more complex applications, putting all the validation logic into your model might not work as well anymore: every call to #save will have to run through all the validation logic, slowing things down, and different parts of your application might have different requirements to the input parameters.
In that scenario there are many ways to go about it. Form objects with validations specific to the forms they represent are a very common solution. These form models then distribute their input among one or more underlying ActiveRecord models.
But the Rails way is to take these one step at a time and avoid premature optimization. For the foreseeable future, putting your validation into your model will be enough to guarantee consistency.
do I need to worry about validations for data elsewhere?
Yes you do.
Application level validations are still prone to race conditions.
For things that should be unique like for example ISBN numbers database constraints are vital if uniqueness is to be guarenteed. Other areas where this can cause issues are when you have a limit on the count of an association.
While validations prevent most errors they are not a replacement for database constraints to ensure the correctness of data. Both are needed if correctness is important.
Is there a way in Rails to manipulate database fields and corresponding accessor methods without the „nifty generators” ?
I want users, insofar they are privileged, to be able to manipulate the database structure, that is, at least, to add or delete columns. The privileged user should have the possibility to „Add new” some columns.
Say I have an Object/Table artist and it should “dynamically” receive columns as "date of birth", "has played with", "copies sold"
Not sure if it's a dup. It takes a preliminary decision whether Rails discourages from letting the user do this to begin with or or not. (if that's the case => certainly some noSQL solution)
In pure ruby at least it is easy to dynamically add an attribute to an existing Model/Class like this
Test.class_eval do; attr_accessor "new_attribute"; end
and
Test.new.new_attribute = 2
would return => 2 as expected
In order to create or manipulate a customized input mask / model: can I not manually go the same way the generators go and manually call ActiveRecord::Migration methods like add_column as well as create getter/setter-methods for ORM ?
If yes or no, in both cases, which are they to begin with?
Thanks!
I am not aware of any elegant way to allow an Object to dynamically create new columns. This would not be a good application design and would lead massive inefficiency in your database.
You can achieve a similar type of functionality you seek using ActiveRecord associations in Rails. Here's a simple example for your Artist model using a related Attributes table.
class Artist < ActiveRecord::Base
has_many :attributes
end
class Attribute < ActiveRecord::Base
belongs_to :artist
end
With this association, you can allow your Artist class to create/edit/destroy Attributes. ActiveRecord will use foreign keys in the database to keep track of the relationship between the two models.
If that doesn't work for you, your next best option is to look into NoSQL databases, such as MongoDB, which allow for much more flexibility in the schema. With NoSQL, you can facilitate the insertion of data without a predefined schema.
I am working at a web app in Rails that behaves much like a CMS: there are articles, which have a text attribute that contains links to other articles (or other object classes). I'm currently storing the attribute as HTML.
Is there any good way to model those links in a way that is relative easy to change, and contain the reference to the object id, instead of the absolute url?
One of the solutons I was thinking was to use some kind of special mark-up, such as:
[link_to "Text for the link", Article:12]
where 12 is the id of the article it links to. This mark-up will be parsed when the text is rendered.
The downside of this is that I have to hack into TinyMCE (the editor I'm thinking of using to edit the HTML) so that it can insert links to other objects, by accessing the database and automatically assigning the object type and ID (the person who's editing the texts doesn't know the id's).
Is there any simple solution to this?
Or should I stick to using absolute urls (which, besides maintenance issues, is annoying in development, as they will always point to production and that is confusing for me)?
Additionally, does anyone have similar examples in other languages (php, Wordpress, other CMS, etc) that tackle this problem in a nice way? I'm thinking, this is pretty vital in CMS, and can reduce a lot of man hours if a nice system can handle all those links.
EDIT:
Another possible solution that I'm thinking about is letting the person copy the link of the article directly in the code, but it should, upon submission, generate the correct association id and make it so that if the url structure changes, the link is always up-to-date. I'd like to hear your opinions and experience with this approach, if you have tried it.
The challenge with this approach is parsing the link with Rails and finding out that it points to an Article, and that article has the id ##. Then I have to insert a shortcode that will always translate, upon parsing and rendering, to an actual link to that article.
I found a method that could make this feasible:
Rails.application.routes.recognize_path
But there may be some caveats that I don't see right now...
EDIT no. 2
I also want to specify that I chose CKEditor as the content editor, but I will consider other ones if there are clearer advantages.
I have built something similar using a shortcode system which would allow me to call specific methods on the model and replace the shortcode in the text:
Helper
def parse_shortcode(model)
text = model.text
text.gsub(/(\[#!\s?\w+\])/i) do |match|
result = model.try(match)
result.nil? '' : link_to(result[:text], result[:url])
end
end
Model
def contact_link
{ :text => self.name, :url => self.url }
end
View
<%= parse_shortcode(#article) %>
I haven't tested the above code and it is obviously a bit simplified but it explains my thought process behind this.
EDIT: Just to clarify my above example uses an invented shortcode syntax of [#! method]
An article can have many related_articles and at the same time this article can be related by many other articles, so it is best to model this as a many-to-many relationship.
One way to define this type of relationship in Rails is has_many :through.
To use has_many :through you have to create a join model, perhaps call it ArticleRelation. This model will have two fields, an article_id that represents the current article and a related_article_id that represents the article who’s being referred as related.
class Article < AR::Base
has_many :article_relations
has_many :related_articles, :through => :article_relations
end
class ArticleRelation < AR::Base
belongs_to :article
belongs_to :article_relation, :class_name => 'Article'
end
When creating self-referential relationships it’s important to remember that you’re only creating one side of the relationship. Although article_1 might list article_2 as related, there is no way to for article_2 to list article_1 as related. You'd need two ArticleRelation records to create a mutual relationship.
It’s difficult to think up appropriate names to define another side of the relationship so you can prefix both with the word “inverse” to give inverse_article_relations and inverse_related_articles. You also need to specify some additional options to make the relationships work. For inverse_article_relations you’ll have to specify the name of the other model as it can’t be inferred from the relationship name and you’ll also have to define the foreign key as related_article_id. For the inverse_related_articles relationship you need to specify the source as articles, as again it cannot be inferred from the name of the relationship.
has_many :inverse_article_relations, :class_name => "ArticleRelation", :foreign_key => "related_article_id"
has_many :inverse_related_articles, :through => :inverse_article_relations, :source => :article
Test it out, this should work for you per current requirement.
A solution I've seen in a lot of other CMSs is a combination of custom file browser in TinyMCE and page rewriting (similar to freakyDaz's answer).
TinyMCE has documentation and example code for implementing a custom browser. You'll have to provide the backend pieces, of course.
CKEditor has documentation for a similar feature as well.
Have your backend implementation return something that's easy to parse for the URLs (urlfor:Article:12, for instance), then have your rendering code replace those with actual URLs.
I just thought of another possible solution for the use case:
The admin user specifies the relationships before they edit the text (using Chosen.js, this can be done in a user-friendly way).
Then the person either submits the form to save the model or it can be done asynchronously.
When the relationships are saved, a shortcode is generated and displayed for each of them, and that short-code can easily be pasted in the text.
When displaying the text in the front-end, the text will be parsed for the shortcode, in a similar way to what #freakyDaz is suggesting.
In this way, I don't have to hack or create custom actions in the editor. I think it's a pretty pragmatical approach, but I'd like to hear your opinions. Of course, the admin who's making the text should be educated to follow the process in that order, but in my case very few persons can be admins (1 or 2), so it's manageable.
I'm writing a quick app for a user to track their daily bills (for money tracking purposes). I want the user to be able to define their own categories that a bill can be applicable for. I'm trying however to decide the best way to model this and also validate categories as unique.
My initial thought was this:
class User
include Mongoid::Document
embeds_many :bills
field :categories, :type => Array
end
class Bill
include Mongoid::Document
embeded_in :user, :inverse_of => :bills
field :category
index :category
end
So a user can add categories, just as strings, and when they add a bill, they'll choose from their available categories for the bill.
So, a couple questions:
Does this seem like the proper design? I Don't think it's necessary to define an actual category model as it's literally just a string used to index bills on, but I'm not sure if there are other benefits to a separate model
How do I validate_uniqueness_of :categories in my user model. I don't think it works on array items like this, but I could be wrong. I don't want a user to create categories with the same name. I suppose this might be the advantage of a separate model, embedded in the User, but again it seems like more work than necessary.
Can someone tell me my best options here to validate that a user has unique categories (but users can have the same categories, i obviously don't care about that, just unique in the scope of a single user)
[Update]
The design seems proper. In a Rails specific way how would you validate the uniqueness? When adding a category pull the list and do an indexOf check to ensure it doesn't exist. If it does just bounce back an error.
I'm not a Rails guy, let me know if I'm off track or something.
I'm not sure MongoDB would be the best choice of storage engines for that. You would be better off using MySQL with a categories table.
Knocks against MongoDB:
Not ACID transactions
No single server durability
Not relational (you want relational for a bill tracking application)
I've recently begun playing around with MongoDB on Rails through use of the MongoMapper gem. I was wondering if there is some sort of way to have a class/object/collection be both a Document as well as an Embedded Document. I want the same entity to be both included/embedded in other documents, and also be able to exist on its own as an object.
Is this possible? Am I tackling this problem the wrong way? Any discussion/advice would be greatly appreciated as resources on the web seem low for this stuff right now.
I have a very similar usecase and the solition was to have (using your entity names):
a collection with People
a embedded model BusinessPerson that belongs_to_related :person
a collection with Businesses that embeds_many :busiess_people
The idea behind this was that I have extra fields in BusinessPerson that may not apply if the Person also run a different business. Let's say the role in that business, an email address or the share of it.
No you might say getting the business a person might look hard, but it isn't:
class Person
def businesses
Business.where('business_people._id' => self.id)
end
end