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 !!!
Related
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
I've added a new custom action to XslStylesheet, however when I try to access this, I get the following error:
undefined method `belongs_to' for #<RailsAdmin::AbstractModel:0x14fb614d>
I have done this once before already for another model, but I didn't encounter this error. I have tried completely copy-pasting that action and renaming it, however this hasn't helped.
New action (\rails_admin\lib\rails_admin\config\actions\xsl_action.rb):
module Config
module Actions
class XslAction < RailsAdmin::Config::Actions::Base
RailsAdmin::Config::Actions.register(self)
register_instance_option :collection do
true
end
register_instance_option :http_methods do
[:get, :put]
end
register_instance_option :controller do
proc do
if request.get? # Show index page
respond_to do |format|
format.html { render #action.template_name }
format.js { render #action.template_name, layout: false }
end
elsif request.put? # UPDATE
#save uploaded image to GridFS
end
end
end
register_instance_option :link_icon do
'icon-list-alt'
end
end
end
end
end
New action's view (\rails_admin\app\views\rails_admin\main\xsl_action.html.haml):
%h1{:style => "padding-bottom: 1%; margin-top: 0px; padding-top:10px;"} Upload Asset
= form_tag('/admin/xsl_sheet/xsl_action?'+'Application='+params[:Application]+'&Company='+params[:Company]+'&locale=en', method: :put, multipart: true) do
= file_field_tag 'stylesheet'
%br
= submit_tag 'Submit'
I've added the following into rails_admin.rb:
require 'rails_admin/config/actions/xsl_action'
RailsAdmin::Config::Actions.register(RailsAdmin::Config::Actions::XslAction)
xsl_action do
only XslStylesheet
end
I've also tried wrapping XslStylesheet in square brackets, this didn't help.
Expected outcome is that clicking on the new tab redirects the user to xsl_action.html.haml through the asset_action action. I instead get undefined method `belongs_to' for #<RailsAdmin::AbstractModel:0x14fb614d>
It might be worth noting that the index page works as intended for this model.
Edit: XslStylesheet.rb Model
require 'mongoid/grid_fs'
class XslStylesheet
include Mongoid::Document
scope :companyId, -> {where(assetable_id: User.current_user.current_scope['Company']) unless User.current_user.nil? or User.current_user.current_scope.nil?}
belongs_to :company
validates_uniqueness_of [:data_file_name, :image_id], :scope => :assetable_id
field :data_file_name, type: String
field :assetable_id, type: Integer
field :image_id, type: BSON::ObjectId
end
Given 2 resources:
jsonapi_resources :companies
jsonapi_resources :users
User has_many Companies
default_paginator = :paged
/companies request is paginated and that's what I want. But I also want to disable it for relationship request /users/4/companies. How to do this?
The best solution I found will be to override JSONAPI::RequestParser#parse_pagination like this:
class CustomNonePaginator < JSONAPI::Paginator
def initialize
end
def apply(relation, _order_options)
relation
end
def calculate_page_count(record_count)
record_count
end
end
class JSONAPI::RequestParser
def parse_pagination(page)
if disable_pagination?
#paginator = CustomNonePaginator.new
else
original_parse_pagination(page)
end
end
def disable_pagination?
# your logic here
# request params are available through #params or #context variables
# so you get your action, path or any context data
end
def original_parse_pagination(page)
paginator_name = #resource_klass._paginator
#paginator = JSONAPI::Paginator.paginator_for(paginator_name).new(page) unless paginator_name == :none
rescue JSONAPI::Exceptions::Error => e
#errors.concat(e.errors)
end
end
I am trying to learn how to use Rails 5 (generally) but specifically, I'm trying to learn how to use service classes.
I'm trying to write a service class that maps a user's given email address (user's have an attribute called :email) to organisation's domain names. Organisations have attributes called :email_format. I use that attribute to hold the part of the email address that follows the "#".
When a user creates an account, I want to take their email address that they use to sign up with, and match the bit after the # to each of the organisations that I know about and try to find a matching one.
My attempts at this are plainly wrong, but I'm struggling to figure out why.
I have resources called User, Organisation and OrgRequest. The associations are:
User
belongs_to :organisation, optional: true
has_one :org_request
Organisation
has_many :org_requests
has_many :users
OrgRequest
belongs_to :user
belongs_to :organisation
I have tried to write a service class as:
class User::OrganisationMapperService #< ActiveRecord::Base
def self.call(user: u)
new(user: user).call
end
def initialize(user: u)
self.user = user
end
def call
if matching_organisation.present?
# user.organisation_request.new(organisation_id: matching_organisation.id)
# user.update_attributes!(organisation_id: matching_organisation.id)
else
#SystemMailer.unmatched_organisation(user: user).deliver_now
end
end
private
attr_accessor :user
def matching_organisation
# User::OrganisationMapperService.new(user).matching_organisation
User::OrganisationMapperService.new(user: user)
end
end
I then have an org requests controller with:
class Users::OrgRequestsController < ApplicationController
before_action :authenticate_user!, except: [:new, :create, :requested]
before_action :set_org_request, only: [:approved, :rejected, :removed]
# skip_before_action :redirect_for_unrequested_organisation
# skip_before_action :redirect_for_unknown_organisation
def index
organisation = Organisation.find_by(owner_id: current_user.id)
return redirect_to(user_path(current_user.id)) if organisation.nil?
#org_requests = organisation.org_requests
end
def new
#all_organisations = Organisation.select(:title, :id).map { |org| [org.title, org.id] }
#org_request = OrgRequest.new#form(OrganisationRequest::Create)
matched_organisation = User::OrganisationMapperService.new(current_user).matching_organisation
#org_request.organisation_id = matched_organisation.try(:id)
end
def create
#org_request = OrgRequest.new(org_request_params)
#org_request.user_id = current_user.id
if #org_request.save
OrgRequest::ProcessService.new(org_request).process
return redirect_to(user_path(current_user),
flash[:alert] => 'Your request is being processed.')
else
# Failure scenario below
#all_organisations = Organisation.select(:title, :id).map { |org| [org.title, org.id] }
render :new
end
end
def requested
# Need help - if this is contained in form inputs - how do i stop from overriding the submit path?
redirect_to(user_path(current_user))
#not sure about this - a similar redirect isnt required for articles or project create
end
def approve
#org_request = current_user.organisation.org_requests.find(params[:id])
if #org_request.state_machine.transition_to!(:approved)
flash[:notice] = "You've added this member."
redirect_to org_requests_path
else
flash[:error] = "You're not able to manage this organisation's members"
redirect_to :index
end
end
def remove
#org_request = current_user.organisation.org_requests.find(params[:id])
if #org_request.state_machine.transition_to!(:removed)
flash[:notice] = "Removed from the organisation."
redirect_to action: :index
# format.html { redirect_to :index }
# format.json { render :show, status: :ok, location: #project }
# redirect_to action: :show, id: project_id
# add mailer to send message to article owner that article has been approved
else
flash[:error] = "You're not able to manage this organisation's members"
redirect_to(user_path(current_user))
# redirect_to action: :show, id: project_id
end
end
def decline
#org_request = current_user.organisation.org_requests.find(params[:id])
if #org_request.state_machine.transition_to!(:declined)
flash[:notice] = "You're not eligible to join this organisation"
redirect_to action: :index
# redirect_back(fallback_location: root_path)
# format.html { redirect_to :index }
# redirect_to action: :show, id: organisation_request.profile
# add mailer to send message to article owner that article has been approved
else
flash[:error] = "You're not able to manage this organisation's members"
redirect_to(user_path(current_user))
# redirect_to action: :show, id: organisation_request.profile
end
end
private
# Use callbacks to share common setup or constraints between actions.
def set_org_request
#org_request = OrgRequest.find(params[:id])
authorize #org_request
end
# Never trust parameters from the scary internet, only allow the white list through.
def org_request_params
params.require(:org_request).permit(:organisation_id, :name) # Need help - not sure if I need to put user id and organisation id in this permission
end
end
I can't figure out another approach to this. When I try this, I get this error:
wrong number of arguments (given 1, expected 0)
The error message highlights line 7 of my service class, which has:
def initialize(user: u)
self.user = user
end
I have previously asked questions about this problem here: superclass mismatch for class User - inheriting from ActiveRecord::Base
but I haven't managed to catch the drift of the advice or what is causing the problem. This attempt is a mash up of suggestions that I have gleaned from at least 10 different tutorials - so I appreciate that its highly unlikely to be correct, but I'm struggling to understand how the different parts of this work to know what to try differently.
Can anyone give me a steer on how to try to progress this attempt?
Organisation mapper decorator has:
class User < ActiveRecord::Base
class OrganisationMapper < ::ApplicationDecorator
def matching_organisation
#matching_organisation ||= Organisation.by_email_format(email_format).first
end
def email_format
user.email.split('#').last
end
private
def user
#model
end
end
end
Application decorator has:
class ApplicationDecorator
def initialize(model)
#model = model
end
private
def method_missing(method, *args)
args.empty? ? #model.send(method) : #model.send(method, *args)
end
end
Org request service class has:
class OrgRequest::CreateService < ActiveRecord::Base
attr_accessor :org_request
def self.call(user_id: user_id, organisation_id: org_id)
new(user_id: user_id, organisation_id: organisation_id).call
end
def initialize(user_id: user_id, organisation_id: org_id)
self.user_id = user_id
self.organisation_id = organisation_id
end
def call
self.org_request \
= OrgRequest.new(user_id: current_user.id,
organisation_id: params[:org_request][:organisation_id])
if org_request.save
# send the email
true
else
false
end
end
end
NEXT ATTEMPT
I have tried every variation on this that I can think of. Nothing I'm trying makes any sense to me but I can't make sense out of any examples that I can find.
My service class currently has:
class User::OrganisationMapperService #< ActiveRecord::Base
def self.call(user: u)
new(user: user).call
end
def initialize(user: u)
self.user = user
end
def call
# if matching_organisation.present?
# user.org_request.new(organisation_id: matching_organisation.id)
# if found create a request for that user to enter the organisation
if match_domain.present?
OrgRequest.create(user: #user, organisation_id: #organisation_domain.organisation.id) #if organisation
# user.update_attributes!(organisation_id: matching_organisation.id)
else
#SystemMailer.unmatched_organisation(user: user).deliver_now
end
end
private
attr_accessor :user
# def matching_organisation
# # User::OrganisationMapperService.new(user).matching_organisation
# User::OrganisationMapperService.new(user: user).Organisation.by_email_format(email_format).first
# end
# def matching_organisation
# #matching_organisation ||= Organisation.by_email_format(email_format).first
# end
def user_domain
user.email.split('#').last
end
def organisation_domain
#organisation = Organisation.find_by(email_format: user_domain)
end
# def user_email_domain
# # extract domain from users email
# user_email_domain = #user.email.split('#').last
# end
def match_domain
return unless #user_domain == #organisation.email_format
end
# find an organisation with a matching domain
# end
end
It's plainly wrong. The error message says:
NameError - undefined local variable or method `organisation' for #<User::OrganisationMapperService:0x007faec6ec06b8>
I can't make sense of the error message either because I have put '#' in front of every instance of 'organisation' just to try to make that error go away. It doesn't.
Please help.
ANOTHER COMPLETELY SENSELESS ERROR MESSAGE
I had another go at trying to write the method to check whether an email domain matches an organisation's email format in my service class.
The call method now has:
def call
if user_domain == Organisation.email_format.any?
OrgRequest.create(user: #user, organisation_id: #organisation_domain.organisation.id) #if organisation
else
end
end
The error message in the console says:
NoMethodError - undefined method `email_format' for #<Class:0x007faec72d8ac0>
That has to be nonsense because my organisation table has an attribute in it called :email_format. In the console, I can write:
o = Organisation.first.email_format
Organisation Load (3.3ms) SELECT "organisations".* FROM "organisations" ORDER BY "organisations"."id" ASC LIMIT $1 [["LIMIT", 1]]
That gives me the result I'm looking for.
I'm trying (to my wits end) to learn how rails communicates. I can't make any sense of any of it.
NEXT ATTEMPT
Next guess of a go at the call method:
def call
if user_domain == organisation_domain?
OrgRequest.create(user: #user, organisation_id: #organisation_domain.organisation.id) #if organisation
else
end
Produces this error:
NoMethodError - undefined method `organisation_domain?' for #<User::OrganisationMapperService:0x007faec3be3600>:
I can't seem to find a single form of expression that doesnt produce this error.
The problem appears to be in the following line:
matched_organisation = User::OrganisationMapperService.new(current_user).matching_organisation
It should be this instead:
matched_organisation = User::OrganisationMapperService.new(user: current_user).matching_organisation
I had a session on code mentor. This is the answer. I hope it might help someone else who is trying to learn.
class User::OrganisationMapperService #< ActiveRecord::Base
def self.call(user: u)
new(user: user).call
end
def initialize(user: u)
self.user = user
end
def call
if organisation_domain.present?
OrgRequest.create(user: #user, organisation_id: organisation_domain.id) #if organisation
else
end
end
private
attr_accessor :user
def user_domain
user.email.split('#').last
end
def organisation_domain
#organisation ||= Organisation.find_by(email_format: user_domain)
end
end
I have several models that I want the user to "disable" it vs destroying it. These models have a disable boolean. trying to make this work.
currently in application_controller.rb
helper_method :disable
def disable(model)
#model = "#{model}".find(params[:id])
#model.update_attribute(:disable => true)
flash[:notice] = "Successfully disabled #{model}."
redirect_to company_ + "#{model}".pluralized + _url(current_company)
end
Do I have to create a new path in routes for each one I want to use this function?
Would be ideal, if I can do something similar like the destroy method.
I would probably extend ActiveRecord with a disable method so that you can call #model.disable() just like you would #model.destroy(). That way you can leave all the default routes as is and just change the destroy action in your controller to try disable() instead of destroy().
Perhaps like this:
module MyDisableModule
def self.included(recipient)
recipient.class_eval do
include ModelInstanceMethods
end
end
# Instance Methods
module ModelInstanceMethods
#Here is the disable()
def disable
if self.attributes.include?(:disabled)
self.update_attributes(:disabled => true)
else
#return false if model does not have disabled attribute
false
end
end
end
end
#This is where your module is being included into ActiveRecord
if Object.const_defined?("ActiveRecord")
ActiveRecord::Base.send(:include, MyDisableModule)
end
And then in your controller:
def destroy
#model = Model.find(params[:id])
if #model.disable #instead of #model.destroy
flash[:notice] = "Successfully disabled #{#model.name}."
redirect_to #wherever
else
flash[:notice] = "Failed to disable #{#model.name}."
render :action => :show
end
end
Note that in this example, disabled is the attribute and disable is the method that makes a model disabled.