rails - is there a way to capture an entire POST's params? - ruby-on-rails

I want to capture an entire posts params, store it in the DB in one field (text), and then later get at each individual param? Possible? Any example you can show? thanks

You can serialize the entire params hash (or any other object)
class SomeModel < ActiveRecord::Base
serialize :params
…
end
class SomeModelsController < Applicationcontroller
def some_action
SomeModel.create(:params => params)
end
end

All you need is something like the following. I didn't include all of the Sendgrid params since there are so many and you will know what I mean with a few:
class SendgridMessage < ActiveRecord::Base
serialize :attachments
...
end
class SendgridMessagesController < ApplicationController
def create
SendgridMessage.create(:to => params[:to], :from => params[:from], :attachments => params[:attachments])
end
end
Sendgrid will send a POST to /sendgrid_messages with the params, and your object will be created with all of the correct fields (you will need to add some to the example) and serialized attachments like you are looking for.

perhaps
request.raw_post
is what you're looking for?
http://api.rubyonrails.org/classes/ActionDispatch/Request.html#method-i-raw_post

Related

Rails Form Associations - Passing id From Previous Page

Trying to figure our how to set up associations in form.
I have 3 models:
class Request < ActiveRecord::Base
has many :answers
has many :users, through: :answers
end
class Answer < ActiveRecord::Base
belongs to :user
belongs to :request
end
class User < ActiveRecord::Base
has many :answers
has many :requests, through: :answers
end
I am trying to figure out: how to have a User link to Answer#new from Request#Show, and then create an Answer record passing in the Request#Show request_id from the previous page - creating an association between the User's Answer and the Request he was viewing.
My method of doing this now is: I flash the request_id value on Request#Show, and then when a User links to Answer#new, it passes the flashed value into a hidden form tag on Answer#new. This does not seem like the best way to do this.
Any thoughts?
Kudos for the creative approach using flash, however your right there is an easy way. You can pass parameters much between controllers just like passing parameters between methods using the route names.
I didn't quite follow what it was you were trying to achieve in this case but it looks like this blog entry here should get you started..
https://agilewarrior.wordpress.com/2013/08/31/how-to-pass-parameters-as-part-of-the-url-in-rails/
Good luck!
User link to Answer#new from Request#Show
This can be achieved with either sessions or nested resources (or both!). Let me explain:
I would definitely add a nested resource to your requests routes:
#config/routes.rb
resources :requests do
resources :answers, only: [:new, :create] #-> url.com/requests/:request_id/answers [POST]
end
This gives you the ability to call a "nested" route (IE one which sends data to a child controller, and requires "parent" data to be appended to the request).
In your case, you want to create an answer for a request. The most efficient way is to use a routing structure as above; this will allow you to use the following controller method:
#app/controllers/answers_controller.rb
class AnswersController < ApplicationController
def new
#request = Request.find params[:request_id]
#answer = #request.answers.new
end
def create
#request = Request.find params[:request_id]
#answer = #request.answers.new answer_params
#answer.save
end
private
def answer_params
params.require(:answer).permit(:title, :body)
end
end
The above gives you the ability to create an answer by passing the request_id to the nested route. You must note the corresponding route will require a POST method in order to work.
You don't need the new method. If you wanted it, it can easily be handled with the above structure.
Passing the user is a little more tricky.
You can either use the routes, or set a session.
I would personally set a session (it's cleaner):
#app/controllers/requests_controller.rb
class RequestsController < ApplicationController
def show
session[:user_id] = #user.id #-> I don't know how you populate #user
end
end
This will give you the ability to access this session here:
#app/controllers/answers_controller.rb
class AnswersController < ApplicationController
def new
user = User.find session[:user_id]
end
end
#app/views/requests/show.html.erb
<%= link_to "New Answer", request_new_answer_path(request) %>
--
If you're using Devise, the user object should be available in the current_user object (which means you don't have to set session[:user_id]):
#app/controllers/answers_controller.rb
class AnswersController < ApplicationController
def new
## current_user available here if using devise
end
end
To assign a #user to the new answer record, just do this in answers#create:
#app/controllers/answers_controller.rb
class AnswersController < ApplicationController
...
def create
#request = Request.find params[:request_id]
#answer = #request.answers.new answer_params
#answer.user = current_user
#answer.save
end
end
Something like this worked for me:
I have two models (Formula and FormulaMaterial)
Formula has_many FormulaMaterials, which belongs to Formula
My Formula controller sets #formula like so:
#formula = Formula.find(params[:id])
I list my Formula Materials in my Formula show.html.erb by declaring it in my Formula controller like so:
#formula_materials = FormulaMaterial.where(:formula_id => #formula)
When I want to add a new FormulaMaterial to my Formula, the "New Formula Material" button in my show.html.erb file looks like this:
<%= link_to 'Add Material To Formula', new_formula_material_path(:formula_id => #formula), class: "btn btn-success" %>
In the "new_..._path" I set the associated id to the #formula variable. When it passes through to the new.html.erb for my FormulaMaterial, my URL looks like so:
http://localhost:3000/formula_materials/new?formula_id=2
In my FormulaMaterial new.html.erb file, I created a hidden_field that sets the value of the association by using "params" to access the formula_id in the URL like so:
params[:formula_id] %>
I am not sure if this is the best way to do this, but this way has allowed me to pass through the view id from the previous page as a hidden, associated and set field in the form every time.
Hope this helps!

Rails: overriding model name in params

I have the following model:
class Action < ActiveRecord::Base
# model code stuff
end
In the controller, I can do something like that:
class ActionsController < ApplicationController
# super simple update
def update
#action = Action.find(params[:id])
#action.update_attributes(params[:action])
end
end
Is it possible to make my Action model to be represented by 'something_action' in params array without renaming the model itself? I.e. so I can retrieve it like that:
#action.update_attributes(params[:something_action])
Any help would be appreciated?
In your form_for use :as => :some_other_params_name
form_for(#action, :as => :different
If you are using form_for, you can change it to
form_for :something_action, #action do |f|
so it uses :something_action as the name of the params

Hide a field in Ruby on Rails

I've a field in my database called IP where I put the user IP (in #create method) when he send a message in my blog built in Rails.
But the field is visible when I want to see the articles in another format (JSON).
How can I hide the field IP?
You can do it in a format block in your controller like this:
respond_to do |format|
format.json { render :json => #user, :except=> [:ip] } # or without format block: #user.to_json(:except => :ip)
end
If you want to generally exclude specific fields, just overwrite the to_json method in your user model:
class User < ActiveRecord::Base
def to_json(options={})
options[:except] ||= [:ip]
super(options)
end
end
Update: In Rails 6, the method became as_json:
class User < ApplicationRecord
def as_json(options={})
options[:except] ||= [:ip]
super(options)
end
end
While this is not quite the right solution for passwords or for what is specifically asked, this is what comes up when you google for hiding columns in ActiveRecord, so I'm going to put this here.
Rails5 introduced new API, ignored_columns, that can make activerecord ignore that a column exists entirely. Which is what I actually wanted, and many others arriving here via Google probably do too.
I haven't tried it yet myself.
class User < ApplicationRecord
self.ignored_columns = %w(employee_email)
end
https://blog.bigbinary.com/2016/05/24/rails-5-adds-active-record-ignored-columns.html

Creating Rails ActiveRecord model from params hash

Most Rails tutorials show how to populate a model class via the params hash like so:
class UsersController < ApplicationController
def create
#user = User.create(params[:user])
# more logic for saving user / redirecting / etc.
end
end
This works great if all the attributes in your model are supposed to be strings. However, what happens if some of the attributes are supposed to be ints or dates or some other type?
For instance, let's say the User class looks like this
class User < ActiveRecord::Base
attr_accessible :email, :employment_start_date, :gross_monthly_income
end
The :email attribute should be a string, the :employment_start_date attribute should be a date, and the :gross_monthly_income should be a decimal. In order for these attributes to be of the correct type, do I need to change my controller action to look something like this instead?
class UsersController < ApplicationController
def create
#user = User.new
#user.email = params[:user][:email]
#user.employment_start_date = params[:user][:employment_start_date].convert_to_date
#user.gross_monthly_income = params[:user][:gross_monthly_income].convert_to_decimal
# more logic for saving user / redirecting / etc.
end
end
According to the ActiveRecord documentation, the attributes should automatically be typecasted based on the column types in the database.
I would actually add a before_save callback in your users model to make sure that the values you want are in the correct format i.e.:
class User < ActiveRecord::Base
before_save :convert_values
#...
def convert_values
gross_monthly_income = convert_to_decimal(gross_monthly_income)
#and more conversions
end
end
So you can just call User.new(params[:user]) in your controller, which follows the motto "Keep your controllers skinny"

Using the same action to show all(index) objects, but also showing a list scoped by a url parameter

I'm having an issue coming up with a good way to do the following. I have a very generic Org model and User model. Org has_many :users, and User belongs_to :org.
I am trying to find a way of showing a list of users that is not restricted by Org, but also show a list of User's that is restricted by Org. I know I could nest the routes, and just have two different routes like
map.resources :users
map.resources :orgs, :has_many => :users
The problem is that they both go back to the same actions in the User controller. The controller code starts to get very messy because I am having to check for the existence of an :org_id param. Then I have to decide whether to return the normal results of a find call on User, or a find that is scoped to an Org. I'm not sure what the best solution is here, or what the best practice is. If someone with some knowledge on this could please enlighten me, it would be great.
Another way of doing this without a plugin would be to use named_scope. You can create a named scope in User that filters by org_id if it's not empty.
class User < ActiveRecord::Base
belongs_to :org
named_scope :by_org, lambda{|org| org.blank? ? {} : { :conditions => ['org_id = ?', org] }}
end
And in the controller just use your named scope. That way if you eventually supply more filter options in the controller you don't need to duplicate them:
class UsersController < ApplicationController
def index
#users = User.by_org(params[:org_id]).all
...
end
end
Not really the answer you were probably looking for on a technical level but I use a plugin for my projects that takes care of that mess for me. Take a look at make_resourceful.
make_resourceful do
actions :all
belongs_to :org
end
It will figure out the rest for you, no need to define your standard crud action. It will even detect scoping and scope it for you. (unless that's an other plugin i'm using I forgot about)
I use resource_controller plugin for most cases. With it, you just put:
class UsersController < ApplicationController
resource_controller
belongs_to :org
end
It works with nested and not-nested resources.
If you don't want to use additional plugin, your controller still won't be very complicated.
class UsersController < ApplicationController
def index
#org = Org.find(params[:org_id]) unless params[:org_id].blank?
#users = params[:org_id].blank? ? User.all : #org.users
...
end
end
What I usually do is this:
class UsersController < ApplicationController
def index
root = Org.find(params[:org_id]) if params[:org_id]
root = User if root.nil?
#users = root.all(:conditions => {...}, :order => "...")
end
end
I'm basically doing a tree-walk. As conditions are added, I simply change the root of the #find call. When I'm done evaluating conditions, I call the final #find / #first / #all method and I'm done.
This also works if you have multiple named scopes:
class UsersController < ApplicationController
def index
root = Org.find(params[:org_id]) if params[:org_id]
root = User if root.nil?
root = root.named(params[:name]) if params[:name]
root = root.registered_after(params[:registered_at]) if params[:registered_at]
# more conditions, as required
#users = root.all(:conditions => {...}, :order => "...")
end
end

Resources