Rails: Create controller instance variables for each entry in the table - ruby-on-rails

In my controller's show method, I am having trouble creating an instance variable for each 'Subject' in my database. The "#pie_correct" variable works, but the problem is in the ".each" loop - I get the error "syntax error, unexpected '=', expecting keyword_end" How should I change this?
#pie_correct = TestQuestions.where(:correct => true).joins(:test).merge(Test.where(user_id: current_user))
Subject.all.each do |s|
"#subject_ + #{s.id} + _correct" = #pie_correct.joins(:question).merge((Question.all).joins(:subject).merge(Subject.where(id: s.id)))
end
Thanks.

You're looking for the instance_variable_set method:
Subject.all.each do |s|
instance_variable_set "#subject_#{s.id}_correct", #pie_correct.joins(:question).merge((Question.all).joins(:subject).merge(Subject.where(id: s.id)))
end
I also believe that you don't need the pluses within your interpolated string.

System
Props to Chris Peters for his answer (which is right)
--
I wanted to raise a point about your system here. Why are you setting multiple instance variables?
At a loss for any other reason why, I'd recommend this goes against the DRY programming principles of Rails, as well as the Single Source Of Truth idea - which means you need to set data once, and use it as you wish.
I would personally set a single instance variable, and loop through that to pull the associative data.

Related

Some questions about symbol and instance method in Ruby

I'm currently try to learn ruby on 'Learn Ruby The Hard Way'
Here's my question...
The following code are from exercise 40:
cities = {'CA'=> 'San Francisco', 'MI'=> 'Detroit', 'FL'=> 'Jacksonville'}
cities['NY'] = 'New York'
cities['OR'] = 'Portland'
def find_city(map, state)
if map.include? state
return map[state]
else
return 'Not found.'
end
end
cities[:find] = method(:find_city)
while true
print 'State? (ENTER to quit) '
state = gets.chomp
break if state.empty?
puts cities[:find].call(cities, state)
end
I played around with the code, and finally understand how it works.
But I still don't understand about two things:
first...
In about middle of the code,
it defined a variable
cities[:find] = method(:find_city)
As what I know for now, the :(colon) declare a symbol.
I want to know is it a better practice to name a variable as cities[:find]
instead of using cities_find in this case?
I'm not quite sure what's the differences, or maybe it's much readable for most rubyist?
And the second one is also about the same line.
method(:find_city)
I know it allows me to call the find_city method.
But again, why I have to put a colon before find_city?
Does this code means parse the arguments I put in to symbols?
I have to say that Learn Ruby The Hard Way gives us a really Really REALLY GOOD example of what we should NOT do. No rubyist will ever type such code in his/her projects. This piece of code is confusing, unreadable and is an abuse of metaprogramming.
Anyway, I dissect that code for you.
The confusing part starts with this line:
cities[:find] = method(:find_city)
Let's look at the right side of the =. It calls a method whose name is method, as you may guess, the return value of the method call is the method find_city, more precisely, a Method object that wraps the method find_city with its scope. Then that method is stored in the hash cities, with a symbol :find as the key. So the value of cities now become
{
'CA'=> 'San Francisco',
'MI'=> 'Detroit',
'FL'=> 'Jacksonville',
'NY' => 'New York',
'OR' => 'Portland',
:find => #<Method:main.find_city>
}
You can see that the last key-value pair is really really weird, and it shouldn't be there because the hash cities should only store states and their capitals. Heck!
Then here comes this even weirder expression cities[:find].call(cities, state). Let's see how this work.
cities[:find] simply retrieve the Method object from the hash (still remember what method it wraps?)
cities[:find].call(cities, state) invokes the method it wraps, which is find_city, in the scope that the Method object wraps, which is the top level object (a.k.a. main), Method#call returns whatever the method wrapped in returns. So this expression is just find_city(cities, state), written in an alien style.
cities[:find] = method(:find_city)
Here, cities is a hash and the method object returned by method(:find_city) is assigned to the hash key find which is a symbol.
I think it depends upon you and the context of program where you are writing this.
A simple method_var = method(:find_city) would work here as well.
method(:find_city)
I know it allows me to call the find_city method. But again, why I have to put a colon before find_city? Does this code means parse the arguments I put in to symbols?
Here, you are passing the method name as an argument, you have to either pass it as a symbol or string.
In Ruby, the method method creates a Method Object. This allow you to pass it around in your code and call it later using the .call method on your Method object.
Since calling method(my_method) would evaluate my_method and pass the result to method(...), you need a way to tell the method method which method to use. That's why you basically pass in the method name as a Symbol into the method method :D
So it actually defined a proc, and make :find and find_city sort like key and value...Probably...

