rails 3 temporary user input to use for comparison - ruby-on-rails-3

I'm not sure how to even research this question so maybe some awesome rails developer can point me in the right direction.
I have a model that's holding a question and correct answer. On the show view, I want the user to enter their answer into an input field and upon pressing submit, their answer is compared to the one held in the model. I don't need to save their answer.
Thoughts?

You could use a non ActiveRecord model for that. Something like this:
class UserAnswer # note that this class doesn't inherit from ActiveRecord::Base
attr_accessor :question_id, :answer
def initialize(params)
#question_id = params[:question_id]
#answer = params[:answer]
end
def correct?
q = QuestionAnswerModel.find(self.question_id)
q.answer == self.answer
end
end
Then in your controller you can do something like this:
user_answer = UserAnswer.new(params) # params contains :question_id and :answer
user_answer.correct? # returns true or false

A simple way is to save the answer confirmation only if it is equal to the answer.
Model:
question
answer
answer_confirmation
Then proceed to make the form as you normally would.
In the model add
validate :check_answer
def check_answer
errors.add(:answer, "Must be the same as answer confirmation") if answer!= answer_confirmation
end

Related

Rails: Is there an efficient way to get all whitelisted attributes with values?

I'm trying to use a model as a template to create a new model. However, I only want to use the attr_accessible attributes from the template model.
Here's what I'm doing now. It works, but it seems too complex.
def copy_attrs_and_errors(other)
self.class.attr_accessible[:default].to_a.each do |attr|
eval("self.#{attr} = other.#{attr}") unless attr.blank?
end
end
I'd like to be able to say something as simple as:
self.attributes = other.whitelist_attributes(:default)
Thanks.
it's a little crazy, but you could do something like this in a module or whatever:
def self.from_accessible_attributes(other)
values = other.attributes.values_at(*other.class.accessible_attributes)
attributes = Hash[other.class.accessible_attributes.zip(values)]
new(attributes)
end

What is the most rails like way to structure the following

I am using devise for two different types of user in my app. They are called user and professional.
I currently have a simple resource based controller called MessagesController which pulls out messages for the current professional like this
class MessagesController < ApplicationController
def index
#messages = Message.find_all_by_profession_id(current_professional.id)
end
end
I want to find the best way of keeping this controller but changing the query based on the type of user that is logged in. I want the same to happen for all actions in the resource (index, new, create, update etc)
I know I can do this
if current_user
#messages = Message.find_all_by_user_id(current_user.id)
else
#messages = Message.find_all_by_profession_id(current_professional.id)
end
but this would be bulky and messy across all actions. I'm sure there must be a better way. What is the most rails like way of doing this? Should I be creating a completely new controller do handle user based messages?
I can think of two ways:
You can put your code inside the initialize method of your controller:
def initialize
if current_user
#messages = Message.find_all_by_user_id(current_user.id)
else
#messages = Message.find_all_by_profession_id(current_professional.id)
end
super
end
Or you can create a before_filter :
class MessagesController < ApplicationController
before_filter :get_messages
private
def get_messages
if current_user
#messages = Message.find_all_by_user_id(current_user.id)
else
#messages = Message.find_all_by_profession_id(current_professional.id)
end
end
end
IMHO, i think you can move this chunk of code to the model, so the controller only make a call passing the user parameter and gets all messages from the model.
# messsages_controller.rb
#messages = Message.find_all_messages(current_user, current_professional)
# message.rb
def self.find_all_messages(user, professional)
if user
self.find_all_by_user_id(user.id)
else
self.find_all_by_profession_id(professional.id)
end
end
I think it is better for this kind of code to be on your model. Of course you can improve the if/else code, but i am out of ideas now.

How to user defined friendly URLs in Rails 3?

