Setting multiple model attributes at once via loop - ruby-on-rails

Im using virtual attributes to concat and form a address before i save the user. So when they click edit user i would like to populate the fields in the form again. Every time i try to assign them they come back nil?
This is what i call from devise registrations controller before_action edit:
def test
resource.populate_address_attributes
end
and here is the method im trying to work with:
def populate_address_attributes
if address == nil || address == ""
return false
else
attributes = address.split(",")
[self.number, self.street_name, self.area, self.postcode, self.state].each { |x| x = attributes.delete_at[0]}
end
end
all i'm getting is this:
=> [nil, nil, nil, nil, nil]
maybe i'm trying to make it to complicated?

When you are passing [self.number, self.street_name] etc you are passing the value of those attributes (which are nil and hence immutable).
Try this
def populate_address_attributes
if address == nil || address == ""
return false
else
attributes = address.split(",")
[:number, :street_name, :area, :postcode, :state].each_with_index do |field, index|
self.public_send("#{field}=", attributes[index])
end
end
end

Related

Get params sent by a form in my model for validate

In my model, I need to check if the value is sent to validate it,
In my validation part, I have :
validate :validate_book, if: ->(book) { book.author.use_library? }
Then I have
def book_title_value=(title)
self.title = title.blank? ? nil : find_book(title)
end
def book_title_value
book.title
end
def find_book(title)
Book.where(title: title).first
end
def book_title_is_blank?
return true if book_title_value(title).blank? # At this point title is always nil, same with book_title_value
false
end
def validate_book
errors.add(:book, :invalid) if book_title_is_blank? #it return the errors
end
In my part book_title_is_blank? how can I get the value passed in the form to check if its blank or not ?
I dont get it because in my book_title_value=(title) I always have the value but not in the other method

Stack level too deep on user.save

