How can I pass an object to rabl views from rake task - ruby-on-rails-3

I am trying to create a json file from a rake task using rabl. Below I have the simplified version to test with.
When I view 'articles.json' or 'articles/2.json' via the url, I get the expected json response.
But when I try to run it via the rake task, it always has a null value for #articles in the jsonFile created. It will render the index.json.rabl view the same number of times as #articles.count, but the values are always null.
So how do I pass in the #articles object created in my rake task to Rabl.render?
index.json.rabl
#feedName ||= 'default'
node(:rss) { partial('articles/rss), :object => #feedName }
node(:headlines) { partial('articles/show'), :object => #articles }
show.json.rabl
object #article
attributes :id,:body
....
exports.rake
task :breakingnews => :config do
filename = 'breakingnews.json'
jsonFile = File.new(filename)
#articles = Article.limit(10)
n = Rabl::renderer.json(#articles,'articles/index',view_paths => 'app/views')
jsonFile.puts b
jsonFile.close

I ran into a similar problem. You basically have 2 options:
Pass a single object explicitly as a parameter
Pass multiple objects implicitly by scope
By Parameter
In your task
#your_object = ...
Rabl.render(#your_object, 'your_template', view_paths => 'relative/path/from/project/root', :format => :json)
In your Rabl Template
object #any_name_you_like
attributes :id,:body
....
This will render your template as a json with the object specified as its instance object (you can name it anything you want)
By Scope
This is a bit more tricky. The only option I found is by setting the desired objects as instance variables in the calling scope and set this scope for the rendering of the template (see scope).
In your task
#one_object = ...
#another_object = ...
Rabl.render(nil, 'your_template', view_paths => 'relative/path/from/project/root', :format => :json, :scope => self)
In your Rabl Template
object #one_object
attributes :id,:body
node(:my_custom_node) { |m| #another_object.important_stuff }

Related

Rails index action always returns null

So I am having an issue where I have this index action that is supposed to return json, but in the browser I am always getting a null response.
Controller
class Admin::Groups::UsersController < Admin::GroupsController
def index
#user_groups = Group.includes(:group_type, :users).where("group_types.group_type_name = ?", 'Users').order("groups.group_name ASC")
render json: #user_groups.to_json(:include => [:group_type, :users], :methods => :enabled)
end
def show
#user_group = Group.includes(:group_type, :users).where("group_types.group_type_name = ?", 'Users').find_by_id(params[:id])
render json: #user_group.to_json(:include => [:group_type, :users], :methods => :enabled)
end
end
Route file
namespace :admin do
namespace :groups do
resources :users, only: [:index, :show] do
get 'enabled', :on => :collection
get 'disabled', :on => :collection
end
end
end
Output of rake routes
admin_groups_users GET /admin/groups/users(.:format) admin/groups/users#index
What is really throwing me off is that when I run the same code that is inside the index action in the rails console, it is outputting exactly what I expected. Also the show action works fine. And just for testing, I added a new route that I just called index2 and copied + pasted the code from the index action into index2 and I got the correct output in the browser.
So it has to be something to do with my route, but I'm not sure what it is?
Edit 1: Add entire output of rake routes
enabled_admin_accounts GET /admin/accounts/enabled(.:format) admin/accounts#enabled
disabled_admin_accounts GET /admin/accounts/disabled(.:format) admin/accounts#disabled
account_owners_admin_accounts GET /admin/accounts/account_owners(.:format) admin/accounts#account_owners
admin_accounts GET /admin/accounts(.:format) admin/accounts#index
admin_account GET /admin/accounts/:id(.:format) admin/accounts#show
enabled_admin_users GET /admin/users/enabled(.:format) admin/users#enabled
disabled_admin_users GET /admin/users/disabled(.:format) admin/users#disabled
admin_users GET /admin/users(.:format) admin/users#index
admin_user GET /admin/users/:id(.:format) admin/users#show
enabled_admin_owners GET /admin/owners/enabled(.:format) admin/owners#enabled
disabled_admin_owners GET /admin/owners/disabled(.:format) admin/owners#disabled
currencies_admin_owner GET /admin/owners/:id/currencies(.:format) admin/owners#currencies
admin_owners GET /admin/owners(.:format) admin/owners#index
admin_owner GET /admin/owners/:id(.:format) admin/owners#show
enabled_admin_counterparties GET /admin/counterparties/enabled(.:format) admin/counterparties#enabled
disabled_admin_counterparties GET /admin/counterparties/disabled(.:format) admin/counterparties#disabled
admin_counterparties GET /admin/counterparties(.:format) admin/counterparties#index
admin_counterparty GET /admin/counterparties/:id(.:format) admin/counterparties#show
enabled_admin_venues GET /admin/venues/enabled(.:format) admin/venues#enabled
disabled_admin_venues GET /admin/venues/disabled(.:format) admin/venues#disabled
admin_venues GET /admin/venues(.:format) admin/venues#index
admin_venue GET /admin/venues/:id(.:format) admin/venues#show
enabled_admin_custody_banks GET /admin/custody_banks/enabled(.:format) admin/custody_banks#enabled
disabled_admin_custody_banks GET /admin/custody_banks/disabled(.:format) admin/custody_banks#disabled
admin_custody_banks GET /admin/custody_banks(.:format) admin/custody_banks#index
admin_custody_bank GET /admin/custody_banks/:id(.:format) admin/custody_banks#show
enabled_admin_client_currencies GET /admin/client_currencies/enabled(.:format) admin/client_currencies#enabled
disabled_admin_client_currencies GET /admin/client_currencies/disabled(.:format) admin/client_currencies#disabled
admin_client_currencies GET /admin/client_currencies(.:format) admin/client_currencies#index
admin_client_currency GET /admin/client_currencies/:id(.:format) admin/client_currencies#show
admin_currencies /admin/currencies(.:format) admin/client_currencies#index
admin_all_currencies /admin/all_currencies(.:format) admin/currencies#index
enabled_admin_groups GET /admin/groups/enabled(.:format) admin/groups#enabled
disabled_admin_groups GET /admin/groups/disabled(.:format) admin/groups#disabled
admin_groups GET /admin/groups(.:format) admin/groups#index
admin_group GET /admin/groups/:id(.:format) admin/groups#show
enabled_admin_groups_accounts GET /admin/groups/accounts/enabled(.:format) admin/groups/accounts#enabled
disabled_admin_groups_accounts GET /admin/groups/accounts/disabled(.:format) admin/groups/accounts#disabled
admin_groups_accounts GET /admin/groups/accounts(.:format) admin/groups/accounts#index
admin_groups_account GET /admin/groups/accounts/:id(.:format) admin/groups/accounts#show
enabled_admin_groups_account_owners GET /admin/groups/account_owners/enabled(.:format) admin/groups/account_owners#enabled
disabled_admin_groups_account_owners GET /admin/groups/account_owners/disabled(.:format) admin/groups/account_owners#disabled
admin_groups_account_owners GET /admin/groups/account_owners(.:format) admin/groups/account_owners#index
admin_groups_account_owner GET /admin/groups/account_owners/:id(.:format) admin/groups/account_owners#show
enabled_admin_groups_users GET /admin/groups/users/enabled(.:format) admin/groups/users#enabled
disabled_admin_groups_users GET /admin/groups/users/disabled(.:format) admin/groups/users#disabled
admin_groups_users GET /admin/groups/users(.:format) admin/groups/users#index
admin_groups_user GET /admin/groups/users/:id(.:format) admin/groups/users#show
enabled_admin_groups_counterparties GET /admin/groups/counterparties/enabled(.:format) admin/groups/counterparties#enabled
disabled_admin_groups_counterparties GET /admin/groups/counterparties/disabled(.:format) admin/groups/counterparties#disabled
admin_groups_counterparties GET /admin/groups/counterparties(.:format) admin/groups/counterparties#index
admin_groups_counterparty GET /admin/groups/counterparties/:id(.:format) admin/groups/counterparties#show
enabled_admin_groups_currencies GET /admin/groups/currencies/enabled(.:format) admin/groups/currencies#enabled
disabled_admin_groups_currencies GET /admin/groups/currencies/disabled(.:format) admin/groups/currencies#disabled
admin_groups_currencies GET /admin/groups/currencies(.:format) admin/groups/currencies#index
admin_groups_currency GET /admin/groups/currencies/:id(.:format) admin/groups/currencies#show
root / trades#index
jasminerice /jasmine Jasminerice::Engine
You need to move this route further down the route list.
admin_group GET /admin/groups/:id(.:format) admin/groups#show
The route list is searched in-order. /admin/groups/users is matching this route with :id equal to users.
render is either in the form
render :json => #user_groups
in which it calls to_json for you and it cannot pass arguments, or you can do this:
format.json { render #user_groups.to_json(:include => [:group_type, :users], :methods => :enabled) }
If you always want to return json no matter what format is, then just get rid of the json: thing:
render #user_groups.to_json(:include => [:group_type, :users], :methods => :enabled)
Please change your routes to this and check
namespace :admin do
namespace :groups do
resources :users, :only => [:index,:show],:collection => {:enabled => :get,:disabled => :get}
end
end

Mongoid dynamic query

This must be an easy one, but I'm stuck...
So I'm using Rails#3 with Mongoid and want to dynamically build query that would depend upon passed parameters and then execute find().
Something like
def select_posts
query = :all # pseudo-code here
if (params.has_key?(:author))
query += where(:author => params[:author]) # this is pseudo-code again
end
if (params.has_key?(:post_date))
query += where(:create_date => params[:post_date]) # stay with me
end
#post_bodies = []
Post.find(query).each do |post| # last one
#post_bodies << post.body
end
respond_to do |format|
format.html
format.json { render :json => #post_bodies }
end
end
You have a few different options to go with here - depending on how complex your actual application is going to get. Using your example directly - you could end up with something like:
query = Post.all
query = query.where(:author => params[:author]) if params.has_key?(:author)
query = query.where(:create_date => params[:post_date]) if params.has_key?(:post_date)
#post_bodies = query.map{|post| post.body}
Which works because queries (Criteria) in Mongoid are chainable.
Alternatively, if you're going to have lots more fields that you wish to leverage, you could do the following:
query = Post.all
fields = {:author => :author, :post_date => :create_date}
fields.each do |params_field, model_field|
query = query.where(model_field => params[params_field]) if params.has_key?(params_field)
end
#post_bodies = query.map{|post| post.body}
And finally, you can take it one level further and properly nest your form parameters, and name the parameters so that they match with your model, so that your params object looks something like this:
params[:post] = {:author => "John Smith", :create_date => "1/1/1970", :another_field => "Lorem ipsum"}
Then you could just do:
#post_bodies = Post.where(params[:post]).map{|post| post.body}
Of course, with that final example, you'd want to sanitize the input fields - to prevent malicious users from tampering with the behaviour.

How to make tests with find and date conditions?

I'm trying to develop some tests for a method which is responsible for retrieve some users created after some date. I don't know how to mock tests for it. The method is the following:
def user_list
render :nothing => true, :status => 422 if params[:time_param].blank?
time = Time.parse(params[:time_param])
#users = User.find(:all, :select => 'id, login, email',
:conditions => ["created_at > ?", time])
render :json => { :users => #users }
end
end
This is my spec:
describe UsersController do
context "when receiving time parameter" do
before (:each) do
#time_param = "2013-01-25 00:01:00"
user1 = mock_model(User, :created_at => Time.parse('2013-01-25 00:00:00'))
user2 = mock_model(User, :created_at => Time.parse('2013-01-25 00:01:00'))
user3 = mock_model(User, :created_at => Time.parse('2013-01-25 00:02:00'))
#users = []
#users << user1 << user2 << user3
end
it "should retrieve crimes after 00:01:00 time" do
User.stub(:find).with(:all, :select => 'id, login, email').and_return(#users)
get :user_list, { :time_param => #time_param }
JSON.parse(response.body)["users"].size.should eq 1
end
end
end
The problem is that it always returns all users despite of returning just one. (the last one). Where am I mistaking?
Help me =)
You are not testing what you have to test there, on a controller spec you only need to test that the method that you want is called with the parameters that you want, in your case, you have to test that the User model receives :find with parameters :all, :select => 'id, login, email', :conditions => ["created_at > ?", time] (with time the value that should be there.
Also, that logic does not belong to the controller, you should have a class method on User, something like select_for_json(date) to wrap around that find method (you can find a better name for it)
Then your controller becomes:
def user_list
render :nothing => true, :status => 422 if params[:time_param].blank?
time = Time.parse(params[:time_param])
#users = User.select_for_json(time)
render :json => { :users => #users }
end
your spec would be
before(:each) do
#users = mock(:users)
#time_param = "2013-01-25 00:01:00"
end
it "retrieve users for json" do
User.should_receive(:select_for_json).once.with(#time).and_return(#users)
get :user_list, { :time_param => #time }
assigns(:users).should == #users
end
that way you are sure that your action does what it does and the spec is A LOT faster since you are not creating users
then you can test that method on the model specs, there you have to create some users, invoke that method and check the users returned (don't stub/mock anything on your model spec)
Your stub call is telling find to ignore what it thought it was supposed to do and return #users instead. It will not attempt to match the conditions.
Unfortunately, to do your test I think you're going to have to allow the find to execute through your database which means you can't use mock_models. You probably will want to do either User.create(...) or FactoryGirl.create(:user) (or some other factory / fixture).
Of course doing it this way, you may hit MassAssignment issues if you use attr_accessible or attr_protected, but those are easy enough to stub out.
I hope that helps.

Rails 3: Controller params default value

I am using a remote form_for for my show action to retrieve content based on the params passed by this form.
= form_tag modelname_path(#modelname), :id=>"select_content_form", :remote => true, :method => 'get' do
= text_field_tag :content_type, params[:content_type], :id=>"select_content_type"
= submit_tag "submit", :name => nil, :id=>"select_content_submit"
And I alter the content in controller as follows:
# Default params to "type1" for initial load
if params[:content_type]
#content_type = params[:content_type];
else
#content_type = "type1"
end
case #content_type
when "type1"
# get the content
#model_content = ...
when "type1"
# get the content
#model_content = ...
My question is, whether the above approach is the only we can set defaults for params or can we do it in a better manner. This works but I would like to know if this is the right approach.
UPDATE
Based on the suggestion below, I used the following and got an error on defaults.merge line:
defaults = {:content_type=>"type1"}
params = defaults.merge(params)
#content_type = params[:content_type]
A good way of setting default options is to have them in a hash, and merge your incoming options onto it. In the code below, defaults.merge(params) will overwrite any values from the params hash over the default ones.
def controller_method
defaults = {:content=>"Default Content", :content_type=>"type1"}
params = defaults.merge(params)
# now any blank params have default values
#content_type = params[:content_type]
case #content_type
when "type1"
#model_content = "Type One Content"
when "type2"
#etc etc etc
end
end
If there is a static list of types you could make it a dropdown box and just don't include a blank option so that something is always selected. But if you're stuck with a textbox you could clean up the controller action by using a before filter:
class FoosController < ActionController::Base
before_filter :set_content_type, :only => [:foo_action]
def foo_action
...
end
protected
def set_content_type
params[:content_type] ||= "type1"
end
end
I wanted to add to this discussion a working way to set default params:
defaults = { foo: 'a', bar: 'b' }
params.replace(defaults.merge(params))
This avoids assigning a local variable via "params =".

Rspec testing complex code with undefined variables

Just need alittle bit of help with rspec testing... very new to it and dont quite know how to test this piece of code
# Get a list of tasks recently used by the user
recent_task_ids = Effort.all( :select => "project_task_id",
:conditions => { :user_id => #user_id },
:group => "project_task_id",
:order => "MAX( week_commencing )" )
# For each of the task ids, get the actual project task records
#recent_tasks = []
recent_task_ids.each do |effort|
if ProjectTask.find_by_id( effort.project_task_id ) != nil
#recent_tasks.push( ProjectTask.find( effort.project_task_id ) )
end
end
Not sure if your even supposed to test undefined variables this way but any help would be great
You can stub out the Effort.all method.
it "tests with nil values" do
Effort.stub(:all).and_return(nil)
end
Source: http://rspec.info/documentation/mocks/stubs.html and http://rspec.info/documentation/mocks/