How should I refactor create_unique methods in Rails 3? - ruby-on-rails

I have following ugly create_unique method in few models ex:
def self.create_unique(p)
s = Subscription.find :first, :conditions => ['user_id = ? AND app_id = ?', p[:user_id], p[:app_id]]
Subscription.create(p) if !s
end
And then in controllers #create actions I have
s = Subscription.create_unique({:user_id => current_user.id, :app_id => app.id})
if s
raise Exceptions::NotAuthorized unless current_user == s.user
#app = s.app
s.destroy
flash[:notice] = 'You have been unsubscribed from '+#app.name+'.'
redirect_to '/'
end

did you try dynamic finders ?
find_or_initialize_by_user_id_and_app_id
find_or_create_by_user_id_and_app_id
first_or_initialize...
first_or_create....
check manual http://guides.rubyonrails.org/active_record_querying.html#dynamic-finders
also option is to create validation rule for check unique value
class Subscription < ActiveRecord::Base
validates_uniqueness_of :user_id, :scope => :app_id
end
then
sub = Subscription.new({:user_id => current_user.id, :app_id => app.id})
sub.valid? #false

You can use validates_uniquness_of :app_id,:scope=>:user_id so app id is uniq for respected user_id

Related

Rails Strong Parameters - create action with foreign key parameters

I have three models, Server has many maintenances and Maintenance belongs to Server and User.
create_table :maintenances do |t|
t.references :server, foreign_key: true
t.references :user, foreign_key: true
t.string :name
t.text :content
end
In console I can create records as follows:
Server.create(:hostname => "Sissy", :description => "Webserver")
Maintenance.create(:server_id => 1, :user_id => 1, :name => "Test", :content => "Test" )
My Question is: How can I do this in my Controller create action?
Problem is that :user_id is not part of the maintenance params hash, so if I write
def create
#server = Server.find(params[:id])
#maintenance = #server.maintenances.create!(maintenance_params)
end
private
def maintenance_params
params.require(:maintenance).permit(:user_id => current_user.id,
:id,
:name,
:content)
end
I'm getting
Syntax error, unexpected ',', expecting =>
...ser_id => current_user.id, :id, :name, :content, :start, :pl...
... ^):
app/controllers/maintenances_controller.rb:41: syntax error, unexpected ',', expecting =>
You can add , user_id inside your create action itself. Try this.
def create
#server = Server.find(params[:id])
params[:maintenance][:user_id] = current_user.id
#maintenance = #server.maintenances.create!(maintenance_params)
end
A good way is by passing a block to the create! method:
#maintenance = #server.maintenances.create!(maintenance_params) do |m|
m.user = current_user
end
The record is yielded to the block (before it is validated/saved).
This also works with new, create, update and update!.
But you should consider if you should be using the bang method create! here as it will raise an uncaught ActiveRecord::RecordNotValid error if any of the validations fail.
def create
#server = Server.find(params[:id])
#maintenance = #server.maintenances.new(maintenance_params) do |m|
m.user = current_user
end
if #maintenance.save
redirect_to #maintenance
else
render :new
end
end
The ActiveRecord::Persistence bang methods should only really be used in things like seed files or where a record not passing the validations is an exceptional event.
Yes, you can't do anything like that in strong_params method. Nor is it its purpose. Separate the whitelisting and default params.
I usually do it like this:
def create
#server = Server.find(params[:id])
#maintenance = #server.maintenances.create!(maintenance_params.merge(user_id: current_user.id))
end
private
def maintenance_params
params.require(:maintenance).permit(:id, :name, :content)
end

Ruby-On-Rails model level class array

I'm trying to think of a best solution for following scenario. I've a model called an 'Article' with an integer field called 'status'. I want to provide class level array of statuses as shown below,
class Article < ActiveRecord::Base
STATUSES = %w(in_draft published canceled)
validates :status, presence: true
validates_inclusion_of :status, :in => STATUSES
def status_name
STATUSES[status]
end
# Status Finders
def self.all_in_draft
where(:status => "in_draft")
end
def self.all_published
where(:status => "published")
end
def self.all_canceled
where(:status => "canceled")
end
# Status Accessors
def in_draft?
status == "in_draft"
end
def published?
status == "published"
end
def canceled?
status == "canceled"
end
end
So my question is if this is the best way to achieve without having a model to store statuses? And secondly how to use these methods in ArticlesController and corresponding views? I'm struggling to understand the use of these methods. To be specific, how to do following?
article = Article.new
article.status = ????
article.save!
or
<% if article.in_draft? %>
<% end %>
I greatly appreciate any sample code example. I'm using rails 4.0.0 (not 4.1.0 which has enum support).
You could define all the methods using define_method, and use a hash instead of an array:
STATUSES = {:in_draft => 1, :published => 2, :cancelled => 3}
# Use the values of the hash, to validate inclusion
validates_inclusion_of :status, :in => STATUSES.values
STATUSES.each do |method, val|
define_method("all_#{method)") do
where(:status => method.to_s)
end
define_method("#{method}?") do
self.status == val
end
end
In that way, you can add statuses in the future without needing to create the methods manually. Then you can do something like:
article = Article.new
article.status = Article::STATUSES[:published]
...
article.published? # => true

