I am trying to create persistante variable local to my model but although something that sounds so simple is not working.
I have this in my model:
class Coupon < ActiveRecord::Base
#username = "empty"
#admin = false
def self.setUser(name, isAdmin)
#username = name
#admin = isAdmin
end
def self.get_user (user)#an attempt to access the current_user but did not work i call this from the controller (I understand it is not best practice)
##user = user
self.setUser(user.username,user.admin?)
end
def has_not_occurred
errors.add("property_of","name is not valid:#{#username}") if !validPropertyOf?
end
end
def validProperty_of?
return property_of == #username # || #Admin
end
end
I actually get a "" instead of "empty" or the new value of username in set.user. How do I make these values persist? I have printed the values inside each method so they persist inside the method but not beyond for some reason.
#username is always nil or "" when it gets to has_not_accurred.
Why is this and how do I make it persist? Thank you so much.
I cannont access #user when I set it either (get_user method). I get a nil instance later down at validateProperty_of
I think you forget about database table, you haven't create this one, that's why every variables are non-persistent.
Upgraded Rails now it works. Not sure why.
Related
I think my question title is bit confusing. But what I am meaning to ask is I am creating my own authentication system using mobile. Just like devise comes with current_user to create a session, I want to know how can I achieve same on a different model.
I have a model called Commuter. It also has a id with it.
A record of commuter looks like this.
Commuter.last
<Commuter id: 867, phone_number: "9483942090">
I am trying to create a session after verfying the mobile number with my controller method as follows:
def verify
#commuter = Commuter.where(phone_number: params[:phone_number]).first
if (#commuter && #commuter.authenticate_otp(params[:otp],drift:300))
#commuter.auth_active = true
if #commuter.save
#Removed from session after verified it
session[:phone_number] = nil
session[:is_verified] = nil
#signed in commuter after verified it
sign_in(:commuter, #commuter)
flash[:notice] = "Your mobile no is verified."
end
else
flash[:alert] = "You have entered wrong otp.Please check again."
end
puts "#{current_commuter.phone_number}"
redirect_to root_path
end
I just a puts there to debug. So right now I am getting current_commuter as undefined local variable for obvious reasons I guess. So I wanted to know how can achieve this session based current commuter ?
You can save the Commuter id in the session as session[:cid] = 1 and create a method on your base controller like this
def current_commuter
#commuter ||= Commuter.find session[:cid]
end
helper_method :current_commuter
Say I have two models, Email and Message, with a boolean read attribute, and to mark them read I add a concern with mark_read and mark_unread patch members that route to ReadablesController.
I'd like to make it so that set_readable is automatic, not requiring me to manually query the params, and instead just work for all models with a read attribute. Is there a simple way to accomplish that?
class ReadablesController < ApplicationController
before_action :set_readable
...
def mark_read
#readable.read = true
#readable.save
flash[:notice] = "#{#readable.class.to_s} marked read."
redirect_to :back
end
def mark_unread
#readable.read = false
#readable.save
flash[:notice] = "#{#readable.class.to_s} marked unread."
redirect_to :back
end
private
def set_readable
throw "error" if params[:email_id].present? && params[:message_id].present?
#readable = Email.find(params[:email_id]) if params[:email_id].present?
#readable = Message.find(params[:message_id]) if params[:message_id].present?
end
end
You can check if a model has read attribute with has_attribute?(:read). From there it is trivial to call your mark_read and mark_unread methods.
#model.mark_read if #model.has_attribute?(:read)
This probably goes to your controller's set_readable method where it still will have to check a relevant param, say, params[:read] to invoke the logic.
I'm trying to monkey patch ActiveRecord::FinderMethods in order to use hashed ids for my models. So for example User.find(1) becomes User.find("FEW"). Sadly my overwritten method doesn't get called. Any ideas how to overwrite the find_one method?
module ActiveRecord
module FinderMethods
alias_method :orig_find_one, :find_one
def find_one(id)
if id.is_a?(String)
orig_find_one decrypt_id(id)
else
orig_find_one(id)
end
end
end
end
Here's an article that discusses how to actually do what you want by overriding the User.primary_key method like:
class User
self.primary_key = 'hashed_id'
end
Which would allow you to call User.find and pass it the "hashed_id":
http://ruby-journal.com/how-to-override-default-primary-key-id-in-rails/
So, it's possible.
That said, I would recommend against doing that, and instead using something like User.find_by_hashed_id. The only difference is that this method will return nil when a result is not found instead of throwing an ActiveRecord::RecordNotFound exception. You could throw this manually in your controller:
def show
#user = User.find_by_hashed_id(hashed_id)
raise ActiveRecord::RecordNotFound.new if #user.nil?
... continue processing ...
end
Finally, one other note to make this easier on you -- Rails also has a method you can override in your model, to_param, to tell it what property to use when generating routes. By default, of course, it users the id, but you would probably want to use the hashed_id.
class User
def to_param
self.hashed_id
end
end
Now, in your controller, params[:id] will contain the hashed_id instead of the id.
def show
#user = User.find_by_hashed_id(params[:id])
raise ActiveRecord::RecordNotFound.new if #user.nil?
... continue processing ...
end
I agree that you should be careful when doing this, but it is possible.
If you have a method decode_id that converts a hashed ID back to the original id, then the following will work:
In User.rb
# Extend AR find method to allow finding records by an encoded string id:
def self.find(*ids)
return super if ids.length > 1
# Note the short-circuiting || to fall-back to default behavior
find_by(id: decode_id(ids[0])) || super
end
Just make sure that decode_id returns nil if it's passed an invalid hash. This way you can find by Hashed ID and standard ID, so if you had a user with id 12345, then the following:
User.find(12345)
User.find("12345")
User.find(encode_id(12345))
Should all return the same user.
I'm working on a twitter-like app for practice. Users create posts, and I'm adding functionality so that users can tag other users in their posts by putting #email of the user they want to tag at the beginning of their post.
in_reply_to is the id of the user being tagged in the Micropost.
This is in my Micropost_controller.rb
#reply_regex = /(\A#[^# ]+(#)\w+(\.[a-z]{2,3}){1,2}.*\z)/i
def create
#micropost = current_user.microposts.build(params[:micropost])
if #micropost.content =~ #reply_regex
email = #micropost.content.scan(/([^# ]+(#)\w+(\.[a-z]{2,3}){1,2})/i).first.first
#micropost.in_reply_to = User.find_by_email(email).id
end
if #micropost.save
flash[:success] = "Micropost created!"
redirect_to root_path
When I run the email-extracting part on a string in the console it works perfectly and returns the email. But when I create new microposts, the in_reply_to always stays nil.
Something like this:
class C
#a = 11
end
does not create an instance variable named #a for instances of C. When you hit #a = 11, self will be the class itself so #a will be an instance variable for the the object C. If you put the above into irb and look at C.instance_variables, you will see this:
>> C.instance_variables
=> [:a]
but when you look at an instance of C:
>> C.new.instance_variables
=> []
Also, instance variables are automatically created on first use and initialized to be nil.
Combining the above tells us that you have a #reply_regex instance variable in the class object MicropostController but not in the instances. Your def create is an instance method so it will use the #reply_regex instance variable; but you don't have #reply_regex as an instance variable for MicropostController objects so it will be created and initialized as nil inside your if statement. The result is that your if ends up being this:
if #micropost.content =~ nil
and #micropost.content =~ nil will evaluate to nil and, since nil is false in a boolean context, the if block is never entered and #micropost.in_reply_to is never assigned a value.
You can use a class variable for your regex:
##reply_regex = /(\A#[^# ]+(#)\w+(\.[a-z]{2,3}){1,2}.*\z)/i
def create
#micropost = current_user.microposts.build(params[:micropost])
if #micropost.content =~ ##reply_regex
#...
as class variables are visible to instance methods or, better, just use a constant:
REPLY_REGEX = /(\A#[^# ]+(#)\w+(\.[a-z]{2,3}){1,2}.*\z)/i
def create
#micropost = current_user.microposts.build(params[:micropost])
if #micropost.content =~ REPLY_REGEX
#...
As an aside, I think you should move the reply-to checking into your model. You could use a before_validation callback to strip off any leading and trailing whitespace in content and extract and save the reply-to:
class Micropost < ActiveRecord::Base
before_validate :process_content, :if => :content_changed?
#...
private
def process_content
# Strip leading/trailing whitespace from self.content
# Extract the reply-to from self.content and save it in self.in_reply_to
end
end
The advantage here is that if the content is changed or created without going through your controller (such as a migration, by hand in the Rails console, some system task that is notifying the users of something, ...), you still get everything done.
I have a model named Post and I created two methods within the model that make changes to fields. The first method's changes get persisted when a save is called. The second method's changes do not get saved. I have noticed this behavior before in other models and I think I'm missing some basic knowledge on how models work. Any help on this would be greatly appreciated!
class Post < ActiveRecord::Base
def publish(user) # These changes get saved
reviewed_by = user
touch(:reviewed_at)
active = true
end
def unpublish() # These changes get ignored.
reviewed_by = nil
reviewed_at = nil
active = false
end
end
EDIT:
Here is a snippet from the controller"
class PostsController < ApplicationController
def publish
if request.post?
post = Post.find(params[:id].to_i)
post.publish(current_user)
redirect_to(post, :notice => 'Post was successfully published.')
end
end
def unpublish
if request.post?
post = Post.find(params[:id].to_i)
post.unpublish()
redirect_to(post, :notice => 'Post was successfully unpublished.')
end
end
...
UPDATE
Problem was solved by adding self to all the attributes being changed in the model. Thanks Simone Carletti
In publish you call the method touch that saves the changes to the database. In unpublish, you don't save anything to the database.
If you want to update a model, be sure to use a method that saves the changes to the database.
def publish(user)
self.reviewed_by = user
self.active = true
self.reviewed_at = Time.now
save!
end
def unpublish
self.reviewed_by = nil
self.reviewed_at = nil
self.active = false
save!
end
Also, make sure to use self.attribute when you set a value, otherwise the attribute will be consideres as a local variable.
In my experience you don't persist your changes until you save them so you can
explicitly call Model.save in your controller
explicitly call Model.update_attributes(params[:model_attr]) in your controller
if you want to save an attribute in your model I saw something like write_attribute :attr_name, value but TBH I never used it.
Cheers