Initializing a class not connected to other classes -- Ruby on Rails - ruby-on-rails

I'm trying to initialize a singular class in my RoR application. This Batch class is not connected to any other class, it is used solely for the Rails API I have set up.
This is the Batch class:
class Batch < ActiveRecord::Base
before_create :access_bucket
def access_bucket
s3 = AWS::S3.new
bucket = s3.buckets['curateanalytics']
bucket.objects.each do |obj|
if obj =~ /swipe batches/i && obj =~ /jpg/i
self.sort_objs(obj.key)
end
end
end
def sort_objs(url)
swipe = url.split("swipe batches/").last
batch_id = url.split("swipe batches/")[1]
folder = swipe.split("/")[0]
self.initialize(batch_id, folder, url)
end
def initialize()
batch = Batch.new
batch.batch_id = batch_id
batch.folder = folder
batch.url = url
batch.save!
end
end
I honestly had no idea where to go so I created a before_create :create_batch method in my User class:
class User < ActiveRecord::Base
has_one :like
has_one :outfit
has_one :wardrobe
before_create :create_batch
after_create :create_wardrobe, :create_outfit, :create_like
serialize :preferences
def self.from_omniauth(auth_hash)
where(auth_hash.slice(:provider, :uid)).first_or_initialize.tap do |user|
user.curate_user_id = "curate"+rand(9).to_s+rand(9).to_s+rand(9).to_s+
rand(9).to_s+rand(9).to_s
user.provider = auth_hash.provider
user.uid = auth_hash.uid
user.name = auth_hash.info.name
user.email = auth_hash.info.email
user.image = auth_hash.info.image
user.oauth_token = auth_hash.credentials.token
user.oauth_expires_at = Time.at(auth_hash.credentials.expires_at)
user.preferences = { height: nil, weight: nil, age: nil, waist_size: nil, inseam: nil, preferred_pants_fit: nil, shirt_size: nil, preferred_shirt_fit: nil, shoe_size: nil}
user.save!
end
end
private
def create_batch
#batch = Batch.new
#batch.save!
end
end
When I ran the server I received the message that the stack was too deep. Am I wrong in thinking that this path should access the Batch class and the Batch.access_bucket method which would then lead to the initialize method?

Delete initialize method in the Batch class.
When you call new on a Class, it instantiates an object and call initialize on it. So when you call Batch.new in create_batch method of your User class, the initialize method of you Batch class is called. The problem is that Batch#initialize method calls Batch.new inside it, so another Batch#initialize is invoked, which calls Batch.new inside it, which again calls another Batch#initialize, and infinite cycle of Bathc.new and Batch#initialize follows.

Related

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

Access varible in ruby after initialize

I am trying to access variable in ruby after initialize, but i didn't get that variable , anything wrong in that?
class Test
def initialize(params)
#has_test = params[:has_test]
#limit_test = params[:limit_test]
end
def self.method1(params)
Test.new(params)
#can i get that two instance variable
end
end
You should probably set up attribute accessors, then use them this way:
class Test
attr_accessor :has_test
attr_accessor :limit_test
def initialize(params)
#has_test = params[:has_test]
#limit_test = params[:limit_test]
end
def self.method1(params)
t = Test.new(params)
// can i get that two instance variable
// Yes:
// use t.has_test and t.limit_test
end
end
You are mixing an instance and a class method in your example.
If this is really what you want, then you have to define an accessor with attr_reader:
class Test
def initialize(params)
#has_test = params[:has_test]
#limit_test = params[:limit_test]
end
attr_reader :has_test
attr_reader :limit_test
def self.method1(params)
obj = Test.new(params)
p obj.has_test
p obj.limit_test
end
end
Test.method1(has_test: 1, limit_test: 3)
It the instance/class-method is a mistake, then this example may help you:
class Test
def initialize(params)
#has_test = params[:has_test]
#limit_test = params[:limit_test]
end
def method1()
p #has_test
p #limit_test
end
end
obj = Test.new(has_test: 1, limit_test: 3)
obj.method1
If you define also the accessors like in the first code, then you have again access from outside the class.
Just in case you don't want a reader, see also Access instance variable from outside the class

