How to execute code from string Rails and render it? - ruby-on-rails

I have content column in my Posts table. I insert into Posts table some info in Post model
Post.create({content: "<%= link_to('post', post_path) %> blah blah ... "})
When I want to renter #post.content I receive "<%= link_to('post', post_path) %> blah blah ..." without string executing.
How to render it right?

So, as said it is a very wrong idea to store code in your database. There is very little exception to that and it's very complicated to implement, bug prone, and present security issues. In short: don't do it.
So you need an alternative design. There is basically 3 situations, pick-up the one that correspond to your application's goal:
A- If your method parameters and output will never change in the future, then you execute your code and store the output in a cached columns (type string or text) of your model before saving. When retrieving this cached output later you can just use it as it
post.cached_output = my_method(...)
post.save
# In the future, in your view:
<%= post.cached_output %>
B- If the parameter of your method will never change in the future but the method output differs according to external elements, then just store that one parameter's value and call the method each time you need its output
post.cached_parameter = 17
post.save
# In the future, in your view:
<%= my_method(params: post.cached_parameter) %>
C- If your method's parameter change over time and your method output also changes overtime, then you don't need to cache anything in your database

Related

Getting to run code a specific number of times selected from a form

I have an instant variable (#number) attached to a drop down array of numbers (1..10). I want my block of code that I have in my HTML to generate that one block of code, however many times the specific number was selected
# for example.
<% #number.times do %>
<div> class = (row) 'HTML code'>
<[image]>
<div/>
<% end %>
I've tried to run this code in my html.erb file and I keep getting an error saying undefined .times.
I'm very new to ruby and code but I heard .times is similar to .each which is ruby language, and I've also defined in my controller parameters to my instant variable #number and even tried to assign an integer of .to_i to it.....any suggestions would be appreciated ;)
The error message tells me your #number variable contains a string. Either make sure you assign a numeric value to it in the first place or transform it to a number prior to calling times:
#number.to_i.times
You wrote above that .to_i didn't work for you though - do you get the same error message then?

Rails 3 and Memcached - Intelligent caching without expiration

I am implementing caching into my Rails project via Memcached and particularly trying to cache side column blocks (most recent photos, blogs, etc), and currently I have them expiring the cache every 15 minutes or so. Which works, but if I can do it more up-to-date like whenever new content is added, updated or whatnot, that would be better.
I was watching the episode of the Scaling Rails screencasts on Memcached http://content.newrelic.com/railslab/videos/08-ScalingRails-Memcached-fixed.mp4, and at 8:27 in the video, Gregg Pollack talks about intelligent caching in Memcached in a way where intelligent keys (in this example, the updated_at timestamp) are used to replace previously cached items without having to expire the cache. So whenever the timestamp is updated, the cache would refresh as it seeks a new timestamp, I would presume.
I am using my "Recent Photos" sideblock for this example, and this is how it's set up...
_side-column.html.erb:
<div id="photos"">
<p class="header">Photos</p>
<%= render :partial => 'shared/photos', :collection => #recent_photos %>
</div>
_photos.html.erb
<% cache(photos) do %>
<div class="row">
<%= image_tag photos.thumbnail.url(:thumb) %>
<h3><%= link_to photos.title, photos %></h3>
<p><%= photos.photos_count %> Photos</p>
</div>
</div>
<% end %>
On the first run, Memcached caches the block as views/photos/1-20110308040600 and will reload that cached fragment when the page is refreshed, so far so good. Then I add an additional photo to that particular row in the backend and reload, but the photo count is not updated. The log shows that it's still loading from views/photos/1-20110308040600 and not grabbing an updated timestamp. Everything I'm doing appears to be the same as what the video is doing, what am I doing wrong above?
In addition, there is a part two to this question. As you see in the partial above, #recent_photos query is called for the collection (out of a module in my lib folder). However, I noticed that even when the block is cached, this SELECT query is still being called. I attempted to wrap the entire partial in a block at first as <% cache(#recent_photos) do %>, but obviously this doesn't work - especially as there is no real timestamp on the whole collection, just it's individual items of course. How can I prevent this query from being made if the results are cached already?
UPDATE
In reference to the second question, I found that unless Rails.cache.exist? may just be my ticket, but what's tricky is the wildcard nature of using the timestamp...
UPDATE 2
Disregard my first question entirely, I figured out exactly why the cache wasn't refreshing. That's because the updated_at field wasn't being updated. Reason for that is that I was adding/deleting an item that is a nested resource in a parent, and I probably need to implement a "touch" on that in order to update the updated_at field in the parent.
But my second question still stands...the main #recent_photos query is still being called even if the fragment is cached...is there a way using cache.exists? to target a cache that is named something like /views/photos/1-2011random ?
One of the major flaws with Rails caching is that you cannot reliably separate the controller and the view for cached components. The only solution I've found is to embed the query in the cached block directly, but preferably through a helper method.
For instance, you probably have something like this:
class PhotosController < ApplicationController
def index
# ...
#recent_photos = Photos.where(...).all
# ...
end
end
The first instinct would be to only run that query if it will be required by the view, such as testing for the presence of the cached content. Unfortunately there is a small chance that the content will expire in the interval between you testing for it being cached and actually rendering the page, something that will lead to a template rendering error when the nil-value #recent_photos is used.
Here's a simpler approach:
<%= render :partial => 'shared/photos', :collection => recent_photos %>
Instead of using an instance variable, use a helper method. Define your helper method as you would've the load inside the controller:
module PhotosHelper
def recent_photos
#recent_photos ||= Photos.where(...).all
end
end
In this case the value is saved so that multiple calls to the same helper method only triggers the query once. This may not be necessary in your application and can be omitted. All the method is obligated to do is return a list of "recent photos", after all.
A lot of this mess could be eliminated if Rails supported sub-controllers with their own associated views, which is a variation on the pattern employed here.
As I've been working further with caching since asking this question, I think I'm starting to understand exactly the value of this kind of caching technique.
For example, I have an article and through a variety of things I need for the page which include querying other tables, maybe I need to do five-seven different queries per article. However, caching the article in this way reduces all those queries to one.
I am assuming that with this technique, there always needs to have at least "one" query, as there needs to be "some" way to tell whether the timestamp has been updated or not.

Rails way to offer modified attributes

The case is simple: I have markdown in my database, and want it parsed on output(*).
#post.body is mapped to the posts.body column in the database. Simple, default Activerecord ORM. That column stores the markdown text a user inserts.
Now, I see four ways to offer the markdown rendered version to my views:
First, in app/models/post.rb:
# ...
def body
markdown = RDiscount.new(body)
markdown.to_html
end
Allowing me to simply call #post.body and get an already rendered version. I do see lots of potential problems with that, e.g. on edit the textfield being pre-filled with the rendered HMTL instead of the markdown code.
Second option would be a new attribute in the form of a method
In app/models/post.rb:
# ...
def body_mardownified
markdown = RDiscount.new(body)
markdown.to_html
end
Seems cleanest to me.
Or, third in a helper in app/helpers/application_helper.rb
def markdownify(string)
markdown = RDiscount.new(string)
markdown.to_html
end
Which is used in the view, instead of <%= body %>, <%= mardownify(body) %>.
The fourth way, would be to parse this in the PostsController.
def index
#posts = Post.find(:all)
#posts.each do |p|
p.body = RDiscount.new(string).to_html
#rendered_posts << p
end
end
I am not too familiar with Rails 3 proper method and attribute architecture. How should I go with this? Is there a fifth option? Should I be aware of gotchas, pitfalls or performance issues with one or another of these options?
(*) In future, potentially updated with a database caching layer, or even special columns for rendered versions. But that is beyond the point, merely pointing out, so to avoid discussion on filter-on-output versus filter-on-input :).
The first option you've described won't work as-is. It will cause an infinite loop because when you call RDiscount.new(body) it will use the body method you've just defined to pass into RDiscount (which in turn will call itself again, and again, and so on). If you want to do it this way, you'd need to use RDiscount.new(read_attribute('body')) instead.
Apart from this fact, I think the first option would be confusing for someone new looking at your app as it would not be instantly clear when they see in your view #post.body that this is in fact a modified version of the body.
Personally, I'd go for the second or third options. If you're going to provide it from the model, having a method which describes what it's doing to the body will make it very obvious to anyone else what is going on. If the html version of body will only ever be used in views or mailers (which would be logical), I'd argue that it makes more sense to have the logic in a helper as it seems like the more logical place to have a method that outputs html.
Do not put it in the controller as in your fourth idea, it's really not the right place for it.
Yet another way would be extending the String class with a to_markdown method. This has the benefit of working on any string anywhere in your application
class String
def to_markdown
RDiscount.new(self)
end
end
#post.body.to_markdown
normal bold italic
If you were using HAML, for example in app/views/posts/show.html.haml
:markdown
= #post.body
http://haml-lang.com/docs/yardoc/file.HAML_REFERENCE.html#markdown-filter
How about a reader for body that accepts a parse_with parameter?
def body(parse_with=nil)
b = read_attribute('body')
case parse_with
when :markdown then RDiscount.new(b)
when :escape then CGI.escape(b)
else b
end
end
This way, a regular call to body will function as it used to, and you can pass a parameter to specify what to render with:
#post.body
normal **bold** *italic*
#post.body(:markdown)
normal bold italic

