So basically I have a Book model that contains information like
book title (string)
author (string)
description (text)
etc.
Now on the front end I added the capability of dynamically adding more author fields. In the form I named it book[coauthor], so if I add a second author, I'd have a field book[coauthor][name_0].
What I'd like to achieve is combine book[author] and all book[coauthor][name_i] to one big string and separate each name with ,. (i.e. so with book[author] being "Alice" and book[coauthor][name_0] being "Bob" I should get "Alice,Bob" saved in book[author] in DB)
First Question: At this point is it better to do it with Javascript or with Rails (in controller)?
Second Questiion: Currently I'm doing it in the Rails controller but got Can't mass-assign protected attribute 'coauthor'. However I don't want to make it a real attribute since all I need is some processing before saving all the information to the author field. What should I do?
Thanks guys.
The first question is a bit loaded, and you might get some heated disagreements but the way I look at it is this. Not everybody runs with Javascript enabled, so I make sure I have a way to do everything through Rails myself.
In this case, it's actually quite easy.
The first thing I recommend is to not send the coauthors as part of the params[:book]. Instead, send them separately as a params[:coauthors]. Then in your controller you can do this:
#book = Book.new(params[:book])
#book.author = ([#book.author] + [params[:coauthors]]).join(",")
#book.save
Actually, it may be better to update params[:book][:author] since that would work for both create and update. Either way, I hope that helps.
To the first question: I don't see any compelling reason to one over the other. JavaScript might provide a bit more ability to validate/fix formatting in this field in 'real time', but I'm not sure that's particularly important.
To the second question: You don't need to make something a real attribute in order to make it accessible. You've presumably created coauthor as a virtual attribute using attr_accessor, but this doesn't automatically add it to the mass-assignment whitelist. To do that, also add it to your attr_accessible list.
Related
I have this information on a rails app that I wish to display, but I want to manipulate it a little bit and change things up with it. The information is stored in a the database. Now i'm looking at my show.html.haml page and creating a table with this information.
In the DB it is stored as
{
"images" => true,
"age" => true,
"position" => true
}
I can also pull this up in the console by simply typing
player.last.information
So with that in mind, in my haml file I have
%dd #player.information
and I get all the information that I need. However, I want to separate them a little bit. Doing some research on this here I was able to break up the information in the console line by line. However I'm having transferring them from my terminal into code.
I'm trying to start out very simple by in my model going simply
def information
#player.information
end
and then in my show.html.haml file assuming it is just as easy as this, and not having any luck. If anything this is my question. How am I able to get a method that was created in my model to appear in my view?
%dd :information
I have not touched anything in my controller. I don't think I need to do anything with it, (although certainly could be wrong) Would anybody be able to take a quick look at this for me?
Much thanks.
First off, this bit of code that you have
def information
#player.information
end
will throw a Stack Error (and actually, it'd probably break on the #player instance being used inside it's own instance method before it even got to the Stack Error). If you absolutely had to override information, for example if information was a string and you required that it was always lower-cased, you could define the method like so:
def information
read_attribute(:information).downcase
end
However, this would typically be discouraged. You'll be better served to leave your attributes alone, so that you can always get the raw content of the attribute when you need it and have supplementary methods if you want some particular formatting of the attribute.
For example, if you need the information attribute broken out, perhaps formatting all of the keys to be in a sentence, it'd be better to have something like this:
def formatted_information
self.information.keys.to_sentence.titleize
end
which would give you the following output:
"Images, Age, And Position"
by doing #player.formatted_information.
Of course, I'm not familiar with exactly what kind of formatting you're wanting to do to that hash but sky's the limit, so go crazy.
Side Question
Also, I'd be curious to know why you're storing a hash of values in an information attribute, instead of just having each of the keys in that attribute be it's own attribute - i.e. #player.images, #player.age, and #player.position?
I'm creating an application where products will be created by my customer (something like an e-commerce website), so I obviously require translated descriptions stored in database, I can't force my customer to learn git/yml.
I have two ideas on how to correctly localize descriptions (and eventually product name) and store them in database, but if there is a well-known approach that I should use, I would be really happy to know it.
The first idea seems the most logical for me, but I would like to make it "transparent" to me, I don't want to write joins everywhere, so some suggestion on how to achieve this, if it's the correct one, would be appreciated.
Idea 1:
Create a database table products (with name and description field set maybe to the default locale language), then a products_translations table which contains a table structured in this way:
products_translations
- id
- locale
- product_id
- name
- description
As an example: product_translation: { id: 1, locale: 'en', product_id: 3, name: 'toy', description: 'play' }
But I want to access to translations without the requirement to write a lot of IFs everywhere. So if I write product.name it should return en or it based on current locale.
Bonus: Are there any gems that can help me to achieve this?
Idea 2: The other idea is to have a table with name_locale1, name_it and so on, but I don't like this approach because will pollute my model objects with fields and I will have a giant table.
However, in this way I can avoid join on every query for that object.
If there is a greater approach which I don't know about (a database pattern or similar), it's ok that too, I'm not forced to strict to only these two ideas, however I have to choose between the two and I really don't know which could be better.
Important: I would like to keep translations stored in yml files, except for dynamic contents, which obviously require translations in database.
I agree with PinnyM that the first approach is the better of the two, but rather than implement your own schema, I would highly recommend you implement Globalize3 where most of the structural decisions have been taken for you (and by Mr Fuchs himself, no less). Also, with the rails helpers, you just call something like product.name on a model instance and the gem will figure out how to display it in the correct locale, awesome!
The first approach is the recommended one. As you surmised, the second approach is not as clean and requires more work on the coding end with no real gain since you still have to join on this monster table. To the contrary, the first method requires at most one join, while the second approach requires a join on each attribute you may want to add localization support.
You can simply append a scope on all your product calls such as:
scope :for_locale, lambda{|locale| joins(:product_translations).
where(product_translations: {locale: locale || 'en'}) }
and pass in the session locale (or wherever you are storing it).
I'm having a very hard time trying to figure out how to do this the MVC way. I have a Comment model which holds a body attribute. This attribute may contain mentions such as the following:
Hi! This is me mentioning #someone.
Everytime someone posts a comment, an accessor method in the model converts all #mention to something like #user:231# where 231 would be the user's id. This way, if the mentioned user changes their username, I can still link & mention him without problems on older comments.
Now, I want to be able to access the body attribute and get the mentions already converted to links. It appears that doing this the MVC way, from within the model is not possible from what I have investigated.
Is there any easy way to do this? I don't wanna have to convert all the mentions on the controller because I think it could lead to repeated code and non-testable code.
Could anyone give me some advice on this?
Thanks!
Parsing the message into a particular format and then re-saving it in the database where it can then be edited at a later date is silly. I'm sorry to be so blunt, but doing it this way is fundamentally broken for one major reason: when a user goes to edit the message later on, they'll see the formatted text unless you format it back. Do you really want to be responsible for doing this?
I would hope not. Because you're a programmer, you're naturally lazy and would like to do things in as few steps as possible.
What I would recommend doing to solve this problem is to parse the message when you display it on the page. Before you go screaming at me that this is computationally intensive if you've got a large amount of hits, hear me out. When it's displayed on the page, you can then cache it like this:
<% cache comment do %>
# code goes here
<% end %>
This will store the final output in whatever cache you've set up with Rails, possibly Memcached or Redis, using a cache key which includes the comment's updated_at timestamp. Pay attention to this, it'll be useful later.
Retrieval from this cache will be faster than parsing it, and will be easier for you than to convert the message back and forth between its versions.
When a comment is updated, the updated_at timestamp will be different and so the new comment will be rendered first, then cached. In Memcached (so I'm told) it will clear the oldest cache key that hasn't been referenced if it needs more memory, thereby cleaning out the older comments.
Wouldn't you end up mangling the original message? Let's say I originally posted:
"Hi! This is me mentioning #bob."
From what I understand, you want to store this as:
"Hi! This is me mentioning #user:1#"
Now, if bob were to change his username to "fred", my message would now look like this:
"Hi! This is me mentioning #fred"
It may be easier to simply store a many-to-many relation between messages and users it mentions. That way, you still can easily see which messages mention a specific user, but you don't need to mangle the original message to do so.
If you need to convert each mention into a link, you could order the entries in the relationship table in the same order that they appear in the message.
maybe this gem help you https://github.com/twitter/twitter-text-rb
First, include Twitter::Autolink module from your class or helper
module ApplicationHelper
include Twitter::Autolink
end
From views, you can call it by :
<%= auto_link("Hi #john_doe, welcome to #ruby") %>
it will generate link to twitter john_doe username and ruby hashtag
I want to have a site that is a simple blog so I created a model:
class Post < ActiveRecord::Base
attr_accessible :title, :body
end
I want to use Markdown but without HTML tags. I also want always to keep database clean and my idea is to use before_save()/before_update() callbacks to sanitise my input and escape HTML.
I don't care about caching and performance therefore I always want to render post when needed. My idea is toadd following to the model:
def body_as_html
html_from_markdown(body)
end
What do you think of such design? MVC and ActiveRecord are new for me and I am not sure of used callback.
I see nothing obvious wrong with that method. Caching is a very simple thing to enable if performance becomes an issue... the important thing to make caching useful is to reduce or eliminate the dynamic content on the page, so that the cache doesn't constantly get obsolete. If you're just showing the blog post, then the cache only needs to be regenerated if the blog changes, or perhaps if someone adds a comment (if you have comments).
My general rule of thumb is to keep the data in your database as "pure" as possible, and do any sanitization, rendering, escaping or general munging as close to the user as possible - typically in a helper method or the view, in a Rails app.
This has served me well for several reasons:
Different representations of your data may have display requirements - if you implement a console interface at some point, you won't want to have all that html sanitization.
Keeping all munging as far out from the database as possible makes it clear whose responsibility it is to sanitize. Many tools or new developers maintaining your code may not realize that strings are already sanitized, leading to double-escaping and other formatting ugliness. This also applies to the "different representations" problem, as things can end up escaped in multiple different ways.
When you look in your database by hand, which will end up happening from time to time, it's nice to see things in their un-munged form.
So, to address your specific project, I would suggest having your users enter their text as Markdown and storing it straight in to the database, without the before_save hook (which, as an aside, would be called on creation or update, so you wouldn't also need a before_update hook unless there was something specific that you wanted on update but not creation). I would then create a helper method, maybe santize_markdown, to do your sanitization. You could then call your helper method on the raw markdown, and generate your body html from the sanitized markdown. This could go in the view or in another helper method according to your taste and how many different places you were doing it, but I probably wouldn't put it in the model since it's so display-specific.
I'm still new to ROR, so pardon the simplicity of the question...
So http://www.example.com/controller/:id displays a record in my table, with :id being a number (1,2,3 etc.).
Is there a way I can have :id in the URL be the value of a field in the displayed record? Such that I can have http://www.example.com/controller/record_field? I want to have a human-friendly reference to specific records in my table. I'm sure this must be possible. Do I change something in routes.rb?
Thanks for the help!
The cleanest way is to add a new find method in your model (or simply use the find_by_fieldname Rails gives you in your control). Then you'll have your controller use that method instead of the regular find(params[:id]) to pull your model record.
Check out Ryan B's screencast on this here. It's pretty easy, and he's a good teacher, so you shouldn't have any problems.
I use the excellent rails plugin named friendly_id.
http://github.com/norman/friendly_id/tree/master
That should sort you out nicely. It is well documented too.
Take care around fields that might have modern Greek characters—might need to figure a work around for those.
Jon Smock's solution will work, too. I tend to prefer the following.
class Hamburger << ActiveRecord::Base
#this normally defaults to id
def to_param
name
end
end
class SomeModelController << ApplicationController
def show
#hamburger = Hamburger.find(params[:id]) #still default code
end
end
#goes in some view
This is the <%= link_to "tastiest hamburger ever", url_for(#hamburger) %>.
This is, loosely speaking, an SEO technique (beautiful URLs are also user-friendly and I suggest them to absolutely everyone even if you don't care about SEO, for example on pages behind a login). I have a more extended discussion of Rails SEO, which includes other tips like this, here.
Important tip: You should consider, at design-time, what you are going to do if the param should change. For example, in my hamburger scenario, it is entirely possible that I might rename "Sinfully Delicious Cheeseburger" to "Triple Bypass". If that changes URLs, there are some possible implications, such as breakage of customer links to my website. Accordingly, for production use I usually give these models an immutable permalink attribute which I initialize to be human-meaningful exactly once. If the object later changes, oh well, the URL stays the same. (There are other solutions -- that is just the easiest one.)