Now i have something like this
http://myapp.com/pages/1
http://myapp.com/pages/2
http://myapp.com/pages/3
http://myapp.com/pages/4
And each page belong to one user
What i need is to each user to set it's own custom name for the page.
I was thinking of using the friendly_id gem http://norman.github.com/friendly_id/
but I don't find any method to directly edit the slug to set a custom friendly url
how should i proceed?
FriendlyID is a great gem.
It shouldn't be hard to implement user defined page URL.
Create table pages with user_id and link
class User < ActiveRecord::Base
has_many :pages
class Page < ActiveRecord::Base
belongs_to :user
has_friendly_id :link # link is name of the column whose value will be replaced by slugged value
On the page#new you add an input for the link attribute.
Alternatively, you could set friendly_id on title or something else with :use_slug => true option. This way FriendlyID will take the title and modify it so it doesn't have and restricted characters. It will use it's own table to store slugs. Use cached_slug to increase performanse.
Updated
To give users a choice whether they wan't to set a custom link, you could do this:
Set friendly_id on the link field without slugs..
Make a virtual attribute permalink so you could show it in your forms.
In the before_filter, check whether the permalink is set.
If it is, write it to the link field.
If it's not, write title to the link field.
FriendlyID uses babosa gem to generate slugs. If you decide to use it as well, this is how your filter could look like:
protected
def generate_link
#you might need to use .nil? instead
self.link = self.permalink.empty? ? make_slug(self.title) : make_slug(self.permalink)
end
def make_slug(value)
value.to_slug.normalize.to_s #you could as well use ph6py's way
end
Adding to_param method to one of the models should help:
def to_param
"#{id}-#{call_to_method_that_returns_custom_name.parameterize}"
end
Hope this is what you are looking for :)
I am not using the friendly_url gem and am not sure whether my way is efficient. But it works fine for me.
I have a model called Node with id and friendly url field called url_title.
My routes.rb file:
resource 'nodes/:url_title', :to => 'Nodes#view'
nodes_controller.rb
class NodesController <ActiveController
def view
#node = Node.find_by_url_title(:params(url_title))
end
end
And use the #node variable to populate your view.
Now, whenever I type www.example.com/nodes/awesome-title , it takes me to the proper page. One argument against this can be need to create an index on a non-primary field. But I think that might be required for better performance even in the friendly_url gem. Also, the non-primary field url_title needs to be unique. Again, this might be required even for correct working for friendly_url .
Feel free to correct me if I am wrong in these assumptions.
There are a variety of ways, you can achieve this-
1) using Stringex
2) sluggable-finder
3) friendly_id
A complete step by step methodology with reasons for each to be used can be found out here. Happy reading!

How to access devise current_user from model

Just start to develop with devise for my app authentication, however running into trouble with accessing current_user from a model. I have no problem accessing this from a controller.
For my scenario I need to get the current user id to keep track who has edit the blog post, which I save the user id with before_save filter.
Appreciate your help!
Thanks
You can only pass it down in a parameter.
class SomeModel
def my_nifty_method(user)
end
end
in controller:
class SomeModelsController < ApplicationController
def some_method
sm = SomeModel.new
sm.my_nifty_method(current_user)
end
end
You should be able to do this in your model. 'c' here being the model object and setting it's user_id attribute before create, or save in your case.
before_create {|c| c.user_id = Authorization.current_user.id}
I'm somewhat new to RoR so be warned.
Why are you trying to access to that method in the model?
You can access to the ID attributes via self.id (within the model)

rails 3 response format and versioning using vendor MIME type in the Accept header

