Why are my API Pie parameters not showing up? - ruby-on-rails

I created the documentation for my methods, and the top-level method description shows up along with its verb, but when I click on any of these links it tells me "Oops!! Method create.en not found for resource questions."
Here is an example of my code:
api :DELETE, '/questions/:id', 'Deletes question. Note that the delete cascades and also removes any child questions.'
param :id, :number
def destroy
#question = Question.find(params[:id])
#question.destroy_cascade
head :ok
end
And here's what the page looks like – all of these links take me to an error page
Weirdly, if I look at the json version it seems to be fine (i.e. it includes all the information).

I ended up needing to add these lines to the initializer:
config.default_locale = 'en'
config.languages = ['en']

Related

How to compare two items within Ruby on Rails?

So I'm trying to re-create GitHub version control for let's say posts. I've found a way to re-create an original post using duplicate AND another method to create a new post based on the original. Cool.
My issue is being able to display both the original and the new on the same page.
What I've attempted thus far is to just rely on the show method with having:
def show
#post = Post.find(params[:id])
end
Then in the view have in the form a checkbox to allow a user to select multiple posts, click a submit, and a new page renders displaying both side by side. Preferably showing the differences between the two but that's a wish list as I deal with this first.
Actually could I just simply do?:
def other_show
#post = Post.where(params[:id])
end
I also added in status as a boolean to help on the view for marking the checkbox. Would I then need to put something in the other_show method about the status?
If you want to "recreate" some sort of version control I suggest you use something like the audited. Instead of building your own. From your example and comments it seems you don't have a clear relation between all related (versions of) posts.
Using this gem, each change to the Post content (for example, if configured properly) would be stored as an audit.
Showing the differences is a different problem. That's usually called a diff and you can find gems that do it for you, for example: diffy
To show 2 different entities on one page you need to give posts_controller both ids.
Declare your show method like this:
def show
#original = Post.find(params[:id])
#compared = Post.find(params[:compared_id])
end
Correct route to this method will look like this:
/posts/:id?compared_id=:another_id
# Example: /posts/1?compared_id=2
To construct such a link in your view, you need to declare link_to method like this:
<%= link_to '1 <> 2', post_path(#post, compared_id: '2') %>
If you want to have a page where user can check 2 checkboxes for certain posts, you'll need to construct such href via Javascript.
But in fact I wouldn't suggest you to modify show method for such a task. It is better to use show method only for showing one entity from database. You can create another method, e.g. compare and pass both parameters there.
def compare
#original = Post.find(params[:original_id])
#compared = Post.find(params[:compared_id])
end
In routes.rb
resources :posts do
get 'compare', on: :collection
end
It will give you helper compare_posts_path, which will lead to /posts/compare and you'll need to pass original_id and compared_id to it, like this:
<%= link_to 'Compare', compare_posts_path(original_id: 'some_id', compared_id: 'some_another_id') %>
It will result to
/posts/compare?original_id=some_id&compared_id=some_another_id

Ruby on Rails basic tips

Im new at Ruby on Rails language and I really need that someone explain me some 'topics' if possible.
I've created an app and I Scaffolded it and it created in the controller lots of code but I have doubts.
One of them is:
This app is 'empty' so far. It only has a 'New Book' in the first page.
//books\index.html.erb
||| <%= link_to 'New Book', new_book_path %> |||
new_book_path redirects me to books_controller
def new
#book = Book.new
respond_to do |format| //-----> What means this 'format'?
format.html # new.html.erb // What really mean two options for 'format'?
format.json { render json: #book } // What means render json: #book
end
# new.html.erb -> has this code inside
New author
/*<%= render 'form' %>
<%= link_to 'Back', authors_path %>*/
Can someone explain me what's hapenning here?
I know that these are really silly questions but I'm not getting it.
Thanks in advance.
The best two ways to understand Rails in-depth are reading its code (https://github.com/rails/rails) and reading Documentation (http://api.rubyonrails.org and http://guides.rubyonrails.org).
So you'll find enough information to cover this topic here: http://api.rubyonrails.org/classes/ActionController/MimeResponds.html or here: http://guides.rubyonrails.org/action_controller_overview.html.
But if you want short answer... listen to the story :)
The entire respond_to do ... end block is responsible for defining rules on how your app should response on different 'formats'. Rails supports a lot of different formats, i.e :html, :json, :xml (you even can define your own formats). Beside mime types it has variants: :desktop, :tablet, :phone. Obviously that with mime types you describe how you want to answer on different types of request and with variants you specify different options for various user agents.
:format variable passed into block has type ActionController::MimeResponds::Collector. They didn't call it so for nothing. It collects all different response types you specify inside block and then using headers section from http request picks an appropriate variant from that options.
Hope it was useful. But again, better check Documentation.
Rails uses MVC pattern in as its foundation ([http://en.wikipedia.org/wiki/Model–view–controller]). So what we've seen before was a Controller. And you can treat new.html.erb as a View for :new action for that controller.
The file itself is an html file flavored with ERB (NOT the same as Epic Rap Battles of History, but [http://en.wikipedia.org/wiki/ERuby]) template engine. ERB is able to inject chunks of ruby code into your pages. <% %> enclosing tag is used just for evaluation and <%= %> for injection of the result of evaluation. So in your case with <%= render 'form' %> you inject result of #render method call into your html and with :link_to helper you create link.
IN CONCLUSION: I recommend you to start with https://www.railstutorial.org. That's an excellent tutorial for starters. You'll find answers for most of your questions and even develop your own little Twitter! (at least 2nd edition is about Twitter).
respond_to is a Rails controller method (explanation here: http://api.rubyonrails.org/classes/ActionController/MimeResponds.html#method-i-respond_to), which gets a block as argument. In short, block is a part of code ran within method it has been passed to.
For a block you declare variable called 'format'. Because this in just variable name so you may declare it i.e. 'f' or whatever you want.
Within the block of respond_to method, you may declare how your controller action responds for given MIME type. So, for HTML you may leave it empty, however if you want your controller to respond to JSON (MIME: application/json and you define it in the request header from the client side), then you have to tell your controller that's the response has to be in json format.

how does devise current_user work?

I want to understand how the devise current_user method works because I want to generalize it to other models which would allow code such as current_forum or current_forum_thread.
To be more specific I am trying to implement a chat forum in Rails. I have a page showing all posts (currently none) for a specific discussion thread. That same page has the new post form embedded. The debug(params) shows:
action: show
controller: discussions
forum_id: '1'
id: '1'
discussion: !ruby/object:Discussion
attributes:
id: 1
title: first discussion (thread)
forum_id: 1
So the create method in the posts controller should know what the discussion id is. Yet this code in the controller does not work.
1. #discussion = Discussion.find(params[:id])
2. #post = #discussion.posts.new(params[:post])
3. if #post.save
4. flash[:success] = "Discussion post created!"
5. redirect_to '#'
6. else
7. render '#'
8. end
Line 1. raises the error:
Couldn't find Discussion without an ID
Also, on inspection it turns out that the #discussion variable is always NIL.
I think it's more of a helper function, devise's way is to get the ID through the session, but you could do the same through the params hash,
i.e
module ApplicationHelper
def current_forum_thread
Thread.find(params[:id])
end
end
does that work for you?
I'm putting before_filter :authenticate_user! on top of every controller, and then do something like this:
current_user.posts.new(params)
This also requires relation User has_many :posts
Seems to work (not sure if that's the best way though).
Also, your error seems to mean that your prarms[:id] is nil, so check if it's passing properly. You should be able to see that in the logs.
# Discussions controller - show action
#discussion = Discussion.find(params[:id])
render ...
# Discussion show view
%ul
- #discussion.posts.each do |post|
%li= post.content # to output list of posts
= form_for #discussion.posts.new do |f|
= f.input :content
= f.submit
# form to create new post related to this discussion
# Post controller - create method
#post = Post.new(params[:id])
#post.save!
render ...
Using the current_id with threads seems overly complex for this implementation, as it looks like a pretty simple nested resource.
The post isn't saving, because it couldn't find the discussion. Since you're on the Post controller and not Discussion, you need to be looking for the discussion with
#discussion = Discussion.find(params[:discussion_id])
The :id your searching for it with is from the post's parameters. It didn't find anything because you probably have significantly more posts that discussions. If it did find something it would be finding the wrong thing.
Another things on the check list to make a nested route work, is to get the routes right. Check them with 'rake routes' but it should look like this:
resources #discussions do
resources #posts
end
This will add the routes so your form, which should look something like <%= form_for [#discussion, #post] do |f| %> can post/put to the discussion_posts_path.
Using the current_id like you got into is really scoping, and it's kind of messy, Ryan Bate's has an awesome video on Multi-tenancy with scopes http://railscasts.com/episodes/388-multitenancy-with-scopes

Add RESTful Action

The source of my information is section 2.9 here:
[http://guides.rubyonrails.org/routing.html#connecting-urls-to-code][1]
What I'm trying to do is add a custom action "search" and corresponding view.
So, as it says to do in the documentation, I've added this code in my config/routes.rb file:
resources :dimensions do
collection do
get "search"
end
end
I've also defined in the dimensions_controller file:
def search
#dimensions = Dimension.all
respond_to do |format|
format.html # search.html.erb
format.json { render json: #dimensions }
end
end
I then stopped and restarted the rails server, but when I navigate to /dimensions/home, I'm still getting this error message:
Couldn't find Dimension with id=search
Also showing that my parameter is:
{"id"=>"search"}
So am I just missing another bit of code that gives the instruction to interpret /dimension/search as a collection action as opposed to the show action?
I've already confirmed that search_dimensions_path exists, so I know that the resource block in the routes.rb file is actually adding paths. It's just interpreting them as a separate search action that's giving me trouble.
Thanks for your time.
This code should work fine. Can you show us your routes.rb file?
On a side note, you probably don't want to have a separate action for searching, using the index action is the preferred way.
Found the issue:
I had to make the resource declaration in my config/routes.db file for dimensions after creating the collection action, like so:
resources :dimensions do
collection do
get "search"
end
end
resources :dimensions
Then everything worked as expected.

How to require a value is entered in a search form

I built a basic search form that queries one column in one table of my app. I followed episode 37 Railscast: http://railscasts.com/episodes/37-simple-search-form. Note I just posted another search related question, but it's on a completely different issue.
In my app, the search queries the zip code column of my profile table, and returns a list of profiles that contain the right zip code.
Here's my problem. Currently, when a user leaves the input blank and hits the submit button, the search displays all profiles on the site. I don't want this to happen. If the field is blank, I don't want the search to go through. I'd like to either do a flash notice or throw an error, explaining that the user needs to enter a zip code to proceed.
Here's my setup:
PROFILES CONTROLLER
def index
#profiles = Profile.search(params[:search])
end
PROFILE MODEL
def self.search(search)
if search
find(:all, :conditions => ['zip LIKE ?', "%#{search}%"])
else
find(:all)
end
end
PROFILE/INDEX.HTML.ERB
<% form_tag ('/profiles', :method => :get) do %>
<%= text_field_tag :search, params[:search], :maxlength => 5 %>
<%= submit_tag "Go", :name => nil %>
<% end %>
Thanks!
def index
#profiles = Profile.search(params[:search]) unless params[:search].blank?
end
You probably don't want to throw an error if the search field is blank, because the user will see that error the first time he comes to the index page. To properly handle that type of error message, you'll need to do one of several things.
Split the form generation and the actual search into two separate actions. In a RESTful app, this would typically be a new and create action. (new for the form, create for the actual search).
Add a check for a post, as opposed to a get. Only attempt the search, or throw the error, if it's a post. Otherwise, just show the form. You'll typically see this in older Rails examples (like pre- 2.0 tutorials).
Add some hidden field that says "Hey, I'm submitting a search." This is the same idea as checking for a post, but would still work if you wanted all gets for some reason.
My choice would be the first one. It'd roughly look like this.
def new
end
def create
if params[:search].blank?
flash.now[:error] = "Please enter a zip code to search for."
render :new
else
#profiles = Profile.search(params[:search])
render :show
end
end
In your views, new.html.erb (or .haml or whatever) would contain your search form and show.html.erb would contain your search results. Usually there's a search form partial that both of them would share.
You just need to check if it's blank before you do your search.
def index
if params[:search].blank?
flash[:error] = "Doh! You forgot the zip code."
else
#profiles = Profile.search(params[:search])
end
end
If returning all results is never a use case then you might want to remove that code from your model as well. Also if you're checking for a blank search in more than this one controller action you should move the logic into the model.
I actually found an answer to this that doesn't require me to make any changes to my current setup.
If you look in the search model above, which I copied from the Railscast, you see that Ryan included wildcards on both sides of the search query:
find(:all, :conditions => ['zip LIKE ?', "%#{search}%"])
I'm not familiar with sql syntax at all, so I missed that. It was those wildcards that was letting through the blank search and returning all results in the database. When I removed the "%'s" from that sql statement, the blank search now returns the same result as a search where we don't have a record matching the zip queried, which is the result I wanted.
Just wanted to share so others might catch this in the future.

Resources