Parsing request.request_parameters in Rails - ruby-on-rails-3

I'm building a web service app and I'm trying to handle nicely a 422 page sending back to the user the JSON the POSTed to better debug the error. To do this, I use request.request_parameters which get me back the JSON I sent, but it happens to be organized (for me) in a wired way and I can't really get it back only with the original data
What I send as JSON is this.
{
"name":"New set intensity",
"properties":
[
{"uri":null,"value":"on"},
{"uri":"https://type.lelylan.com/properties/intensity","value":"100.0"}
]
}
What I get from request.request_parameters is this.
{"{\"name\":\"New set intensity\",\"properties\":"=>{"{\"uri\":null,\"value\":\"on\"}, {\"uri\":\"https://type.lelylan.com/properties/intensity\",\"value\":\"100.0\"}"=>{"}"=>nil}}}
My main problem is that somehow the content becomes the key, and this recursively inside. Is there a way to get back the clean data? Thanks a lot.
UPDATE: I'm trying to better understand where and why this problem occurs.
In my controller I tried to access in the two available ways I know.
# request.body.read.inspect
"{\"name\":\"New set intensity\",\"properties\":[{\"uri\":\"not_valid\"}]}"
# request.request_parameters
{"{\"name\":\"New set intensity\",\"properties\":"=>{"{\"uri\":\"not_valid\"}"=>{"}"=>nil}}}
The request is made from Capybara
page.driver.post(#uri, #params.to_json)
The controller returns only JSON so this is the way I defined it. I din't put 'respond_to' and 'respond_with' and when I make the request it renders the json view show.rabl.json. This makes me think that it recognize the correct format.
class FunctionsController < ApplicationController
before_filter
...
def index
...
end
def show
..
end
def create
body = JSON.parse(request.body.read)
#function = Function.new(body)
if #function.save
render 'show', status: 201, location: FunctionDecorator.decorate(#function).uri
else
render_422 "notifications.resource.not_valid", #function.errors
end
end
Thanks.

Related

Right way to dynamically update view in Angular

What is the right way to updated the Model in the view, say after a successful API POST. I've a textarea, something like in a Twitter, where a user can enter text and post. The entered text must show up soon after it is posted successfully.
How to achieve this? Should I make another call to get the posts separately or is there any other way to do this?
My Code looks like
feedsResolve.getFeeds().then(function(feeds){
$scope.feeds = feeds;
}
where feedsResolve is a service returning a promise
$scope.postFeed = function(){
var postObj = Restangular.all('posts');
postObj.post( $scope.feed.text ).then(function(res){
//res contains only the new feed id
})
}
How do I update the $scope.feeds in the view?
I assume you are posting a new post and that generally posts look like:
{
id: 42,
text: 'This is my text'
}
In this case you can do something like:
$scope.postFeed = function(){
var postObj = Restangular.all('posts');
var feedText = $scope.feed.text;
postObj.post( feedText ).then(function(res){
$scope.feeds.push({ id: res.id, text: feedText});
})
};
A better practice when writing restful service though is to just have your POST return an actual JSON object with the new feed that was added (not just the id). If that were the case you could just add it to your feeds array.
If your JSON object is complex, this practice is the most common an easiest way to handle this without needing extra requests to the server. Since you already are on the server, and you've likely already created the object (in order to be able to insert it into the database), all you have to do is serialize it back out to the HTTP response. This adds little to no overhead and gives the client all the information it needs to effortlessly update.

Angular JS ngResource with nested resources

I'm trying out angular JS and I want to get data from a nested resource defined in my rails application.
I wrote the following lines:
UserMission = $resource("/users/:user_id/user_missions/:id", {user_id: "#user_id", id: "#id"}, {update: {method: "PUT"}})
$scope.user_missions = UserMission.query()
and I get the following error:
Processing by UsersController#show as JSON
Parameters: {"id"=>"user_missions"}
User Load (0.6ms) SELECT "users".* FROM "users" WHERE "users"."id" = $1 LIMIT 1 [["id", 1]]
User Load (0.5ms) SELECT "users".* FROM "users" WHERE "users"."id" = $1 LIMIT 1 [["id", "user_missions"]]
Completed 404 Not Found in 10ms
ActiveRecord::RecordNotFound (Couldn't find User with id=user_missions):
app/controllers/users_controller.rb:100:in `current_resource'
app/controllers/application_controller.rb:34:in `authorize'
My rails routes are organized like so:
resources :users do
resources :user_missions
end
I think it comes down to me not understanding "#id". It says it comes off of the "data object" from the angularjs site and I am not exactly sure what that means.
Any help would be appreciated thanks.
Update
Another question. I have not found any examples of rails with angularjs using nested resources (an example User has_many :missions, through: :user_missions ) with $resource. Is there a good example of angularjs manipulating nested resources (with $resource) with ajax?
Read through my answer to your previous question, you should include the values for both the parameters in an object you pass as a parameter in the call, i. e.:
$scope.user_missions = UserMission.query({user_id: some_value, id: some_other_value});
I was also looking for a way to handle nested resources with ngResource. I am not familiar with how rails works or what your data looks like but this is what I had:
{"num_results": 5, "objects": [....], "page": 1, "total_pages": 1}
I needed to reach in and grab the nested objects array for the query action. Using the 1.0 version of angular this is not possible. However, with the 1.1 version (I tested with 1.1.3) it is possible to do this.
In my controller I just setup the resource like this:
$scope.MyModel = $resource("/api/mymodel/:id",
{},
{'query': {method: 'GET', isArray: true, "transformResponse": function (data) {
return JSON.parse(data).objects;
}}});
The key here is the transformResponse function passed as part of the action config. In 1.1 any extra config items in the action config are passed down into the $http config for the request. The $http service allows a transformResponse function that can manipulate the data returned by the request. Using this function I can reach into the nested structure and return the array I need for the action.
One caveat to notice here is that the transformResponse function receives a string value, so you must first parse the data into what you are expecting. The other caveat is that you return the actual final data value you want, NOT a string. Even though you receive a string value you should return the end data value required.
The "data object" refers to your instance objects ($scope.user_missions is an array of those objects -- or would be rather, if you actually managed to successfully retrieve it), and you can think of UserMission as being the class.
If you call the query method on the class object (UserMission), in your case, it requires at least one parameter in order to know which User's UserMission(s) to retrieve.
UserMission.query({user_id: <USER_ID>});
The above would perform a "GET ALL"
To obtain a specific UserMission ( "GET ONE" ), you would have to supply both IDs
UserMission.query({user_id: <USER_ID>, id: <MISSION_ID>});
Makes sense?

Ruby on Rails: Basic parameterized queries and URL formation

I'm trying to learn how to query a rails database and return the results as JSON. In my example, I want to query the data using the parameters, city and state.
So far, in my controller, I have gotten the following action to work.
def state
#bathrooms = Bathroom.where("state = ?" ,params[:state])
respond_to do |format|
format.json { render :json => #bathrooms }
format.js { render :nothing => true }
end
end
This is also my routing entry.
match '/bathrooms/state/:state',
:controller => "bathrooms",
:action => "state"
I can call this resource with the following URL:
http://localhost:3000/bathrooms/state/CA.json
That's all good but I don't know how to query by more than one parameter. Adding and AND clause in the controller seems to be easy enough.
BUT....I don't know how to
a.) Correctly write the routing entry?
b.) What would the URL look like if I tested it in a browser?
I've tried to understand rake routes but I must be missing something.
Could someone provide a basic example for what the action should look like? What the routing entry should look like and what does the URL to access the resource look like?
Again, if written in SQL, this is what I would like to be returned.
SELECT * from bathrooms WHERE city='Chicago' AND state = 'IL'
Any help appreciated.
You don't have to pass everything by the route - the URL also support GET parameters - those are the parameters you usually see after the question mark in the URL. You can add those GET parameters without changing your routes: http://localhost:3000/bathrooms/state/IL.json?city=Chicago. Then your can access the city parameter via params[:city]. but in your case, I think it will be better to use http://localhost:3000/bathrooms/index.json?state=IL&city=Chicago. You'll also need to change your routing to
match '/bathrooms/index',
:controller=>:bathrooms,
:action=>:index
and put the code in the index method of BathroomsController. You access the parameters the same - but the concept is different - you don't enter a state and look for bathrooms by city, you just look for bathrooms by state and city.
Anyways, you don't want to write the URL by hand - you want to a Rails helper or an HTML form generate it:
link_to "bathroom in Chicago, IL",:controller=>:bathrooms,:action=>:index,:state=>'IL',:city=>'Chicago'
If you want to use a form(to let the users choose their own state and city), you need to set it's method to GET:
form_tag {:controller=>:bathrooms,:action=>:index},:method=>:get do
and put state and city as fields.
It's also worth noting that while you can use SQL's AND to perform a search by multiple fields, you can also chain where methods: Bathroom.where(:state=>params[:state]).where(:city=>params[:city]).
You can put any arbitrary parameters in your querystring.
For example:
http://localhost:3000/bathrooms/state/CA.json?city=Chicago
your query looks like this:
#bathrooms = Bathroom.where("state = ? and city= ?" ,params[:state], params[:city])

Nested Resource

I would like to display Brand and Subbrand together on a view page. I am getting an error when I try to declare:
class BrandsController < ApplicationController
def index
#brands = Brand.all
#subbrands = #brands.subbrands #error is coming from this line
Error:
undefined method `subbrands' for #<Array:0x9af1898>
I can't seem to get this working, for the life of me!
I originally posted about my problem in this post:
Undefined method
I have tried to put the logic into the controller as above to see if that helps, but I am still getting the error. The details of the models and routing can be found in my original post.
Please help!
#brands is an Array, not a single ActiveRecord object, so you can't call the subbrands association directly on it. Since you're calling Brand.all, and I assume all Subbrand items are associated with a Brand, you might as well just query all of the Subbrand objects separately:
#brands = Brand.all
#subbrands = Subbrand.all
However, if you were trying to only get a subset of the brands, you could do this. (The second line queries all the subbrands that have a brand_id included in #brands).
#brands = Brand.where(...)
#subbrands = Subbrand.where(:brand_id => #brands.collect(&:id))

What is the proper RESTful way to "like" something in Rails 3?

Let's say I have a Rails 3 app that displays videos. The user can "Like" or "Dislike" the videos. Also, they can like/dislike other things like games. I need some help in the overall design and how to handle the RESTful routes.
Currently, I have a Like Class that uses polymorphic design so that objects are "likeable" (likeable_id, likeable_type)
I want to do this via AJAX (jQuery 1.5). So I was thinking something like:
javascript
// these are toggle buttons
$("likeVideo").click( function() {
$.ajax({
url: "/likes/video/" + video_id,
method: "POST",
....
});
} );
$("likeGame").click( function() {
$.ajax({
url: "/likes/game/" + game_id,
method: "POST",
....
});
} );
rails controller
Class Likes < ApplicationController
def video
# so that if you liked it before, you now DON'T LIKE it so change to -1
# or if you DIDN'T like it before, you now LIKE IT so change to 1
# do a "find_or_create_by..." and return JSON
# the JSON returned will notify JS if you now like or dislike so that the
# button can be changed to match
end
def game
# same logic as above
end
end
Routes
match "/likes/video/:id" => "likes#video", :as => :likes_video
match "/likes/game/:id" => "likes#game", :as => :likes_game
Does this logic seem correct? I am doing a POST via AJAX. Technically, shouldn't I be doing a PUT? Or am I being too picky over that?
Also, my controller uses non-standard verbs. Like video and game. Should I worry about that? Sometimes I get confused on how to match up the "correct" verbs.
An alternative would be to post to something like /likes/:id with a data structure that contains the type (game or video). Then I could wrap that in one verb in the controller...maybe even Update (PUT).
Any suggestions would be appreciated.
Rest architectural style does not specify which "verb" you should be using for what. It simply says that one can use HTTP if they want to for connectors.
What you are looking for is HTTP specifications for method definitions. In particular POST is intended for:
- Annotation of existing resources;
- Posting a message to a bulletin board, newsgroup, mailing list,
or similar group of articles;
- Providing a block of data, such as the result of submitting a
form, to a data-handling process;
- Extending a database through an append operation.
while PUT:
requests that the enclosed entity be stored under the supplied Request-URI. If the Request-URI refers to an already existing resource, the enclosed entity SHOULD be considered as a modified version of the one residing on the origin server.
Which category your functionality falls into is up to you - as long as you are consistent with yourself about it.