Just to show you how I got to this point:
Every user has many profiles. Every profile has type recognized by single table inheritance(amateur, professional, and some other). I need to store current_profile somewhere and somehow.
Professionals Controller
class ProfessionalsController < ApplicationController
def create
#professional = Professional.new(professional_params)
#user = current_user
#professional.user_id = current_user.id
#update_current_profile = User.update(#user, {:current_profile => #professional.id})
if #professional.save
...
else
...
end
end
private
def professional_params
params.require(:professional).permit(:id, :username, :user_id)
end
end
This is meant to update current_profile of user to the newly created professional profile and do some staff then.
When the profile is created current_profile is set(updated) to NULL. If I change :
#update_current_profile = User.update(#user, {:current_profile => #professional.id})
to something different, for example:
#update_current_profile = User.update(#user, {:current_profile => #professional.user_id})
or
#update_current_profile = User.update(#user, {:current_profile => 3})
it stores data in User.current_profile perfectly.
I was trying #professional without an .id too. Why is this doing so?
Another question is. Is this the best way to store current_profile of user? Would you recommend me any better/safer/more efficient solution?
Thanks all of you guys.
#professional is a new, unsaved record and therefore does not have an id yet. Can only update the current_profile once the #professional record was saved.
Just reorder the lines a bit and it should work:
def create
#professional = Professional.new(professional_params)
#user = current_user
#professional.user_id = current_user.id
if #professional.save
#update_current_profile = User.update(#user, {:current_profile => #professional.id})
# ...
else
# ...
end
end
Just another tip: You use many instance variables (the one with the #) in this method. I do not know enough about your code, but I would suggest to look if some of them can be replaced with local variables. Rule of thumb: Only use instance variables in controllers when you want to share that variable with another method or the view.
Related
I had a previous question that helped me loop through all users where a certain question is met.
However, I'm realizing I can't hard code that condition. I need to somehow get that data from the submitted form, which doesn't seem to be possible in the mailer.
In other words, I'm trying to loop through all users where the user's state is equal to the home_state of the candidate being entered. Basically when the candidate is created, I want to get the home_state of that candidate, and then loop through all users, and for each user that has same state as that candidate, I want to send them the email via this mailer.
Here's my candidate_mailer.rb file
class CandidateMailer < ApplicationMailer
default from: 'wesleycreations#gmail.com'
def self.send_request(row)
#candidate = Candidate.new(candidate_params) # if I can access this here, how to I create the
# following array?
emails = []
User.where(state: #candidate.home_state).each do |u|
emails << u.email # To insert the user email into the array
end
emails.each do |email|
new_request(email,row).deliver_now
end
end
def new_request(email, row)
#candidate = row
mail(to: email, subject: 'New request')
end
end
But the
#candidate = Candidate.new(candidate_params)
obviously doesn't work because the params aren't available in the mailer.
Here in the candidates_controller.rb I have this
def create
#candidate = Candidate.new(candidate_params) #of course here I can access params
if #candidate.save
row = #candidate
CandidateMailer.send_request(row)
else
render('new')
end
end
SO the question is, how do I access params in rails mailer? And if I can't, then how do I refactor my code so that the lines that check if the user meets certain condition is done in the controller?
I was able to figure this out by doing this. after I saved the candidate, I saved the candidate to a global variable. and THEN I send the mailer.
def create
#candidate = Candidate.new(candidate_params)
if #candidate.save
row = #candidate
$candidate = #candidate
end
CandidateMailer.send_request(row)
else
end
end
This way the mailer had access to the new candidate that been created, and I was able to check my condition in there.
So in my mailer, when I use $candidate.home_state, it returned the correct state, mail went out, and made me very happy :)
emails = []
User.where(state: $candidate.home_state).each do |u|
emails << u.email # To insert the user email into the array
end
I can't seem to figure out how to accomplish what I am trying to do here on my create method.
What I have right now works if there are no values, the item is deleted. However, if 1 or more param values exist, it passes and is saved. Not what I needed. I need an all or nothing scenario. I want to save only if all the permitted keys have their value. params.permit(:name, :description, :copyright)
Before an entry is saved using organizations.save!, I need to make sure none of the params that are permitted are nil or empty.
I search all over and can't seem to narrow down on an answer to my exact issue.
Here is my code:
class OrganizationsController < ApplicationController
def index
query_params = params.permit(:id, :name,)
if query_params.blank?
organizations = Organization.all
else
organizations = Organization.where(query_params)
end
render json: organizations, root: "organizations"
end
def create
organizations = Organization.new(organization_params)
if organization_params.present?
organizations.delete
else
organizations.save!
render json: organizations
end
end
private
def organization_params
params.permit(:name, :description, :copyright)
end
end
You should add validations to your model.
From your question i understand that you want to save details only if you get values in all the field, if not you don't want to save, right?. If yes, then adding validations to your model will give you what you wanted.
Add the following to your organization model
validates_presence_of :name
validates_presence_of :description
validates_presence_of :copyright
by doing so, the user won't be allowed to save the details unless and until all three fields have some value in it.
There is no need to use delete as the incomplete information will not be saved.
for more and advanced info click here
To check none of the values of organization_params hash is empty, you can do something like this:
organization_params.values.all? { |x| !x.empty? }
or, this:
organization_params.all? { |k,v| !v.empty? }
You can also check if any param value is empty:
organization_params.any? { |k,v| v.empty? }
So, your create method can be re-written as:
def create
organizations = Organization.new(organization_params)
if organization_params.any? { |k,v| v.empty? }
# at least one param is empty, so delete the record
organizations.delete
else
# all the params values are present, so save the record
organizations.save!
render json: organizations
end
end
Background
The Entity class is a base class that gets inherited by several subclasses that holds entities received over a REST API. The entity classes are immutable and should return a new instance of themselves whenever a change is attempted.
The Entity class has an .update() method that takes a hash of values to update, if the changes aren't really changes it returns itself and if there are real changes it returns a new instance of itself with the changes effected before instantiation.
To be user friendly Entity also allows for direct assignment to properties (so that if a subclass of Entity has a name attribute you can do instance.name = 'New Name') that also returns a new instance of the class. This is implemented in terms of update using dynamic methods that are created when the class is instantiated.
And they are the problem.
Problem
The code in the Entity class looks, in part, like this (for a complete code listing and tests check out the Github repo: https://github.com/my-codeworks/fortnox-api.git):
require "virtus"
require "ice_nine"
class Entity
extend Forwardable
include Virtus.model
def initialize( hash = {} )
super
create_attribute_setter_methods
IceNine.deep_freeze( self )
end
def update( hash )
attributes = self.to_hash.merge( hash )
return self if attributes == self.to_hash
self.class.new( attributes )
end
private
def create_attribute_setter_methods
attribute_set.each do |attribute|
name = attribute.options[ :name ]
create_attribute_setter_method( name )
end
end
def create_attribute_setter_method( name )
self.define_singleton_method "#{name}=" do | value |
self.update( name => value )
end
end
end
Doing this:
instance.update( name: 'New Name' )
and this:
instance.name = 'New Name'
Should be the same, literally since one is implemented in terms of the other.
While .update() works perfectly the .attr=() methods return the value you assign.
So in the above example .update() returns a new instance of the Entity subclass but .attr=() returns 'New Name' ...
I have tries capturing the output inside the .attr=() method and log it before returning so that I have this:
self.define_singleton_method "#{name}=" do | value |
p "Called as :#{name}=, redirecting to update( #{name}: #{value} )"
r = self.update( name => value )
p "Got #{r} back from update"
return r
end
And the log lines say:
"Called as :name=, redirecting to update( name: 'New Name' )"
"Got #<TestEntity:0x007ffedbd0ad18> back from update"
But all I get is the string 'New Name'...
My forehead is bloody and no posts I find show anything close to this. I bet I'm doing something wrong but I can't find it.
Getting dirty
The Github repo has tests in rspec that you can run, the failing ones are focused right now and some extra logging is in the Entity class to capture the different internal steps.
Comments, links and/or pull requests are welcome.
Turns out that the = methods always return the value being assigned.
o = Struct.new(:key).new(1)
o.define_singleton_method("something") { #something }
o.define_singleton_method("something=") do |v|
#something = v
return 6
end
As you can see, I've 'fixed' the return value to 6 each time something= is called. Let's see if it works:
o.something = 1 #=> outputs 1, not 6
o.something #=> outputs 1, so the method did indeed run
Conclusion? My guess is that an = method will return the value that you are assigning through it. And IMO it's better this way; one reason would be to ensure proper functioning of assignment chains:
new_val = o.something = some_val
I'm submitting a simple form with variables that are named in the database.
I am trying to:
Store the submitted variables in the database (which works fine)
Run a calculation, then store that value into the database
No matter what I try I either get an error or 'nil' (upon #kcmil.inspect) as my result for #kcmil. I'm assuming in my current code i'm not passing the variables to the model, but it doesn't work even when it's in the controller.
I'm at a loss here. My variables that are submitted in the form store just fine as expected. I just want to be able to use submitted variables from a form (that are also database items that get stored upon submission) and before saving to the database (or after, should it matter?) run a calculation and store the result in a database item (that is not previously called in or saved from the form). Does that make sense? Any help or hints are GREATLY APPRECIATED!!
Here are my current calculators_controller create and edit actions:
def create
#calculator = Calculator.new(calc_params)
if #calculator.save
flash[:notice] = "Calculation created successfully."
redirect_to(:action => 'index')
else
render('new')
end
end
def update
#calculator = Calculator.find(params[:id])
if #calculator.update_attributes(calc_params)
flash[:notice] = "Calculation updated successfully."
redirect_to(:action => 'index', :id => #calculator.id)
else
render('edit')
end
end
private
def calc_params
params.require(:calculator).permit(:subsection, :amps, :volts, :distance, :vdrop, :phase, :kcmil, :section_id)
end
Here's my model
class Calculator < ActiveRecord::Base
before_create :kcmil_calc
def kcmil_calc
if #phase == 1
self.kcmil = ((13 * #distance.to_i * #amps.to_i ) / #vdrop.to_i).round(2)
else
self.kcmil = ((12.9 * #distance.to_i * #amps.to_i ) / #vdrop.to_i).round(2)
end
end
end
I HAVE IT! I HAVE IT!
before_update :defaults
def defaults
if self.phase == 1
self.kcmil = ((12.9 * distance * amps) / vdrop).round(2)
else
self.kcmil = ((13 * distance * amps) / vdrop).round(2)
end
end
solved it! I had to call self.phase instead of #phase and change before_create to before_update to get it to work . No change in the controller required. Dang - one simple #! I also removed the to_i because it's not needed since my views prevent me from submitting anything other than integers.
controller/makenew.rb
class MakeController < Controller
map '/makenew'
#require 'model/debate'
def debate
if request.post? #this line is potentially dangerous!
#---> 1/3 fetch postdata
data = request.subset(:question, :type, :category, :assertion)
data['user_id'] = user.id #id = request.params['id']
#---> 2/3 check permissions
if user.points < 40
flash[:error] = 'You don\'t have enough points to make a debate.'
redirect_referrer
else
debate = Debate.new
end
#---> 3/3 modify database
begin
debate.save(data)
flash[:success] = success
flash[:form_data] = debate
redirect 'debates'
rescue => e
Ramaze::Log.error(e)
#flash[:form_errors] = debate.errors
#flash[:error] = data
flash[:error] = e
#flash[:error] = 'Failure whilst saving. Contact technical support!'
redirect 'debates' #redirect_referrer
end
#|
end #closes posting conditional
end #closes makesave
end
The error I get is.
SQLite3::ConstraintException: debates.question may not be NULL
I have checked the postdata for data.question and it is not null.
What is going on?
You need to pass 'data' to #update. Thus:
debate.save(data)
is wrong, you have to do:
debate.update(data)
debate.save
If you don't do this, your debate object has no member assigned and thus its question member is nil, violating your DB constraints.
See the differences between #save and #update here:
Update : http://sequel.rubyforge.org/rdoc/classes/Sequel/Model/InstanceMethods.html#method-i-update
Save : http://sequel.rubyforge.org/rdoc/classes/Sequel/Model/InstanceMethods.html#method-i-save
In a nutshell: #save will save the current model instance to the database, while #update will change a bunch of instance attributes in one operation.
But you have to remember that changing a model instance's attributes DOES NOT write them to the database. You always have to call #save explicitly.
Are you sure that your model accepts mass assignment of primary keys?
Try calling Debate.unrestrict_primary_key
You can check the rules in the Sequel documentation.