I'm trying to learn rails 4.1 from rails 4 in action book, I've got to chapter 13, in this chapter defined API for JSON and XML.
It defined a controller like this :
class Api::V1::ProjectsController < Api::V1::BaseController
def index
respond_with(Project.for(current_user).all)
end
def create
project = Project.new project_params
if project.save
respond_with(project, :location => api_v1_project_path(project))
else
respond_with(errors: project.errors.messages)
end
end
private
def project_params
params.require(:project).permit(:name)
end
end
And a rspec test like this :
it "unsuccessful JSON" do
post "#{url}.json", :token => token, :project => {}
expect(last_response.status).to eql(422)
errors = {"errors" => { "name" => ["can't be blank"]}}.to_json
expect(last_response.body).to eql(errors)
end
when I run the test, I get these results :
ActionController::ParameterMissing: param is missing or the value is
empty: project
I know, it's because of strong parameters.
i solved issue like this but any one has a batter idea?
def project_params
if params[:project].nil?
params[:project] = {:name => ''}
end
params.require(:project).permit(:name)
end
it has resolved this problem in my case
params.require(:project).permit(:name, :description) if params[:project]
You can permit nested parameters as explained in strong parameters in github.
In your case, this is probably what you want:
params.permit(:project => [:name, :description])
Related
I want to change the name of the attribute in strong parameter so it does not have "_attributes" in the end.
I have:
params.require(:setting).permit(:recording,
:special_settings_attributes => [:orientation])
I am testing it with :
describe "Settings Creation" do
context 'new setting success' do
before do
a = post :create, format: :json, :setting => {
:recording => "recorded",
:special_settings_attributes => [:orientation => "left"]
}
end
it 'creates a new setting' do
expect(Setting.last.special_settings.last.orientation).to eq("left")
end
end
end
end
I want
params.require(:setting).permit(:recording,
:special_settings => [:orientation])
I tried renaming of course, but then the SpecialSetting model is no created..
Just alter your params before it's called/used by any of your actions:
before_action do
params[:special_settings_attributes] ||= params.delete :special_settings
end
I've searched high and low for this and the other solutions don't seem to help.
The error:
The action 'create' could not be found for WatchedWatchersController
I get this after clicking a link made with this code in a view:
<%= link_to 'Watchlist', { :controller => "watched_watchers",
:action => "create",
:watcher_id => current_user.id,
:watched_id => user.id},
:method => "post" %>
My model for this class is:
class Watched_Watcher < ActiveRecord::Base
attr_accessible :watched_id, :watcher_id
validates :watched_id, :watcher_id, presence: true
end
And my controller is:
class WatchedWatchersController < ApplicationController
def new
end
def create
#ww = Watched_Watcher.new
#ww.watcher_id = params[:watcher_id]
#ww.watched_id = params[:watched_id]
#ww.save
end
def update
end
def edit
end
def destroy
end
def index
end
def show
end
end
I've set up my routes as a RESTful resource:
resources :watched_watchers
And rake routes reveals:
POST /watched_watchers(.:format) watched_watchers#create
So I'm stumped and any help is appreciated. Thanks in advance.
Vimsha's comment solved it
"Rename your model to WatchedWatcher(no underscore)". And then once I fixed the model name I just needed to have a redirect back to the same page.
I am trying to upgrade my app from Rails 3 to Rails 4 and I can't seem to get the syntax right in line 12:
class ProjectsController < ApplicationController
before_filter :find_project
...
private
def find_project
#project = Project.find(params[:id])
end
def valid_people
if params[:project][:person_ids].present? # how to do this with strong parameters?
person_ids = params[:project][:person_ids].map(&:to_i)
valid_ids = current_user.people.pluck(:id)
redirect_to root_path if (person_ids - valid_ids).any?
end
end
def project_params
params.require(:project).permit(:name, :description, :person_ids)
end
end
I keep getting this error: Unpermitted parameters: person_ids
How can I use only the person_ids parameter?
Thanks for any help.
The idea of strong parameters is that you call the function you have defined to get your parameters
if params[:project][:person_ids].present? # how to do this with strong parameters?
would become
if project_params[:person_ids].present? # how to do this with strong parameters?
also i'm guessing :person_ids is an array if so replace your :person_ids with {:person_ids => []}
I'm really struggling trying to learn rspec :( So I hope you can give me a little bit of help with a really simple create-action in the controller. I would like to use Rspec::mocks for this, as I think that is the way to do it? Instead of having to hit the database when testing.
I'm having a before_filter:
def find_project
#project= Project.find_by_id(params[:project_id])
end
The create action looks like this:
def create
#batch = Batch.new(params[:batch])
#batch.project = #project
if params[:tasks]
params[:tasks][:task_ids].each do |task_id|
#batch.tasks << Task.find(task_id)
end
end
if #batch.save
flash[:notice] = "Batch created successfully"
redirect_to project_batch_url(#project, #batch)
else
render :new
end
end
I'm really in doubt when it comes to #batch.project = #project how do I define #project? And also the whole params[:tasks][:task_ids].each part.. Ya.. pretty much the whole thing :(
Sorry for this newbie question - Hope you guys can help or atleast point me in the right direction :)
Thanks
The idea of a controller spec is to check whether the actions are setting instance variables, and redirecting/rendering as needed. To set up the spec, you would normally create an object or a mock, set attributes/stubs, and then call the action, passing a params hash if necessary.
So for example (air code):
describe MyController do
before(:each) do
#project = mock_model(Project)
Project.stub(:find_by_id) {#project}
#batch = mock_model(Batch)
Batch.stub(:new) {#batch}
end
it "should redirect to project_batch_url on success" do
#batch.stub(:save) {true)
post :create, :batch => { :some_key => :some_value }, :tasks => { :task_ids => [1,2,3] }
response.should redirect_to(project_batch_url(#project,#batch))
end
it "should render :new on failure" do
#batch.stub(:save) {false)
post :create, :batch => { :some_key => :some_value }, :tasks => { :task_ids => [1,2,3] }
response.should render_template("new")
end
end
You can find lots more information about this in the RSpec Rails docs.
Using BDD helps you define your interfaces. So if your controller wants the project to create a batch and add some task id's, then "write the code you wish you had." In practice for controllers, this means trying to push logic out of the controller and into your models. Testing models tends to be more intuitive and are definitely faster than testing controllers.
Here are some possible specs (untested) from the "mockist" point of view:
# controller spec
describe BatchesController do
def mock_project(stubs={})
#mock_project ||= mock_model(Project, stubs)
end
def mock_batch(stubs={})
#mock_batch ||= mock_model(Batch, stubs)
end
context "POST create"
it "calls #create_batch_and_add_tasks on the project"
mock_project.should_receive(:create_batch_and_add_tasks).with(
:batch => { :name => 'FooBatch' },
:task_ids => [1,2,3,4]
)
Project.stub(:find).and_return(mock_project)
post :create, :batch => { :name => 'FooBatch' }, :tasks => { :task_ids => [1,2,3,4] }
# consider changing your params to :batch => { :name => 'FooBatch', :task_ids => [1,2,3,4] }
end
it "redirects to the project_batch_url on success" do
mock_project(:create_batch_and_add_tasks => mock_batch(:save => true))
Project.stub(:find) { mock_project }
post :create, :these_params => "don't matter because you've stubbed out the methods"
end
# controller
def create
#batch = #project.create_batch_and_add_tasks(
:batch => params[:batch],
:task_ids => params[:tasks].try([:tasks_ids])
)
if #batch.save
...
I'm implementing a distributed application, server with rails and mobile clients in objective c (iPhone). To enable internationalization, I use the rails plugin 'globalize2' by joshmh.
However, it turned out that this plugin does not translate attributes when calling to_xml or to_json on an ActiveRecord. Does anyone know of a workaround / patch? Do you have any ideas how to fix this, where to alter globalize2?
Using:
Rails 2.3.5
globalize2: commit from 2010-01-11
With Globalize2 (and with model_translations as well) translated attribute in a model is not a real attribute but is a method. Thus and so when you execute to_json method you can use :methods, as Joris suggested, but in a simpler way:
class Post < ActiveRecord::Base
attr_accessible :title, :text
translates :title, :text
end
class PostsController < ApplicationController
def index
#posts = Post.all
respond_to do |format|
format.html
format.json { render :json => { :posts => #posts.to_json(:only => :id, :methods => :title) }}
format.js
end
end
end
Here I would like to receive only post id and title in json response. For additional information see to_json (Serialization) in Rails API.
I found this fork on github: http://github.com/leword/globalize2
But it looks like it is based on an older version.
I was looking for this myself, but solved my problem using the :methods option:
If you want to translate one attribute in #item, you can use:
class Item < ActiveRecord::Base
translates :name
def t_name
self.name
end
end
And in your controller:
render :text => #item.to_xml(:methods => [ :t_name ])
If your api path is something like /en/api/item.xml, you should get the english translation in the t_name attribute
For a belongs_to relation:
belongs_to :category
def category_name
self.category.name
end
And in your controller:
render :text => #item.to_xml(:methods => [ :category_name ])
Your use case is probably different. Above is a workaround that works for me.