Post controller require post - ruby-on-rails

In an article, a Post controller is created.
But in post_params, params.require requires :post. How this possible or What does it mean that Post controller requires :post
def create
Post.create(post_params)
redirect_to root_path
end
private
def post_params
params.require(:post).permit(:description, :image, :user_id)
end

it's a way Rails Controlling mass-assigment attributes with strong_parameters. this approach forbids mass-assignment of a model attribute from a controller unless that attribute was white-listed
whitelisting is configured using two methos
require, calling require will validate that the parameter is actually present, if not present it will throw an error
permit, calling permit with list of attributes will enable those attributes to "pass through" to model during mass-assignment (this restriction disable code injection by passing unpermitted objects, hashes etc)
from your sample above
params.require(:post).permit(:description, :image)
meaning an instance of post must exist in params and keys/column that allowed to pass through only description and image, if your form passing other field for example :other_description and it's not listed in permit, it will not be saved

The class name :post is required because the controller expects that the parameters are arranged hierarchically under their class names, as one single POST request can yield parameters pertaining to many classes. For example your post description will be available as
params[:post][:description]
In development, it's sometimes useful to inspect the parameters in the browser window. You could use this simple code:
<% if !Rails.env.production? %>
<%= debug(params) %>
<% end %>

Related

Unpermitted parameter error when adding request parameter while using Administrate

I'm using Administrate v0.11.0 with search_term textbox,
it works totally fine,
and now I want to add a request parameter my_search_condition_flag which is a boolean flag value that affects search condition.
In my index action of controller,
I added the following line, so that requests with this parameter pass the Strong Parameters validation.
params.permit(:search, :my_search_condition_flag)
The rest of the code in index action is simply copied from ApplicationController.rb of Administrate.
When I make a HTTP request with request parameter my_search_condition_flag=1 ,
my index action is processed just fine,
but HTTP response returns following error:
ActionController::UnpermittedParameters in Admin::MyPage#index
Showing /usr/local/bundle/gems/administrate-0.11.0/app/views/administrate/application/_search.html.erb where line #19 raised:
found unpermitted parameter: :my_search_condition_flag
which is raised from rendering method of search_term textbox inside index.html.erb
<% if show_search_bar %>
<%= render(
"search",
search_term: search_term,
resource_name: display_resource_name(page.resource_name)
) %>
<% end %>
I've already tried the following to my Dashboard class, introduced here:
# -- Overwrite the method to add one more to the permit list
def permitted_attributes
super + [:my_search_condition_flag] # -- Adding our now removed field to thepermitted list
end
How can I tell Administrate to permit a parameter which I want to add?
Do I have to use request body instead? (which I don't want)
You were on the right track there. The exception originates at /app/views/administrate/application/_search.html.erb:19, as you mention. If you look there, you'll see it uses the method clear_search_params, which also uses strong_parameters to allow/deny query params. You can override this with a helper of your own. For example:
module Admin
module ApplicationHelper
def clear_search_params
params.except(:search, :page, :my_required_condition_flag).permit(
:per_page, resource_name => %i[order direction]
)
end
end
end
If you do this, you'll get a new, related error. This time from /app/helpers/administrate/application_helper.rb:48. The method there is called sanitized_order_params, and can be overriden similarly:
module Admin
module ApplicationHelper
# ...
def sanitized_order_params(page, current_field_name)
collection_names = page.item_includes + [current_field_name]
association_params = collection_names.map do |assoc_name|
{ assoc_name => %i[order direction page per_page] }
end
params.permit(:search, :my_required_condition_flag, :id, :page, :per_page, association_params)
end
end
end
And with that, you should be clear of errors.
Admittedly, this is not very nice fix. Ideally Administrate should be providing some better way to override this list of allowed search params. Fancy submitting a PR? ;-)

Email compose view in rails