Rails 4 create associations in class method

This works perfectly fine:
User.first.social_profiles.create!
On the other hand, this creates the social_profile but does not create the association between the two:
class SocialProfile < ActiveRecord::Base
def self.create_google( auth_info )
# if where(provider: auth_info["provider"], uid: auth_info["uid"]).empty?
create! do |google|
google.provider = auth_info["provider"]
google.uid = auth_info["uid"]
google.image_url = auth_info["info"]["image"]
google.email = auth_info["info"]["email"]
google.access_key = auth_info["credentials"]["token"]
google.refresh_token = auth_info["credentials"]["refresh_token"]
google.expires_at = Time.at(auth_info["credentials"]["expires_at"])
google.expires = auth_info["credentials"]["expires"]
end
# else
# where(provider: auth_info[:provider], uid: auth_info[:uid]).first
# end
end
end
Console:
2.1.2 :102 > User.first.social_profiles.create_google( ...the auth hash ...)
What's the problem here? How can I fix it?
This does work though
p = User.first.social_profiles.create_google(...the auth hash ...)
User.first.social_profiles << p
The User.first instance does not get carried into the SocialProfile.create_google method, and therefore the create! method wouldn't have the user instance available.
You can assign it yourself by passing it in:
class SocialProfile < ActiveRecord::Base
def self.create_google( user, auth_info )
create! do |google|
google.user_id = user.id,
...
end
end
end
And calling it with
SocialProfile.create_google( User.first, auth_info )
Alternatively, consider having the create_google_profile method in User, so that you can
class User < ActiveRecord::Base
def create_google_profile( auth_info )
self.social_profiles.create(
provider: auth_info["provider"],
...
)
end
end
and calling it with
User.first.create_google_profile( auth_info )

How can I work with this Module/Class setup?

I have the following module/class:
module Pigeons
class FedEx
attr_accessor :signature_name
def initialize(account)
self.account = account
end
def response(number)
body = "...some xml..."
return HTTParty.post('http://example.com', :body => body)
end
def track(number)
details = response(number)
self.signature_name = details[:delivery_signature_name]
end
end
end
What I'd like to be able to do is this:
#fedex ||= Pigeons::FedEx.new('123abc')
tracker = fedex.track('1234567890')
tracker.signature_name
Everything is working up until the tracker.signature_name part, which throws an undefined method 'signature_name' for nil:NilClass error.
The problem is in this line:
self.signature_name = details[:delivery_signature_name]
details[:delivery_signature_name] turns out to be nil, which is then assigned to self.signature_name and then becomes return value of track method. And here
tracker = fedex.track('1234567890')
tracker.signature_name
tracker will be nil and you try to call a method on it.
You probably meant to write this instead:
def track(number)
details = response(number)
self.signature_name = details[:delivery_signature_name]
self # <== here
end
You need two methods called signature_name= and response that do whatever they are supposed to do in your class.

Rails Active Record Instance Variables

My questions is in regards to this AR and its instance variable #saved
class PhoneNumber < ActiveRecord::Base
has_one :user
validates_presence_of :number
def self.create_phone_number( user, phone_hash )
#new_phone = PhoneNumber.new(phone_hash)
#user = user
PhoneNumber.transaction do
#user.phone_numbers << #new_phone
#new_phone.save!
#user.save!
end
#saved = true
return #new_phone
rescue ActiveRecord::RecordInvalid => invalid
#saved = false
return #new_phone
end
def saved?
#saved ||= false
end
end
It is my understanding that the instance variables will keep their values through the existence of the instance.
When using this AR in my controller, saved? always returns false..
#phone_number = PhoneNumber.create_phone_number(#active_user, params[:phone_number])
puts "add_phone_number"
if #phone_number.saved? => always false
What am I missing in regards to these instance variables? Thank you
you're using the instance variable #saved inside a class method, the #saved var then belongs to the class, not to its instances, so you cannot call it from an instance method like #saved?.
what you can do, is, at the top of the class add:
attr_accessor :saved
and inside the create_phone_number method, replace:
#saved = true
with:
#new_phone.saved = true
then it should work

Resources