How do you get recurring PaypalExpress payments to work with Active Merchant in Rails 4?

I'm using Active Merchant to try to implement recurring payments in the PaypalExpressCheckout module, but I keep on getting this error:
"message"=>"The token is invalid", "error_codes"=>"11502"
The token is being properly returned and set and now I'm speculating the problem maybe because the Active Merchant gem was not written for PaypalExpress to work with recurring payments, but after looking at the documentation there are methods included from the PaypalRecurringApi that can be used in the PaypalExpress module.
This is the relevant code that is causing the problem.
In my paypal_subscriptions_controller.rb:
def create
#subscription = PaypalSubscription.new(subscription_params)
if #subscription.save
if #subscription.subscribe_with_paypal
render "success"
else
render "failure"
end
else
render :new
end
end
private
def subscription_params
params.require(:paypal_subscription).permit(:express_token,:payer_id,:email,:first_name,:last_name,:amount,:plan_id)
end
And the relevant code in my model PaypalSubscription.rb:
def express_token=(token)
write_attribute(:express_token, token)
if new_record? && !token.blank?
details = EXPRESS_GATEWAY.details_for(token)
self.email = details.params["email"]
self.payer_id = details.payer_id
self.profile_id = details.params["profile_id"]
self.first_name = details.params["first_name"]
self.last_name = details.params["last_name"]
self.amount = details.params["amount"]
end
end
def subscribe_with_paypal
response = process_purchase
Rails.logger.info response.inspect
response.success?
end
def process_purchase
EXPRESS_GATEWAY.recurring(amount, nil,{
:ip => ip_address,
:token => express_token,
:payer_id => payer_id,
:period => "Month",
:frequency => 3,
:start_date => Time.now,
:description => "Checking recurring auto-renewal"
})
end
If anyone could help me on this on that'd be great! Thanks

Before_destroy filter now calling given method

My User's have one Group assigned each. If admin removes they group they are in i need to move them down to most basic gorup "Member" that cannot be removed.
Now i know that the method works and routing and controller however because at first i called the method moveUsers from controller in order:
user = User.find(id)
user.moveUsers
user.delete
However i want to seperate this and keep controller minimalistic, the: Rails3 way?
Controller method:
def destroy
group = Group.find(params[:id])
if group.delete
redirect_to groups_url, :notice => "Group deleted."
else
redirect_to :back, :notice => "Cannot delete group."
end
end
Model:
class Group < ActiveRecord::Base
before_destroy :moveUsers
validates :title, :presence => :true, :uniqueness => true
has_many :user
# Get the number of users associated with the group & title
def getUserCount
return User.where(:group_id => self.id).length
end
# If group is deleted move all of its users
# to core group: Members (id = 1)
private
def moveUsers
users = User.where(:group_id => self.id)
users.each do |user|
user.group_id = 1
user.save
end
end
end
The delete method ignores callback methods
You need to use group.destroy in order to fire the before_destroy callback
http://guides.rubyonrails.org/active_record_validations_callbacks.html#skipping-callbacks

Rails: validations not enforced, call manually?

I check for
validates :group_id, :presence => true
in my model "project". Yet when I create a project without a group_id, I don't get a validation-error. If I try to do
p = Project(:name => "Test")
p.save
the rails console returns false, with save! it tells me the validation for group_id failed. So the validation is somehow performed on the save method, yet it isn't by default in my app. Do I have to do a manual
if #project.save == true
...
end
or something in my controller?
Thank you,
B
You can check #project.valid? before save.
def signup
if request.post?
#user_data = UserData.new(:login => params[:user], :password => params[:password])
if #user_data.valid?
if #user_data.save
session[:cuser] = UserData.authenticate(#user_data.login, #user_data.password).login
redirect_to(:controller=> 'sync', :action=> 'all')
return
end
else
#error_on_signup = true
end
end

Resources