Read json serialised objects back from a file

I am aiming to serialise a set of objects into a file so as to create a backup. I have the start of that working, using a methods on the models (simplified here, assuming I have two ActiveRecords foo and bar):
def backup(file, foo, bar)
file.write(foo.to_json(root: true))
file.write(bar.to_json(root: true))
end
This gives me a file as I desire, in this case with two records:
{"foo":{"Account_id":1,"Name":"F","created_at":"2013-04-16T10:06:19Z","id":1,"updated_at":"2013-04-20T11:36:23Z"}}
{"bar":{"Account_id":1,"Name":"B","created_at":"2013-04-16T10:06:19Z","id":1,"updated_at":"2013-04-20T11:36:23Z"}}
At a later date I then want to read that backup in and reinstantiate those objects, probably then persisting them back to the database. My aim is to iterate through the file checking the type of each object, then instantiating the right object.
I have part of the logic, but not yet all of it, I haven't worked out how I determine the type of each serialised object before I instantiate it. The code I have for a restore is as follows:
def restore(file)
file.each_line do |line|
**<some magic that parses my line into objectType and objectHash>**
case objectType
when :foo
Foo.new.from_json(objectHash)
Foo.process
Foo.save!
when :bar
Bar.new.from_json(objectHash)
Bar.process
Bar.save!
end
end
end
What I'm looking for is the bit that goes in the "some magic" section. I can just write the code to parse the line directly to determine whether it's a foo or a bar, but I feel like there's probably some tricky Rails/Ruby way to do this that is automatic. Unfortunately, in this case Google is not being my friend. All I can see are pages that are focused on json in the web requests, but not parsing json back in this way. Is there something I'm missing, or should I just write the code to split the string directly and read the object type?
If I do write the code to split the string directly, I would write something along the lines of:
objectType = line[/^{"(\w*)"=>(.*)}/, 1]
objectHash = line[/{"(\w*)"=>(.*)}/, 2]
This is pretty ugly and I'm sure there's a better way (which I'm still looking into), but I'm not sure that this is even the right approach v's there being something that automatically looks at a json representation and knows from the root value what object to instantiate.
Lastly, the actual instantiation using from_json isn't working either, it isn't populating any of the fields on my ActiveRecord. It gives me nil parameters, so I think the parse syntax isn't right.
So, that makes three questions:
Is there a way to determine which object it is that I'm just missing, that is much cleaner?
If there isn't and I need to use a regexp, is there a syntax to get both bits of the line parsed in a single go, rather than my two lines with the same regexp?
The from_json syntax appears unhappy. Is there a syntax I'm missing here? (no longer a question - the code above is fixed, I was using as_json when it should have been to_json, although the documentation is rather unclear on that....)
(Note: edits over time to clarify my question, and because I've now got a regexp that works (didn't before), but still not sure it's very elegant.)
Further information - one of the problems here, as I dig into it further, is that the as_json isn't actually giving me json - what I have in the file is a hash, not json at all. Further, the values for created_at and lastupdated_at in the hash aren't quoted - so basically that's what's causing the parse on the way back in to fail. I've worked out that I should use to_json instead of as_json, although the documentation suggests that as_json should work.
I'm not sure I fully understand you're methodology, but I think using JSON.parse() would help.
There's some good information here http://mike.bailey.net.au/2011/02/json-with-ruby-and-rails/
This would help you translate the raw object back to a hash.
OK, so I think I've got something that works. I'm not convinced at all that it's elegant, but it gives me the result. I'll spend some time later trying to make it cleaner.
The code looks like this:
file.each_line do |line|
objectType = line[/^{"(\w*)":(.*)}/, 1]
objectJSON = line[/{"(\w*)":(.*)}/, 2]
objectHash = JSON.parse(objectJSON)
case objectType
when 'foo'
restoredFoo = Foo.new(objectHash.except('id', 'created_at', 'updated_at'))
restoredFoo.created_at = objectHash['created_at']
restoredFoo.updated_at = objectHash['updated_at']
restoredFoo.save!
end
when 'bar'
restoredBar = Bar.new(objectHash.except('id', 'created_at', 'updated_at'))
restoredBar.created_at = objectHash['created_at']
restoredBar.updated_at = objectHash['updated_at']
restoredBar.save!
end
end
Items of note:
I feel like there should be a way to create the object that isn't a JSON.parse, but rather would make use of the from_json method on the model. I'm not sure what the from_json is good for if it doesn't do this!!
I'm having fun with mass_assignment. I don't really want to use :without_protection => true, although this would be an option. My concern is that I do want the created_at and updated_at to be restored as they were, but I want a new id. I'm going to be doing this for a number of entities in my application, I didn't really want to end up replicating the attributes_protected in the code - it seems not very DRY
I'm still pretty sure my reg exp can give me both objectType and objectJSON in one call
But having said all that, it works, which is a good step forwards.

How to chain try() and scoped to_s() in Rails?

In a Rails view, one can use try to output only if there is a value in the database, e.g
#model.try(:date)
And one can chain trys if, for example, the output is needed as a string
#model.try(:date).try(:to_s)
But what if I need to call a scoped format? I've tried
#model.try(:date).try(:to_s(:long))
#model.try(:date).try(:to_s).try(:long)
What is the correct syntax for this? And what is a good reference for more explanation?
Thanks
From the fine manual:
try(*a, &b)
[...]
try also accepts arguments and/or a block, for the method it is trying
Person.try(:find, 1)
So I think you want:
#model.try(:date).try(:to_s, :long)
This one won't work:
#model.try(:date).try(:to_s(:long))
because you're trying to access the :to_s symbol as a method (:to_s(:long)). This one won't work:
#model.try(:date).try(:to_s).try(:long)
because you're trying to call the long method on what to_s returns and you probably don't have a String#long method defined.
mu is too short's answer shows the correct usage for the try method with parameters:
#model.try(:date).try(:to_s, :long)
However, if you are using Ruby 2.3 or later, you should stop using try and give the safe navigation operator a try (no pun intended):
#model&.date&.to_s(:long)
The following answer is here for historical purposes – adding a rescue nil to the end of statements is considered bad practice, since it suppresses all exceptions:
For long chains that can fail, I'd rather use:
#model.date.to_s(:long) rescue nil
Instead of filling up my view with try(...) calls.
Also, try to use I18n.localize for date formatting, like this:
l #model.date, format: :long rescue nil
See:
http://rails-bestpractices.com/posts/42-use-i18n-localize-for-date-time-formating
In case you often use try chains without blocks, an option is to extend the Object class:
class Object
def try_chain(*args)
args.inject(self) do |result, method|
result.try(method)
end
end
end
And then simply use #model.try_chain(:date, :to_s)

Is there any way to define a model's attribute as always html_safe?

I have a model called Feature with a variable called body_string, which contains HTML markup I'd like to render, rather than escape.
Every time I reference body_string in my views, I need to use <%=raw or .html_safe. This seems redundant and not-so-DRY.
Is there any way that I can establish once-and-for-all the body_string variable as html_safe?
I'm assuming this would happen in the app/models/feature.rb file, but I can't figure out what the right syntax would be, exactly. I've thought of this:
def body_string
return self.body_string.html_safe
end
But Rails doesn't like it; it raises a stack level too deep exception.
Naturally I could define a variable/method with a different name:
def safe_body_string
return self.body_string.html_safe
end
And then just change all references in the views from body_string to safe_body_string. But somehow this seems almost as un-DRY as simply using raw or .html_safe in the first place.
Any insights to how best to handle this? I feel like there must be something really elegant that I'm just not seeing.
Just use read_attribute to avoid the recursive call to body_string:
def body_string
read_attribute(:body_string).html_safe
end
read_attribute is complemented by write_attribute for setting attributes from within your model.
A note on style: Don't use explicit returns unless you actually need them. The result of the last statement in a method is implicitly the value returned from the method.
While #meager's answer will definitely work, I don't think this logic belongs in a model. Simply because it adds view-level concerns (HTML safeness) to the model layer, which should just include business logic. Instead, I would recommend using a Presenter for this (see http://nithinbekal.com/posts/rails-presenters/ or find a gem for this -- I personally love Display Case). Your presenter can easily override the body_string method and provide the .html_safe designation when displaying in the view. This way you separate your concerns and can continue to get body_string from other models without mixing in the view concern.
Maybe this gem is useful for you. I also wanted to stop repeating html_safe all the time when the content is completely trustable.
http://rubygems.org/gems/html_safe_attribute
Or you can also use this approach,
def body_string
super && super.html_safe
end

Ruby on Rails- :symbols, #iVars and "strings" - oh my!

New to Rails and trying to get my head around when/why to use :symbols, #ivars , "strings" within the framework.
I think I understand the differences between them conceptually
only one :symbol instance per project
one #ivar per instance
multiple "strings" - as they are created whenever referenced (?)
Feel free to correct me!
The main confusion comes from understanding the rules & conventions of what Rails expects - where and WHY?
I'm sure there's an "Ah ha!" moment coming but I haven't had it yet...as it seems pretty arbitrary to me (coming from C/Obj-C).
-thx
The #instance_variable is an instance variable. It is usually defined in the controller and accessible in the views.
The "string" is a string, like as in any other language.
The :symbol, is as you mentioned it's an efficient way of representing names and strings; they are literal values. It is initialized and exists only once during the ruby session. It's not a string, since you don't have access to String methods; it's a Symbol. On top of that, it's immutable. For those reasons, it becomes very handy in representing keys in hashs. Rails methods uses hashes, thus, you find symbols a bit everywhere in Rails.
Instance variables are pretty straightforward: they track properties/values of a particular instance, so you use them when you the values will vary across instances.
Symbols vs. strings are a bit more arbitrary. Symbols are generally used for constant values, in much the same way that a language such as C would use enums; Ruby doesn't have enums, so symbols are often used to fill that gap. Strings are used for more varied pieces of text that won't be used as a flag or similar constant.
Symbols are kind of like pointers (not in the C-ish way, but in C-ish thinking, they point). Well, you use symbols when you are manipulating properties. They are one of the great benefits of dynamic typing if you'd ask me. (For potential voters I do not mean any harm, I do know that they are not pointers, but it felt 'ah-ha!' for me).
:action => "index"
Instance variables are needed when you fetch data from your model and you want to use them across your views (inside your controller method).
def my_controller_method
#myposts = Post.find(:all)
end
# inside view
<% for #myposts do |m| %>
<i><%= m.title %></i>
<% end %>
Just a heads up, the rules and conventions kinda change rapidly (as I discovered on my Rails journey) quite a lot per version. Having the right guide with the right Rails helps. Good luck with coding!
Instance variables don't really belong in the same list as strings and symbols. Strings and Symbols are types of classes whereas instance variables are a type of variable. So instance variables (#var) are just a way to store a value between methods of one instance of one class:
class Calculator
#counter = 0
def inc
#counter += 1
end
def dec
#counter -= 1
end
end
Here is a good article on the distinction between symbols and strings.
The Rails controller access the rails database through Models by ORM (Object Relation Mapping)i.e Model class will mapped to its corresponding table and Objects are directly mapped to rows in the table.In order to get the results for a given user query,the instance variable (#instance_variable) is the perfect choice to deal with it.

Resources