Call a controller method via button in Rails - mysql

I want to create a button in views/companies/edit.html.haml for destroy_referrals method, but I can't get the button to work. To call the action on a button I tried to create a new route, but I am still receiving the error The action 'destroy_referrals' could not be found for CompaniesController
I created a destroy_referrals method in Admin::CompaniesController,
def destroy_referrals
load_company
#company.destroy_referrals
flash[:notice] = 'Referrals deleted'
redirect_to :action => :index
end
it manipulates the company.rb model to find the referrals from users with the same company_id and destroys them,
def destroy_referrals
Referral.joins(:user).where("users.company_id = ?", self.id).destroy_all
end
Some things I've tried below...
routes.rb
post '/destroy_referrals' => 'admin/companies#destroy_referrals', :as => 'destroy_referrals'
views/companies/edit.html.haml
= button_to 'Destroy Referrals', destroy_referral_path
= button_to 'Destroy Referrals', admin_company_destroy_referral_path(#company)

HTML links (not “buttons”) aren't wired up to call “methods” directly.
The button_to method creates an HTML button, which behaves similarly to a link, but with different visual styles.
Ruby has methods, which are a common programing construct.
HTML links have an href attribute that a browser can follow by making a subsequent HTTP request to.
When a browser sends a request to your Rails application, the application uses your routes to determine how to handle that request.
The conventional way to handle a request in Rails is to route it to a controller action.
A RESTful request to /products/1 might, by convention, route to the show action on the ProductsController.
In Rails, an “action” is created by defining a method on the controller.
RESTful routes reflect the representational state transfer paradigm, involving reading, listing, creating, updating, and deleting records/objects.
To delete (or destroy) a record, RESTful convention would be to send a DELETE request to the Rails application, which would be routed to the relevant controller's destroy action, defined by the destroy method.
You can create an HTML link that results in a browser sending such a request using the link_to helper and specifying method: :delete in the parameters.
You can define a route instructing Rails to handle such a request using the built in “resourceful” routing: e.g. resources :products. Alternately, an explicit route can be defined: delete '/products/:id' => 'products#destroy'.
This all said, the error Rails is giving you is that it can't find the destroy_referrals method in CompaniesController. Defining methods in Admin::CompaniesController won't do anything to resolve this.

#Swards the route needed to be delete
'companies/:id/destroy_referrals' => 'companies#destroy_referrals',
:as => 'destroy_referrals'
I thought it might be a simple fix. Do you still need the admin namespace?
Consider this, instead of creating a named route
namespace 'admin' do
resources :companies do
delete 'destroy_referrals', :on => :member, :as => 'destroy_referrals'
end
end
And refer to it as admin_destroy_referrals_company_path
= button_to 'Destroy Referrals', admin_destroy_referrals_company_path(#company), :method => :delete

Related

Rails Web App. Help display OpenWeather API icons or json on home page

I'm trying to simply make a third party api call to Open Weather and display the result on the home page of my web app. I am able to successfully make the request in my console by running the file but I cannot get it to show up on my view file. Oddly enough I cannot find anything about actually displaying the returned json in ruby on rails.
<h1 class="hello"><%= #weather.posts %></h1>
The index.html (home)
require 'rubygems'
require 'httparty'
class Weather
include HTTParty
base_uri 'http://https://api.openweathermap.org/'
def posts
self.class.get('https://api.openweathermap.org/data/2.5/weather?q=Memphis&appid=36197f2c3289996e0f0fd7a5ef7d851c')
end
end
#weather = Weather.new
puts #weather.posts
The .rb model
class WeatherController < ActionController::Base
response = HTTParty.get('http://https://api.openweathermap.org/data/2.5/weather?q=Memphis&appid=36197f2c3289996e0f0fd7a5ef7d851c')
JSON.parse response, symbolize_names: true
end
The Controller
So when I start up the server I basically get a blank where the weather icon or json should be. No errors, just blank. Considering how little I could find, it seems this might be helpful to others if answered clearly. Thank You
The code in your controller does not look correct. I guess you want to have something like:
class WeatherController < ActionController::Base
def index
#weather = Weather.new
end
end

How to have Rails routes.rb redirect several paths to a single controller#action?

I'm wondering if there is a way to use "resourceful routing" in Rails to have all the routes point to one controller#action. I want React Router to handle the links.
I've reviewed https://guides.rubyonrails.org/routing.html#specifying-a-controller-to-use and found this How to use React Router on top of Rails router (not using react-rails gem)?
My current routes.rb file (is this the best way to achieve to do this?):
Rails.application.routes.draw do
root to: "home#index"
get 'games', to: 'home#index'
get 'games/new', to: 'home#index'
get 'games/:id', to: 'home#index'
get 'games/:id/edit', to: 'home#index'
namespace :api do
resources :games
end
end
resources :games, controller: :home
This will provide you with the crud paths + index that you want. The paths will be
/games
/games/new
/games/:id
/games/:id/edit
all pointing to HomeController
And then you can still have you're namespaced routes
UPDATE: Updated answer based on comment from op

Single schema multi tenancy in a Rails Web API

I have a single client application - Angular JS frontend and a Rails API backend with a MySQL DB. I'm trying to convert the application into a single schema multi tenant application. I've done a lot of reading and:
I do not want to use a gem if possible - Apartment, which is multiple schemas and uses Postgres doesn't fit the bill, and act_as_tenant seems to use Thread.current to identify the tenant which I do not want to do.
I have read that default_scope should not be used as well, for a host of reasons I won't get into here.
I'm passing a tenant token in the request header from the frontend to the Rails backend, and using the tenant token I identify the tenant in my ApplicationController. I'm now figuring out the best way to both read and write data so that is associated with the tenant that made the request.
Having ruled out the options above, the only option I can see is to go into all of my controller methods and update them wherever data is being written and read. I would much rather apply some sort of callback to each of my Models, so that the tenant id is always written when data is being written and the tenant id is always used as a filter whenever data is being read.
Given that I cannot access the tenant token in the models, I am not sure how to proceed with this other than updating all my controller methods, which would be an arduous and mistake prone process.
Thanks in advance!
Not using default_scope is a good idea - it behaves as sort of a black box and can wreck havoc down the line especially if you ever do anything with paranoid deletion.
One way to do what you asked is to use thread_mattr_accessor. You can define the tenant_id token at the beginning of the web request, and then access it through the class attribute for the duration of the web request. This creates a thread-safe attributes accessor on your tenant model.
In your controller you can detect the tenant for the current request (using subdomain or token) and set the Token.current_id variable. This variable will be available for the duration of the request. Note that it will not automatically be available for any background jobs or other processes because the variable is set inside the current thread.
This method is demonstrated using scopes in this RailsCast, but you don't have to use scopes. You can set a helper method like current_tenant and then explicitly scope all your queries like current_tenant.posts.
# models/tenant.rb
class Tenant < ApplicationRecord
thread_cattr_accessor :current_id
# ...
end
# controllers/application_controller.rb
class ApplicationController < ActionController::Base
around_action :scope_current_tenant
private def scope_current_tenant
Tenant.current_id = current_tenant.id
yield
ensure
Tenant.current_id = nil
end
def current_tenant
#current_tenant ||= Tenant.find_by_token! params[:token]
end
helper_method :current_tenant
end

How to load CSS when updating a user record in Rails

I am using devise for authentication on a client app. After extensive reading of the wiki, I managed to configure my app to suit my needs properly. However I have a CSS loading problem.
I chose the option of allowing an admin approve an account over :confirmable as I am not familiar with combining both :confirmable and approving the account.
I followed the instructions in "How To: Require admin to activate account before sign_in" to do just that and "How to do browser-based new user approval by Admin in Rails 3.2 using Devise" to help with configuring my routes.rb file when updating the attribute.
The issue now is, when I click the link to update the attribute, it does so flawlessly but the path for finding CSS after redirecting back changes.
To clarify what I mean, let's say my CSS file is in public/user/mycss.css, when I sign in with devise, it loads properly but upon clicking the link_to tag it updates the record, but looks for mycss.css via users/:id/approve/mycss.css which doesn't exist with reference to the second link.
I have an admin model that allows the client to create posts for a blog. The authentication system works flawlessly. The devise wiki link in the first link suggested that I list all the users and create a method that is simple and efficient to update the approved attribute. In my controller I have this code:
def approve_admin
admin = Admin.find(params[:id])
unless admin.approved?
admin.update_attribute :approved, true
if admin.save
flash[:notice] = "#{admin.first_name} approved"
else
flash[:alert] = "#{admin.first_name} approval failure"
end
end
redirect_to :back
end
In my view I use a link_to tag that updates the attribute using the above method:
<% if !(admin.approved?) %>
<p class="hvr-shutter-out-horizontal hvr-grow-shadow"><%= link_to "Approve", approve_admin_path(admin.id) %></p>
<% end %>
In my routes file I have this:
get 'admin/:id/approve', to: 'posts#approve_admin', as: 'approve_admin'
This works perfectly but then the CSS and JavaScript files do not load. I inspected the browser for errors and this was the output:
http://localhost:3000/admin/3/js/modernizr.custom.js
for example, instead of loading the files from
http://localhost:3000/admin/js/modernizr.custom.js
Why is it looking for the CSS file via that path?
Based on further research I came up with this answer, which could be a possible solution for anyone who is experiencing trouble with approving a user in devise via an admin. In my case I call them "admin".
After following all the steps in "How To: Require admin to activate account before sign_in" I created a method in the corresponding controller to update the attribute:
def approve_admin
admin = Admin.find(params[:id])
unless admin.approved?
admin.update_attribute :approved, true
if admin.save
flash[:notice] = "#{admin.first_name} approved"
else
flash[:alert] = "#{admin.first_name} approval failure"
end
end
redirect_to :back
end
In my view I created a link_to tag that updates the attribute:
<% if !(admin.approved?) %>
<p class="hvr-shutter-out-horizontal hvr-grow-shadow"><%= link_to "Approve", approve_admin_path(admin.id),method: :put %></p>
<% end %>
In my routes file I did this:
put 'admin/:id/approve', to: 'posts#approve_admin', as: 'approve_admin'
I changed get to put and in my view I used method: :put. That told Rails to update the record without having to look for the CSS files somewhere else.

Rails API: make application controller serve JSON

This tutorial, React + Flux Backed by Rails API, by Fancy Pixel, says:
Now we need to change the application controller so that it inherits
from ActionController::API, and kiss the protect_from_forgery goodbye.
Since we are serving only JSON, it makes sense to add
respond_to :json
to the applciation controller, helps DRYing all out. While we are at
it, we might as well delete the assets and views folders, we won’t
need them.
I am not sure about what I am supposed to do.
This is what my application controller looks like:
class ApplicationController < ActionController::API
respond_to :json
end
Is it correct?
Also, should I keep or remove protect_from_forgery with: :null_session :
class ApplicationController < ActionController::API
protect_from_forgery with: :null_session
respond_to :json
end
To communicate from any Rails Backend (not even necessarily just an API) all you have to do is write the following in your API's controller(s):
class Api::V1::SearchController < ApplicationController
#Run an Authentication Method on their API Key
before_action :authenticate
def search
#Performs your backend logic
content_array = get_content_method
render :json => content_array
#Renders up a JSON that you can retrieve with an HTTP get request
end
end
Also on the topic of serving data, you can send your params in an obscured JSON, allowing you to hide your API Key, thus protecting you from unwanted access. My favorite way of doing this is by using the .to_query method to send over the necessary params. I have done it like so:
BASE_URL = "http://yourwebsite.com/search?"
def generate_search_url(params)
BASE_URL + params.to_query + "&Api-Key=#{ENV['API-KEY']}"
end
This way you can work with the data you're given just like any other params, but it would be more difficult to abuse the API without a granted key.