I want to assign a confirmation code to my users while creating one. And I also titleize some columns before saving-updating them. So my user.rb looks like this (it may be a bit messy):
// user.rb
*** some code ***
before_save { titleize_column(:name)
titleize_column(:surname)
capitalize_column(:complaints)
capitalize_column(:education)
capitalize_column(:job)
capitalize_column(:complaintsdetails)
capitalize_column(:prediagnosis)
capitalize_column(:existingdiagnosis)
capitalize_column(:knownilnessesother)
capitalize_column(:usedmedicine)
capitalize_column(:operation)
capitalize_column(:trauma)
capitalize_column(:allergy)
capitalize_column(:otherhabits)
capitalize_column(:motherother)
capitalize_column(:fatherother)
capitalize_column(:siblingsother)
}
before_save :generate_confirmation_code
protected
def generate_confirmation_code
unless self[:confirmed]
if(self[:type] == 'Patient')
update_attribute :confirmation_code, SecureRandom.urlsafe_base64(20)
update_attribute :confirmed, false
else
update_attribute :confirmed, true
end
end
end
protected
def capitalize_column(attr)
unless self[attr].nil?
self[attr] = Unicode::capitalize self[attr]
end
end
protected
def titleize_column(attr)
unless self[attr].nil?
words = self[attr].split
words.each_with_index do |v,i|
words[i] = Unicode::capitalize v
end
self[attr] = words.join(" ")
end
end
I'm using separate methods for titleizing and capitalizing columns because they may be nil when first creating a user, so I'm checking if it is null or not in those methods. This structure works fine on a normal signup with strong parameters. However, if I try to use twitter signup with the method below, it gives me the error 'stack level too deep' and I can see that it calls the generate_confirmation_code 123 times from the application trace and then these happens:
app/models/user.rb:83:in each'
app/models/user.rb:83:ineach_with_index'
app/models/user.rb:83:in titleize_column'
app/models/user.rb:20:inblock in '
app/models/user.rb:64:in generate_confirmation_code' (x123 times)
app/models/user.rb:101:infrom_omniauth'
app/controllers/socials_controller.rb:4:in `create'
// method for signing up/logging in a user from twitter
class << self
def from_omniauth(auth_hash)
if exists?(uid: auth_hash['uid'])
user = find_by(uid: auth_hash['uid'])
else
user = find_or_create_by(uid: auth_hash['uid'], provider: auth_hash['provider'], type: 'Patient')
user.password_digest = User.digest('111111')
user.name = auth_hash['info']['name']
user.location = get_social_location_for user.provider, auth_hash['info']['location']
user.avatar = auth_hash['info']['image']
user.url = get_social_url_for user.provider, auth_hash['info']['urls']
user.save! // THIS IS THE LINE 101!
conversation = Conversation.create()
user.conversation = conversation
admin = Admin.first
admin.conversations << conversation
user.progress = Progress.create(active_state:1)
end
user
end
I think I'm messing up by using before_save not properly, but do not know how to do it right. What am I doing wrong here?
update_attribute also fires the save callbacks, thereby looping the before_save infinitely, thus producing stack level too deep.
You can just simply assign values in a before_save callback methods, because they will simply be saved afterwards anyway. See the following:
def generate_confirmation_code
unless self[:confirmed]
if(self[:type] == 'Patient')
self.confirmation_code = SecureRandom.urlsafe_base64(20)
self.confirmed = false
else
self.confirmed = true
end
end
end
You are calling update_attribute inside before_save callback method, instead you can just assign values to attributes. The method signature generate_confirmation_code should be like below -
def generate_confirmation_code
unless self[:confirmed]
if(self[:type] == 'Patient')
self.confirmation_code = SecureRandom.urlsafe_base64(20)
self.confirmed = false
else
self.confirmed = true
end
end
end

How to mimic asp.net get set in rails

I am trying to mimic asp.net get{} set{} in rails, here is what i tried in my controller:
def get_segment=(segment)
if params[:s] != nil
segment = params[:s]
else
segment = "personal"
end
end
Then i am trying to access it like this:
#something = get_segment
But it always returns as nil.
How can i do this?
Thanks
Why are you using get segment=(segment)?
look like what you are wanting to do is test params[:s], so the = is uncessary, as is the segment parameter.
def get_segment
if params[:s] != nil
params[:s]
else
"personal"
end
end
I think this would give you what you want.
If you just want to mimic get{} set{} in C#, the property Segment
private string _segment;
public string Segment {
get { return _segment; }
set { _segment = value; }
}
is written as followed in Ruby:
# get
def segment
#segment
end
# set
def segment=(value)
#segment = value
end
# if you don't have additional logic, you can just write
attr_accessor :segment
Then you can use some_instance.segment to retrieve the value and some_instance.segment = some_value to modify the value.
According to your code sample above, you want to fetch s parameter with a default value if it doesn't exist. You should define a getter, not in the setter form as you have provided.
def get_segment # or just "segment"
params[:s] || "personal"
end

Saving URL params in a session (rails)

I'm trying to save the url params from ever page, into a session, so if someone fills out a form after navigating away from their landing page it keeps the campaign id. I've managed to make it work page by page, so if they land on the form with params it keeps it, but if they navigate away obviously it doesn't. I currently have:
Controller:
def campaign
if params[:campaign]!= nil
session[:campaign] = params[:campaign]
end
end
def post
rif = Registerinterest.find(:all, :conditions => ["reference = ?", session[:campaign]])
if rif.count == 0
post["Campaign_ID"] = "701D00000001111"
else
post["Campaign_ID"] = rif.first.campaign_id
end
end
It worked when i used params, but not session, so i'm assuming i'm not saving it properly?
You could add a before filter in controllers/application_controller.rb
class ApplicationController < ActionController::Base
before_filter persist_campaign_session
def persist_campaign_session
session[:campaign] ||= params[:campaign]
end
end
If session[:campaign] is set it will be used, otherwise params[:campaign]will be used. You should implement
the reset of session[:campaign] at a good place.
I put this in my application layout:
- if params[:campaign] != nil || params[:campaign] != ""
- session[:campaign] == params[:campaign]
and then that made the controller work properly:
def post
rif = Registerinterest.find(:all, :conditions => ["reference = ?", session[:campaign]])
if rif.count == 0
post["Campaign_ID"] = "701D00000001111"
else
post["Campaign_ID"] = rif.first.campaign_id
end
end

inject method to retrieve data from hash

I'm having trouble getting the method below in my user model to handle a hash ('auth') I'm getting from LinkedIn for user signin:
def self.deep_get auth, *fields
auth.inject(auth) { |acc, e| acc[e] if acc }
end
I call the 'deep_get' method later in my user model as I create a user using omniauth/linkedin gem. However, it's returning nil values for the provider/uid/headline/email user fields that I know are not nil.
I included first_name and last_name fields as an example because this approach is working (not returning nil values), but (as I realize) bad style/exception handling. Any ideas as to why my deep_get inject method isn't working to retrieve the data in the hash as I'd like it to?
def self.create_from_omniauth(auth)
create! do |user|
# i'd like to retrieve user information from linkedin per the following with my inject method, but i am getting nil values when i should be getting data.
# :provider and :uid are on the same branch level of data. first_name,last_name,email,etc. are on a branch just below called 'info'
user.provider = deep_get(auth, :provider)
user.uid = deep_get(auth, :uid)
user.headline = deep_get(auth, :info, :headline)
user.email = deep_get(auth, :info, :email)
# the below is working but i know pokemon exception handling is not good style.
begin
user.first_name = auth["info"]["first_name"]
rescue
end
begin
user.last_name = auth["info"]["last_name"]
rescue
end
try this
def deep_find(obj,key)
if obj.respond_to?(:key?) && obj.key?(key)
obj[key]
elsif obj.respond_to?(:each)
r = nil
obj.find{ |*a| r=deep_find(a.last,key) }
r
end
end
or try this
class Hash
def deep_fetch(key, default = nil)
default = yield if block_given?
(deep_find(key) or default) or nil
end
def deep_find(key)
if key?(key)
self[key]
else
self.values.inject(nil) do |memo, v|
memo = v.deep_find(key) if v.respond_to?(:deep_find)
memo unless memo.nil?
end
end
end
end

Resources