Preamble:
I investigated how to version an API and found several ways to do it. I decided to try peter williams' suggestion and created new vendor mime types to specify version and format. I could find no definitive write-up for doing this following "the rails way" so I pieced together info from several places. I was able to get it working, but there is some goofiness in the way the renderers handle Widget array vs Widget instance in respond_with.
Basic steps & problem:
I registered mime types and added renderers for version 1 in both xml and json to ApplicationController, the renderers call to_myproj_v1_xml and to_myproj_v1_json methods in the model. respond_with(#widget) works fine but respond_with(#widgets) throws an HTTP/1.1 500 Internal Server Error saying that the "Template is missing".
Workaround:
"Template is missing" means that no render was called and no matching template exists. by accident, I discovered that it is looking for a class method... so I came up with the code below which works but I'm not really happy with it. The goofiness is mostly in and related to xml = obj.to_myproj_v1_xml(obj) and the duplication in the model.
My question is - has anyone done anything similar in a slightly cleaner fashion?
-= updated code =-
config/initializers/mime_types.rb:
Mime::Type.register 'application/vnd.com.mydomain.myproj-v1+xml', :myproj_v1_xml
Mime::Type.register 'application/vnd.com.mydomain.myproj-v1+json', :myproj_v1_json
app/controllers/application_controller.rb:
class ApplicationController < ActionController::Base
protect_from_forgery
before_filter :authenticate
ActionController.add_renderer :myproj_v1_xml do |obj, options|
xml = obj.to_myproj_v1_xml
self.content_type ||= Mime::Type.lookup('application/vnd.com.mydomain.myproj-v1+xml')
self.response_body = xml
end
ActionController.add_renderer :myproj_v1_json do |obj, options|
json = obj.to_myproj_v1_json
self.content_type ||= Mime::Type.lookup('application/vnd.com.mydomain.myproj-v1+json')
self.response_body = json
end
end
app/models/widget.rb:
class Widget < ActiveRecord::Base
belongs_to :user
V1_FIELDS = [:version, :model, :description, :name, :id]
def to_myproj_v1_xml
self.to_xml(:only => V1_FIELDS)
end
def to_myproj_v1_json
self.to_json(:only => V1_FIELDS)
end
def as_myproj_v1_json
self.as_json(:only => V1_FIELDS)
end
end
app/controllers/widgets_controller.rb:
class WidgetsController < ApplicationController
respond_to :myproj_v1_xml, :myproj_v1_json
def index
#widgets = #user.widgets
respond_with(#widgets)
end
def create
#widget = #user.widgets.create(params[:widget])
respond_with(#widget)
end
def destroy
#widget = #user.widgets.find(params[:id])
respond_with(#widget.destroy)
end
def show
respond_with(#widget = #user.widgets.find(params[:id]))
end
...
end
config/initializers/monkey_array.rb
class Array
def to_myproj_v1_json(options = {})
a = []
self.each { |obj| a.push obj.as_myproj_v1_json }
a.to_json()
end
def to_myproj_v1_xml(options = {})
a = []
self.each { |obj| a.push obj.as_myproj_v1_json } # yes this is json instead of xml. as_json returns a hash
a.to_xml()
end
end
UPDATE:
Found another solution that feels better but still a little weird (I'm still not completely comfortable with monkey patches), probably ok though... basically moved building the response data from the class method to_myproj_v1_json to a monkey patch on Array. This way when there is an Array of Widgets, it calls the instance method as_myproj_v1_json on each Widget and returns the whole Array as desired format.
One note:
as_json has nothing to do with json format, just creates a hash. Add custom formatting to as_myproj_v1_json (or an as_json override if you aren't using custom mime types), then to_json will change a hash to a json string.
i have updated the code below to be what is currently used, so the original question may not make sense. if anyone wants the original question and code shown as was and fixed code in a response i can do that instead.
For the answer: see the question :-)
In short, there are different solutions, of which one is in the question above:
Monkey-patch Array to implement a method that will give the (old) v1 JSON back
I haven't seen this content type trick used anywhere in a Rails project before so this is new to me. The way I've typically seen it done is to define a route namespace (e.g. /api/v1/) which goes to a controller (say, Api::Version1Controller).
Also, I know you want to do things the "Rails way", and maybe this sounds crotchety coming from a guy who has been with Rails since 1.3, but the whole respond_with / respond_to stuff is rather magic to me. I didn't know that respond_to looks for a to_XXX method when it serializes objects, for instance (maybe I need to read up on that). Having to monkey-patch Array like that seems rather silly. Besides, for an API, formatting the model data is really the view's job, not the model's. I might look into something like rabl in this case. There's a good writeup about it here.