Where do I put a rails method/scope that uses params? - ruby-on-rails

I have a scope that uses RubyGeocoder method, near, to filter events by location using param[:searchCity]. The param gets the user's geolocation so it shows events only near them. I currently have it working in my events_controller index action, but I also need to call it on my home page.
Considering it's a filter that gets data from the database, I thought it would be best to go in the model, but I'm finding conflicting information on whether having a param in the model is ok or bad practice. Also, I can't get it to work in the model with the param present.
What's the best practice for something like this? Where should I place the scope, the model, controller, helper, or somewhere else?
Here's my code:
Model:
class Event < ActiveRecord::Base
# attr, validates, belongs_to etc here.
scope :is_near, self.near(params[:searchCity], 20, :units => :km, :order => :distance) #doesn't work with the param, works with a "string"
end
Controller:
def index
unless params[:searchCity].present?
params[:searchCity] = request.location.city
end
#events = Event.is_near
# below works in the controller, but I don't know how to call it on the home page
# #events = Event.near(params[:searchCity], 20, :units => :km, :order => :distance)
respond_to do |format|
format.html # index.html.erb
format.json { render json: #events }
end
end
The line I'm calling in my home page that gets how many events are in the area
<%= events.is_near.size %>
Edit: Using a lambda seems to be working. Is there any reason I shouldn't do it this way?
Model:
class Event < ActiveRecord::Base
scope :is_near, lambda {|city| self.near(city, 20, :units => :km, :order => :distance)}
end
Controller:
def index
#events = Event.is_near(params[:searchCity])
...
home.html.erb
<%= events.is_near(params[:searchCity]).size %>

Accessing the params in model is not possible. Params is something which is made to exist only at controller and view level.
So best way is to write some helper method in controller to perform this.
Class Mycontroller < ApplicationController
before_action fetch_data, :only => [:index]
def fetch_data
#data = Model.find(params[:id])#use params to use fetch data from db
end
def index
end

Related

ActiveAdmin limit records on Index page

I have some 10,000+ records in my model. In active_admin index page for that model I have set config.paginate = false. So all the 10,000+ records are shown by default.
How can I limit the number to say last 500 records. I have tried using the below method described here, but its not doing anything to the index page.
ActiveAdmin.register Post do
controller do
def scoped_collection
Post.all.limit(500)
end
end
end
set custom # of rows on page with controller before_filter
controller do
before_filter :set_per_page_var, :only => [:index]
def set_per_page_var
session[:per_page]=params[:per_page]||30
#per_page = session[:per_page]
end
end
and render sidebar with corresponding text input (you can render it as a drop-list)
#...
sidebar('Rows on page', :only => :index) do
form do |f|
f.text_field nil, 'per_page', :value => session[:per_page]
end
end
The issue is this code in Active Admin:
module ActiveAdmin
class ResourceController < BaseController
module DataAccess
def per_page
return max_csv_records if request.format == 'text/csv'
return max_per_page if active_admin_config.paginate == false
#per_page || active_admin_config.per_page
end
def max_csv_records
10_000
end
def max_per_page
10_000
end
end
end
end
When the paginate config option is set to false, it defaults to the number value returned by max_per_page. If you're fine with overriding it globally, you can put this in an initializer:
# config/initializers/active_admin_data_access.rb
module ActiveAdmin
class ResourceController < BaseController
module DataAccess
def max_per_page
500 # was 10,000
end
end
end
end
I was looking for an answer to this same question. I was unable to limit the number of records, so instead I have opted for putting a default value in one of my filters that guarantees an empty page when it loads.
(NOTE: I stole this idea from this stackoverflow question here:: Set ActiveAdmin filter default value )
Example::
In this example, I set a filter called "my_filter_id" equal to "0" in the "before_filter" method if all of the parameters are blank.
ActiveAdmin.register MyModel do
before_filter my_filter_id: :index do
params[:q] = {my_filter_id_eq: 0} if params[:commit].blank?
end
end
Use
Post.limit(500) instead of Post.all.limit(500) so it will minimize the latency.
controller do
def scoped_collection
Post.limit(500)
end
end
index :pagination_total => false do
selectable_column
column :id
column :user_name
column :country
column :city
end
Hope this will help someone.
Try below code. Replace something with your model name.
result = Something.find(:all, :order => "id desc", :limit => 5)
while !result.empty?
puts result.pop
end

Moving Logic to Model

In my Rails 4 app I am starting to move my logic to the model (working on the Fat Model principle). However, I'm a little unsure on how best to tackle the basics.
My app displays bookings for the logged in user. In my BookingsController I have an action that displays the confirmed bookings:
def confirmed
#bookings = Booking.where(:status => 3, :accommodation_id => current_user.accommodation.id).order('updated_at DESC')
end
However, I have tried to move the logic to the controller so that the BookingsController now looks like this:
def confirmed
#bookings = Booking.confirmed_bookings
end
and the Booking model looks like this:
def confirmed_bookings
bookings = where(:status => 3, :accommodation_id => current_user.accommodation.id).order('updated_at DESC')
end
I am receiving an undefined method `confirmed_bookings' error and not sure what I'm doing wrong. A fix for this and some simple "starter" advice would be much appreciated.
Split your scopes in reusable chunks:
scope :confirmed, ->{ where(:status => 3) }
scope :for_user, ->(user) { where(:accommodation_id => user.accommodation.id) }
def self.confirmed_bookings_for(user)
confirmed.for_user(user).order('updated_at DESC')
end
Notice, it' would be much better not to refer to the user at all, since it's for an accommodation.
This should work if you change the confirmed_bookings method to
self.confirmed_bookings
...
end
[EDIT] Missed the part about the current user, that function will be unavailable in the model, one solution is to pass it as a parameter to the confirmed bookings function:
class Booking < ActiveRecord::Base
def self.confirmed_bookings_for (user)
where(:status => 3, :accomodation_id => user.accomodation.id).order('updated_at DESC')
end
...
end
Then in your controller you can write
#bookings = Booking.confirmed_bookings_for current_user

Including virtual attributes when responding_to a JSON call

I have a post model that has a virtual attribute that I would like to set and then include in a response to a JSON call to my post#index action. I can't seem to get the virtual attribute to be included in the response.
class Post < ActiveRecord::Base
attr_accessible :height
attr_accessor :m_height
end
class PostsController < ApplicationController
respond_to :html, :json, :js
def index
story = Story.find(params[:story_id])
#posts = story.posts.where("posts.id >= ?", 100)
#posts.each do |post|
post.m_width = post.height * 200
end
results = { :total_views => story.total_views,
:new_posts => #posts }
respond_with(results)
end
end
I think that I must need something similar to #post.to_json(:methods => %w(m_width)), but I don't see how to use :methods in a respond_with
This seems to provide the answer. Implement a to_json and to_xml in your models, as appropriate, with definitions like:
There's a better answer implied here.
Following code stolen from the post:
def as_json(options={})
super(options.merge(:methods => [...], :only => [...], :include => [...])
end
to_json won't be called on your model in this case, from what I can tell in the source, but as_json will be, in the process of serialization.
So, here's what happens, in overview form:
You call respond_with with the results hash you've constructed.
Rails (ActionController) calls to_json on that.
to_json sends you over to JSON::Encoding which keeps calling as_json all the way down until everything is JSONified.
That's why there was the confusion about to_json and as_json in an earlier version of this answer.

Rails as_json with two unrelated models

Given two models and a controller:
Apples
class Apples < ActiveRecord::Base
belongs_to :not_oranges
...
def as_json(options={})
opts = {:include => [:not_oranges]}
super(options.reverse_merge! opts)
end
end
Oranges
class Oranges < ActiveRecord::Base
belongs_to :not_apples
...
def as_json(options={})
opts = {:include => [:not_apples]}
super(options.reverse_merge! opts)
end
end
Search Controller
class SearchController < ApplicationController
a = Apples.search params[:q]
o - Oranges.search params[:q]
#results = {
:apples => a,
:oranges => o
}
respond_to do |format|
format.json { render :json => #results }
end
As you can see, the two models are completely unrelated and both have different :include options in their as_json definitions.
All works as expected if the search query only hits apples or only hits oranges, but once both objects aren't empty I get:
undefined method `not_apples' for #<Oranges:0x00000004af8cd8>
Seems either the two as_json definitions are being merged, or Oranges.as_json is being overriden by Apples.as_json.
Is this expected behaviour? Is there any clean way around it without using something like RABL? I feel it would be overkill for my needs.
In pseudo code the code for hash as_json method looks like
def as_json(options={})
Hash[collect {|key,element| [key.to_s,element.as_json(options)]}]
end
But your element is modifying the options argument you pass to it. Hash is unaware of this and so passes the modified options hash to as json.
It's usually a good idea not to modify in place the arguments passed to you, except when it is very clear this is ok. I'd rewrite your method as
def as_json(options={})
defaults = {:include => :not_apples}
super(defaults.merge(options))
end

Passing a variable into a models after_initialize method

I have the following (heavily simplified) model, that uses will_paginate
class Search < ActiveRecord::Base
attr_reader :articles
def after_initialize
#articles = Article.paginate_by_name name, :page => 1
end
end
and the controller code in my show action is
#search = Search.new(params[:search])
This all works fine, but notice i hard coded the page number to 1, problem is passing params[:page] value into the after_initialize method, can anyone suggest an elegant way to do this please?
Thanks
Add a page parameter (or even better an options hash parameter) to the initialize method:
class Search
def initialize(search, options = {})
#options = options
end
def after_initialize
#articles = Article.paginate_by_name name, :page => #options[:page]
end
end
and then in your controller:
#search = Search.new(params[:search], :page => params[:page])
You can even supply default values to the option hash, if you like.

Resources