I have created and (hopefully set up) a mailer.
Instead of sending templates, I would like to email the content of a form textarea in a view.
I need a view to edit the message, which should be sent to the controller which in turn calls the send_mail method in my mailer.
Class Notifier < ActionMailer::Base
default from: "from#example.com"
def send_email( email, subject, body )
mail(
:to => email,
:subject => subject
) do |format|
format.text { render :text => body }
end
end
end
This is my view:
<%= form_for(:post, :url => {:action => 'send'}) do |f| %>
<%= f.text_field(:title, class: 'form-control')%>
<%= f.text_area(:content, rows: 15)%>
<%= f.button "Submit", type: 'submit' %>
<% end %>
The problem is that when generating a mailer with rails g mailer Notifier
I get a notifier.rb in mailers and a notifier folder in views. However I have no view controller for the notifier views.
Question: How do I make the link between my view and sending the input text as email?
You need to create a controller which handles your view, and in that controller you need to call the mailer somewhat like this: (you'll need to change the names of your form fields to match the params in the call or vice versa)
Notifier::send_email( params[:email], params[:subject], params[:body]).deliver
I'd recommend to check out these RailsCasts:
http://railscasts.com/episodes/61-sending-email-revised
http://railscasts.com/episodes/61-sending-email
http://railscasts.com/episodes/206-action-mailer-in-rails-3
This might be a good place to make a non-ActiveRecord model. I understand that right now a problem is solved and this is a bit beyond the scope, but it's useful, so why not?
I suggest you look at pattern 3 in this article and build a form model (Notification?) that encapsulates the process of storing form contents, validating them and sending the actual email. Note that the implementations in the article are pretty much out of date, Rails 4 introduced ActiveModel::Model that facilitates the process.
Pros:
Another class defined mostly in declarative style, easy to read and find
Can be easily and cleanly laid out by SimpleForm or Rails' form helpers
Gets all the benefits of a traditional Rails model, like validations (and errors if they fail)
Semantic, code looks consistent with the rest of the app working with DB or whatever
Cons:
Another class, can be considered overengineering
More code, some more work, ease of maintenance is arguable
Another object to create in controllers that render this form
Once it's done, the process of making it work is pretty much the same as making any other resource to work. And I assume, that this mailer sits on its separate page.
Routes:
resource :notification, only: [:create] do
get :new, path: "" # A matter of taste, really
# You may use the default `new` route
# with a path `notifications/new`
end
Controller:
class NotificationsController
def new
#notification = Notification.new
end
def create
#notification = Notification.new(notification_params)
if #notification.send
# success! redirect somewhere?
else
render :new # render the form again with the errors
end
end
private
def notification_params
params.require(:notification).permit(:email, :subject, :body)
end
end
You will also need a view for the new action that renders the #notification into a form. Only new, create doesn't need its own. And now for the fun part, model:
class Notification # Yep, it inherits nothing!
include ActiveModel::Model
attr_reader :email, :subject, :body
validates :email,
presence: true # You might want to validate its format?
validates :subject,
presence: true, length: {in: 0..100} # Too long subjects are annoying
validates :body,
presence: true
def persisted?
false # I have no idea why, but it's defined in the article, no harm done
# I'd love to hear the explaination about this though
end
def send
if valid? # no objections from validations?
# Alright, send it already!
Notifier.send_mail(email, subject, body).deliver
# thanks for this line go to #Daniel and his answer
true
else
false
end
end
end
And, finally, a pro tip: Rails 4.2 (bleeding edge right now!) introduced ActiveJob, that's integrated with mailers. By replacing a call to deliver method with a call to deliver_later you will enqueue the email for sending by the background task processor as described here (edge guides, subject to change quite soon). I don't really think it's about time to use it everywhere (too new), but consider this for future projects.
Do I really think it's good? Yeah, I really do, I've refactored a user password changer to look this way, the code has become easier to navigate and look at.
So think of your notifier.rb as a controller. In which you defined the #send_mail. This means that in views/notifier you should add a send_mail.html.haml (erb/slim... matter of taste) which will be the body of the mail.
Now from the controller that receives your form you just need to call
Notifier.send_mail(email, subject, body).deliver

undefined method `pushes_path' for #<#<Class:0x007f85a15c6c90>

i'v been trying to resolve this error for the past 5 hours and I'm gonna burn my computer if I can't solve this.
undefined method `pushes_path' for #<#:0x007f859d605250> this is the error code I'm getting but i don't understand why.
this is my index.html.erb file inside of the interaction
<%= simple_form_for #push do |f| %>
<%= f.input :payload, as: :text %>
<%= f.input :segment, as: :radio_buttons %>
<%= submit_tag "start the campaign" %>
<% end %>
and this is my interaction controller
class InteractionController < ApplicationController
def index
#push =Push.new
end
end
Push is my table in the database and i'll get the inputs and write them in the database to use them for later one.
and this is my routes file
devise_for :partners
get 'home/index'
get 'segmentation/index'
get 'interaction/index'
root to: "home#index"
i really don't know why its looking for pushes_path, what am i doing wrong?
form_for
The problem you have is that your form_for method is going to try and generate a route based off your #path object. And as such, if you don't have a path created for it, you'll receive the error you're getting:
:url- The URL the form is to be submitted to. This may be represented
in the same way as values passed to url_for or link_to. So for example
you may use a named route directly. When the model is represented by a
string or symbol, as in the example above, if the :url option is not
specified, by default the form will be sent back to the current url
(We will describe below an alternative resource-oriented usage of
form_for in which the URL does not need to be specified explicitly).
The bottom line is that as Rails is object orientated, its built around the assumption that you'll have routes set up to handle the creation of individual objects.
Every time you use form_for, Rails will attempt to construct your routes from your object -- so if you're trying to do the following, it will treat the routes as photo_path etc:
#app/views/pushes/new.html.erb
<%= form_for #push do |f| %>
...
<% end %>
--
Fixes
As #mandeep suggested, there are several fixes you can employ to get this to work:
Firstly, you can just create a route for your push objects:
#config/routes.rb
resources :pushes
Secondly, as you're using a different controller, you'll want to do the following:
#config/routes.rb
resources :interactions
#app/views/pushes/new.html.erb
<%= form_for #push, url: interaction_path do |f| %>
...
<% end %>
This will route your form submission to the interactions controller, rather than the pushes controller that you'll get by default!
Objects
Something to consider when creating Rails-based backends is the object-orientated nature of the framework.
By virtue of being built on Ruby, Rails is centered on objects - a term for a variable, which basically encompasses much more than just a piece of data. Objects, in the case of Rails, are designed to give the application:
Once you understand this, the entire spectrum of Rails functionality becomes apparent. The trick is to realize that everything you do in Rails should be tied to an object. This goes for the controllers too:
--
Ever wondered why you call resources directive in your routes, for a controller? It's because you're creating a set of resourceful routes based for it:
Do you see how it's all object orientated?
This gives you the ability to define the routes for specific controllers etc. The most important thing to note is how this will give you the ability to determine which routes / controller actions your requests should go
--
There's nothing wrong in using the controller setup as you have - the most important thing is to ensure you're able to define the custom URL argument, as to accommodate the non-object based structure
In your index action you have
def index
#push =Push.new
end
and your form has
<%= simple_form_for #push do |f| %>
so your form is looking for /pushes with post verb or pushes_path and you don't have that route in your routes.rb file so to fix this you need to add this in routes.rb:
resources :pushes
Update:
when you add resources :push rails basically creates seven different routes for you. One of which is
POST /pushes pushes#create create a new push
and if you look at the html generated by your form it would be something like:
<form action="/pushes" class="new_push" id="new_push" method="post">
// your fields
</form>
notice the action and verb so when you submit your form your routes are checked for them and since you didn't define them in your routes you were getting this error
And how will i be able to use the params i m getting from this form with this new resource addition?
Your form will take you to pushes_controller create action so first of all you'll have to define them. You can access them simply by params[:pushes] in your controller action but since you want to create a new record so you'll have to permit those attributes, checkout strong parameters
If you are using rails >= 4 then you can do
class PushesController < ApplicationController
def create
#push =Push.new(push_params)
if #push.save
redirect_to #push
else
render 'interaction/index'
end
end
private
def push_params
params.require(:push).permit(:attributes)
end
end
If you are using rails < 4 then instead of permitting these attributes(because strong parameters feature came from rails 4) you'll have to tell rails that these attributes are accessible by writing this in your pushes.rb
attr_accessible :attribute_name
Why it is assuming that its pushes controller?Because of the Push.new creation?
That's because if you look at your index action #push = Push.new so #push contains a push object with nil values(as you have just initialized it) so this is where rails magic comes, rails automatically tries to figure out url of your form and since your #push is only an initialized variable so rails takes you to create action for it. For details you should checkout rails polymorphic urls If you want your form to go to interaction_controller or some other url then you'll have to specify the url option for it
<%= form_for #push, url: "your_url_for_custom_method" %>
// other fields
<% end %>
And in the end you should really read docs

How to get 1st nested object into a 2nd level nested object controller?

I have a Character model that has a show page. On the show page, I have a loop of comments that are dynamically generated via a partial. In that comments partial, I have another partial for votes, which contains voting buttons. Naturally, I want to allow votes on comments.
I am unsure how to get the comment object into the votes controller (or VotesController module, depending on the implementation) for creating a vote.
Getting the character object id to the votes controller is simple enough, since the actual view is the character show page, but obtaining a specific comment's id that is genrated from a partial, by clicking a vote button in a partial that is nested in the comments partial is causing me to draw a blank for the syntax of accessing that comment.
(I am using acts_as_votable for votes, and acts_as_commentable for comments.)
app/views/characters/show.html.haml
= render partial: 'comments/comment', collection: #comments, as: :comment
app/views/comments/_form.html.haml
.comment{ :id => "comment-#{comment.id}" }
%hr
= render partial: 'votes/vote_comment'
%h4
#comment body
app/views/votes/_vote_comment.html.haml
.vote-comment-buttons
= link_to image_tag("upvote.png"), votes_upvote_path(), method: :post, remote: true
= link_to image_tag("downvote.png"), votes_downvote_path(), method: :post, remote: true
app/controllers/votes.html.haml
VotesController < ApplicationController
def upvote
# Need the specific comment or comment id whose vote button was clicked.
end
def downvote
# Need the specific comment or comment id whose vote button was clicked.
end
end
Well, here are the basic tips:
You can not pass ruby objects through HTTP, but you can pass id and type of them to build them in your controller.
Even when you type something like comment_path(comment), only id of that comment is passed to your action. That is easily checked by observing your action code (it should contain something like Comment.find(params[:id])).
Passing any desired amout of additional parameters can be done with just providing them to your route helpers, like that: some_voting_path(commentable_id: 14, commentable_type: 'character').
You can access that params inside of your action with params['commentable_type'] or whatever values you pass with your URL. In case you follow passing id and type approach, you should be able to do some metaprogramming:
def upvote_method
model = params[:commentable_type].camelize.constantize # => e.g., Post
object = model.find(params[:commentable_id]) # => post object
# here goes your inner logics
end
Beware that in case you send your request using GET method, these params are gonna be shown in your browser URL. However, you should not use GET for your purpose here, as voting changes the state of objects in your database.

Adding a hidden input field to all the forms in rails

Is there a way to add a hidden input field to all the form that are declared in views in rails. The hidden field needs to have a value passed by a public property in the controller. Essentially I want any form that is given as response by the web server to have an additional hidden input element.
How I do this? Can I override the form_for by some means ? Or can I go with a wrapper of form in partials and enforce everybody to use the partial?
EDIT: OK, my first pass on this didn't work because you can't define a value in an initializer that you're later going to pass in from a controller. So you can go about this one of two ways.
You can define a CustomFormBuilder class - put it in an initializer -
class CustomFormBuilder < ActionView::Helpers::FormBuilder
def submit(value, options = {})
#template.hidden_field_tag(options.delete(:custom_param)) + super
end
end
Then pass the :builder option to form_for
form_for #whatever, builder: CustomFormBuilder
and assuming you call submit(value, options) in the form, where options includes your custom_param, it will be overwritten by the custom method that inserts your hidden field with the value you want.
The alternative is to monkey patch monkey patch FormTagHelper instead:
module ActionView::Helpers::FormTagHelper
def extra_tags_for_form_with_custom_param(html_options)
hidden_field_tag(html_options.delete('custom_param') +
extra_tags_for_form_without_custom_param(html_options)
end
alias_method_chain :extra_tags_for_form, :custom_param
end
That's tweaking the code form_for uses to insert the authenticity token at the top of every form. Now you can pass that custom param to form_for after setting it as an instance variable in the controller:
form_for #object, custom_param: #custom_param do |f|
If you are using Ruby 2.0+ then you can use module prepend instead of the deprecated alias_method_chain e.g.
module CustomParamFormPatch
private
def extra_tags_for_form(html_options)
hidden_field_tag(html_options.delete('custom_param')) +
super
end
end
ActionView::Base.prepend(CustomParamFormPatch)

Resources