As the title says, my action is being fired twice when clicked a single time.
The action is just meant for copying a model then saving the copied model.
module RailsAdmin
module Config
module Actions
class CopyAction < RailsAdmin::Config::Actions::Base
RailsAdmin::Config::Actions.register(self)
register_instance_option :member do
true
end
register_instance_option :http_methods do
[:get]
end
register_instance_option :controller do
proc do
if request.get? # EDIT
#newObject = #object.dup
objectNameCopy = #object.name + "_copy_"
#queues = Filter.where('name LIKE ?',"%#{objectNameCopy}%")
x = 1
#queues.each do |q|
x=x+1
end
#newObject.name = #newObject.name + "_copy_" + x.to_s
#newObject.key = #newObject.key + "_copy_" + x.to_s
if #newObject.save!
respond_to do |format|
format.html { redirect_to_on_success }
end
else
#newObject.errors.full_messages.each do |message|
flash.now[:error] = message
end
end
end
end
end
register_instance_option :link_icon do
'fa fa-copy'
end
end
end
end
end
I have noticed that by entering the URL manually, it works as intended.
When clicking the icon to run this action, it opens a URL with a # at the end. I've not a clue where this could be coming from.
As a #max said in the comments its probably a turbolinks issue, try disabling it for your action like this
module RailsAdmin
module Config
module Actions
class CopyAction < RailsAdmin::Config::Actions::Base
RailsAdmin::Config::Actions.register(self)
# ADD THIS
register_instance_option :pjax? do
false
end
end
end
end
end
Related
I would like to silence the logs of a particular controller under a specific condition. For #product there is a #product.sensitive (boolean) attribute. I would like the #show-controller to not log anything if #product.sensitive == true. This is how it is solved for silencing all logs without a condition (which works fine for me):
def show
Rails.logger.silence do
if #product.price > 0
# Do this
else
# Do that
end
end
end
But what if I now want the Rails.lgger.silence to be conditional of #product.sensitive == true ? I can't do:
def show
if #product.sensitive
Rails.logger.silence do
end
if #product.price > 0
# Do this
else
# Do that
end
end
if #product.sensitive
end
end
end
So, wow do I solve this? I guess there are two different solutions: either there is a way to activate the loop only under the condition that #product.sensitive == true or by simply disabling the logger in any other way (and reactivate it at the end of the controller).
What are my options?
You could throw the "real" code in a lambda and run it inside/outside Rails.logger.silence as appropriate:
def show
the_real_work = -> do
if #product.price > 0
# Do this
else
# Do that
end
end
if #product.sensitive
Rails.logger.silence(&the_real_work)
end
the_real_work[]
end
end
or use a separate method:
# This would probably be private in real life.
def the_real_show
if #product.price > 0
# Do this
else
# Do that
end
end
def show
if #product.sensitive
Rails.logger.silence { the_real_show }
# or Rails.logger.silence(&method(:the_real_show))
end
the_real_show
end
end
If #product is (or can be) created in a before_action before #show is called, you could turn things inside out:
# Of somewhere private...
def silence_logging
Rails.logger.silence { yield }
end
def needs_silencing?
action_name == 'show' && #product.sensitive
end
And then hook up the silence.
before_action :whatever_already_loads_the_product
around_action :silence_logging, if: :needs_silencing?
Unfortunately you can't mix :if and :only options since :if overrides :only, otherwise you could say things like if: :sensitive_product, only: :show.
I am newest a Rails Admin and I have written a custom action to toggle a record active with an play & pause icons. I would like to exclude the action in others model.
Here is my custom action
# lib/rails_admin/custom_actions.rb
module RailsAdmin
module Config
module Actions
class MemberAction < RailsAdmin::Config::Actions::Base
register_instance_option :member? do
true
end
register_instance_option :visible do
true
end
end
class ToggleActive < MemberAction
RailsAdmin::Config::Actions.register(self)
register_instance_option :only do
['FirstJob', 'Internship']
end
register_instance_option :http_methods do
[:get, :post]
end
register_instance_option :link_icon do
subject = bindings[:object]
icon = subject.active ? 'icon-pause' : 'icon-play'
end
register_instance_option :controller do
Proc.new do
#object.active = !#object.active
if #object.save!
flash[:notice] = "#{#object.title} is now #{ #object.active ? 'actived' : 'desactivated'}"
redirect_to back_or_index
else
flash[:notice] = "Toggle activation of #{object.title} did not worked"
redirect_to back_or_index
end
end
end
end
end
end
end
I have a model Profile and I want to exclude ToggleActive.
I tried this one :
# config/initializers/rails_admin.rb
RailsAdmin.config do |config|
config.actions do
dashboard
# breadcrumb actions
index
new
export
# selected items actions
bulk_delete
bulk_activate
bulk_desactivate
# inline item actions
show
edit
delete
show_in_app
toggle_active do
except 'Profile'
end
end
end
But it's still loading the ToggleActive action and try to find the method active for Profile. So it raises :
undefined method `active' for #<Profile:0x00007fd9b7932c98>
If I add unless condition, it is working but I am not sure it is the proper way to do this.
# lib/rails_admin/custom_actions.rb
icon = subject.active ? 'icon-pause' : 'icon-play' unless subject.class == Profile
Thanks !!!
I am using fragment caching in my rails 4 project. I have cities controller and city_sweeper
cities_controller.rb
cache_sweeper :city_sweeper, :only => [:update, :destroy]
.
.
def update_featured
#city = City.unscoped.find(params[:id])
if params[:featured]
#city.update_attribute(:featured, params[:featured])
end
render :text => "success:
end
.
end
and in my city_sweeper.rb I have this code
class CitySweeper < ActionController::Caching::Sweeper
observe City
def after_update(city)
expire_cache(city)
end
def after_destroy(city)
expire_cache(city)
end
def after_update_featured(city)
expire_cache(city)
end
def expire_cache(city)
expire_fragment "city_index_#{city.id}"
end
end
its working fine with CRUD operation, but its not working for my custom method.its calling my sweeper.rb , but I am not getting city object there. I am getting this error:
NoMethodError (undefined method `expire_fragment' for #<CitySweeper:0xab9f1e0 #controller=nil>):
You can expire the fragment cache using this
UPDATE
if #cities.present?
#cities.each do |city|
cache(action: 'recent_update',key: "city_index_#{city.id}", skip_digest: true) do
...
end
end
end
In Sweeper
class CitySweeper < ActionController::Caching::Sweeper
observe City
.....
def expire_cache(city)
expire_fragment(controller: 'cities', action: 'recent_update',key: "city_index_#{city.id}")
end
end
I have:
class Competitor < ActiveRecord::Base
after_commit :delayed_create_matching_surveys
end
class Survey < ActiveRecord::Base
after_create :create_matching_results, :touch_review
end
class Result < ActiveRecord::Base
after_update :update_inputs
def update_inputs
if input_id.present?
#input = Input.find(input_id)
if survey.reload.results.sort_by{|r| r.points}.last.selected
competitor.inputs << #input unless competitor.inputs.include?(#input)
else
competitor.inputs.delete(#input) if competitor.inputs.include?(#input)
end
end
binding.pry_remote ## << which exists purely to slow down the app
end
end
I have configured Sidekiq to execute all delayed methods immediately in the test environment. I have also tried removing the delay_ and have confirmed this does not solve the problem.
With this code, the following spec passes:
describe "update_inputs" do
before :each do
#product = create(:product)
#question = create(:question, :product => #product)
#review = create(:review, :product => #product)
#client = #review.competitors.first
#competitor = create(:competitor, :review => #review)
#category = create(:category)
#input1 = create(:input)
#input2 = create(:input)
#competitor.categories << #category
#input1.categories << #category
#input2.categories << #category
end
it "assigns one input to a competitor when one is selected" do
#survey = #competitor.reload.surveys.select{|s| s.input_id == #input1.id}.first
#survey.results.select{|r| r.name.include?("sells this product")}.first.update_attributes :selected => true
#competitor.inputs.should == [#input1]
end
end
All I have to do is type pry-remote and then exit when Rspec gets to binding.pry_remote.
It seems all it's doing is slowing my app down long enough for Rspec to catch up.
But without binding.pry_remote, the spec fails. Without it, #competitor.inputs is [], as is #competitor.reload.inputs and #competitor.inputs(true) and #competitor.reload.inputs(true).
How can I fix this?
I want add any kind of permissions for my rails models just including one module to the model and defining metadata in one database field. How i can do this?
For example:
Folder < AR::B
#permissions_list = [:is_private, :public_on_negotioation]
#permissions_field = :perms
include Permissions
end
module Permissions
"...?"
end
i want to have methods "is_private?", "is_private", "is_private=" for all items in a #permissions_list variable.
So i can use model in this way:
f = Folder.new
f.is_private = true
f.public_on_negotioation = false
f.save
f.reload
f.is_private?
=> true
f.public_on_negotioation?
=> false
so i wrote next Module:
module Permissions
def self.included(mod)
permissions_list = mod.instance_variable_get(:#permissions_list)
permissions_list.each_with_index do |permission, index|
define_method permission.to_sym do
perms_bits[index] == '1'
end
alias_method (permission.to_s << "?").to_sym, permission.to_sym
end
end
def perms_bits
send(self.class.instance_variable_get(:#permissions_field)).to_i.to_s(2).reverse
end
def set_permission(name, weight, options)
permissions_field = self.class.instance_variable_get(:#permissions_field)
if options[name]
self.send("#{permissions_field}=", self.send(permissions_field).to_i + weight.to_i) unless send(name)
elsif options.has_key?("#{name}_off")
self.send("#{permissions_field}=", self.send(permissions_field).to_i - weight.to_i) if send(name)
end
end
def update_perms(options)
permissions_list = self.class.instance_variable_get(:#permissions_list)
permissions_list.each_with_index do |permission, index|
set_permission(permission.to_sym, 2**index, options)
end
save
end
end
some improvements?
To extend the answer from mdesantis. The way you can wrap up the permissions code for reuse could be something like this (untested):
class Folder < ActiveRecord::Base
include Permissions
end
PERMISSIONS_STRUCT = Struct.new(:is_private, :public_on_negotiation)
module Permissions
def self.included(klass)
klass.class_eval do
serialize :permissions, PERMISSIONS_STRUCT
end
klass.include(InstanceMethods)
end
module InstanceMethods
def is_private?
permissions.is_private
end
def is_private=(is_private)
permissions.is_private = is_private
end
end
end
Take a look at ActiveRecord::serialize:
Folder < AR::B
# Must be costant, otherwise Rails will raise an
# ActiveRecord::SerializationTypeMismatch
PERMISSIONS_STRUCT = Struct.new(:is_private, :public_on_negotiation)
serialize :permissions, PERMISSIONS_STRUCT
def is_private?
permissions.is_private
end
def is_private=(is_private)
permissions.is_private = is_private
end
# The same for public_on_negotiation
end
f = Folder.new
f.is_private = true
f.save
f.reload
f.is_private?
=> true
If you need to dynamically define accessor methods:
Folder < AR::B
[:is_private, :public_on_negotiation].each do |action|
define_method("#{action}?") do
permissions.send action
end
end
# And so on for "#{action}=", ...
end
And remember: refactoring is up to you! :-)