Why do I get nil objects iterating an array in an ERB template?

I am new to Ruby and currently trying a few experiments.
I am confused about these scripts:
<%=#myworlds[2].topic%>
and
<% id = 1 %>
<%=#myworlds[id+1].topic%>
#mywodrld is an instance of a model and topic is the field. When executing the first one, the program runs correctly. When I run the second script, I get the following error:
You have a nil object when you didn't expect it!
The error occurred while evaluating nil.topic
What causes the nil object?
When I try your approach, I can't replicate your problem. It works fine for me. My guess is that you might use the variable id somewhere else also and that when you call #myworlds[id+1].topic id has some other value. But as I said, only a guess.
However, I recommend that you use another syntax when looping through collections of models in Ruby. Try something like this:
<% #myworlds.each do |myworld| %>
<h1><%= myworld.topic %></h1>
<% end %>
And if you really need the value of the iterator, you could always go with:
<% #myworlds.each_with_index do|myworld, i| %>
Where i keeps track of the current index in the array. Another good thing with this is that id no longer exists in memory after the block ended.
Are you sure that you have no other differences between these two code snippets?
In your comment you say that you have #myworlds[#id+1], in the original question you say #myworlds[id+1] (local variable versus instance variable). Can you show the exact code?
Both scripts are OK. You can create variables in one <% %> block, and you can use them in another one (if they are in the same .erb file, of course).
The error message says that your array has no element with index #id+1 or id+1. You have to debug the value of the expression used for the index. I guess that there is somewhere some small mistake, like a typo.
What is the output of your debug(#myworlds[#id+1]) statement when #myworlds[#id+1].topic raises the error?
Also try to debug the value of id:
<pre>The id = <%= debug(id) %> (<%= id.inspect %>)</pre>
(Depending on your version of Rails you may want to use h( id.inspect ))
I'm guessing but for some reason id+1 is probably not equal to 2.
To check the value of id+1 you can do that :
raise (id+1).inspect
Inspect is very useful is you want to see what is in an object :)
I think I know how to solve the problem is. You are trying to each an array data from a model, but u use the parameter [#id+1]. No matter the "id" is global or local variable, but the problem is in the end of array, there are no array with index "id+1". You should add another parameter to prevent the unrecognized parameter.
Try this
if((#myworlds.length-1) > #id)
#id = #id+1
end
:D
It looks like you're looping over an array, but possibly using a for or while loop to accomplish it, rather than use an [].each. Your sample code doesn't give us enough information to work from so we're shooting in the dark attempting to help you.
Manually creating your index then trying to walk the array tends to run into problems where you either miss the first or last item, or you go too far and get the error you are seeing. Because each returns only the items in the array it can't do that.
Something like this might work better:
<% #myworlds.each do |world| %>
...
<%= world.topic %>
<% end %>
I didn't see the answer #DanneManne gave before I wrote my response. I think he's got the right solution.

Rails optmization (with activerecord and view helpers)

Is there a way to do this in Rails:
I have an activerecord query
#posts = Post.find_by_id(10)
Anytime the query is called, SQL is generated and executed at the DB that looks like this
SELECT * FROM 'posts' WHERE id = 10
This happens every time the AR query is executed. Similarly with a helper method like this
<%= f.textarea :name => 'foo' %>
#=> <input type='textarea' name='foo' />
I write some Railsy code that generates some text that is used by some other system (database, web browser). I'm wondering if there's a way to write an AR query or a helper method call that generates the text in the file. This way the text rendering is only done once (each time the code changes) instead of each time the method is called?
Look at the line, it may be going to the database for the first one but ones after it could be saying CACHE at the start of the line meaning it's going to ActiveRecord's query cache.
It also sounds to me like you want to cache the page, not the query. And even if it were the query, I don't think it's as simple as find_by_id(10) :)
Like Radar suggested you should probably look into Rails caching. You can start with something simple like the memory store or file cache and then move to something better like memcached if necessary. You can throw in some caching into the helper method which will cache the result after it is queried once. So for example you can do:
id = 10 # id is probably coming in as a param/argument
cache_key = "Post/#{id}"
#post = Rails.cache.read(cache_key)
if #post.nil?
#post = Post.find_by_id(id)
# Write back to the cache for the next time
Rails.cache.write(cache_key,#post)
end
The only other thing left to do is put in some code to expire the cache entry if the post changes. For that take a look at using "Sweepers" in Rails. Alternatively you can look at some of the caching gems like Cache-fu and Cached-model.
I'm not sure I understand your question fully.
If you're asking about the generated query, you can just do find_by_sql and write your own SQL if you don't want to use the active record dynamic methods.
If you're asking about caching the resulset to a file, it's already in the database, I don't know that if it was in a file it would be much more efficient.

Resources