Right now I have a page with a custom template.
I would like to use a decorator before filter like so in the pages decorator:
before_filter :get_gallery_index, :only => [:gallery_main]
To define this method I would like to declare something similar to:
def get_gallery_index
# a = Refinery::Page.where(id: 6).children <- would be ideal
end
Of course, children is not a valid method with Refinery Pages and ActiveRecord. Does anyone have a suggestion of how I can get these records?
Got it by searching through parent_id:
def get_gallery_index
#gallery_index = Refinery::Page.where(:parent_id => 6)
end
Related
I have an html table in my rails app where I sholl all my expenses. Expenses are an enum, which can take values like "education", "beer", "bliblablub".
I would like to be able to filter my html table according to these enum values. I have links on the side of the table on which the user can click. Now I want the table to filter the results according to the links.
Now, I think scopes are the way to go here, but I'm having a hard time understanding how they really work. I created a scope like this just to understand it:
scope :find_category, -> {Expense.where(category:"education")}
In the Rails console this works, and when I call the find_category method it gives me the instances with education.
I think a dynamic scope then would be something like:
scope :find_category, -> (category) {Expense.where(category:category)}
So far so good. Now, what I don't really get is how I can use the filtered result now in my controller and view, that is to say how to filter it when clicking on the link.
I tried this:
controller (trying to get my queried results)
def find_category
#expense = Expense.find_category
render action: :index
end
And then put a route for find_categories:
resources :expenses, path: 'expenses' do
collection do
get :find_category
end
end
and put a link in the index:
<%= link_to 'education', {controller: 'expenses', action: 'index', :find_category => 'education'} %>
I know this is not really the way. Clicking on the link though it gives me expenses?find_category=education. Yet nothing changes. So I struggle to find the right way to do it. Of course it would also be awesome to do that without page reload, so I guess I have to use an AJAX call and JavaScript. But also with page reload would help me a lot.
When using enum, Rails creates the methods for you, so you can call directly the category, for example Expense.education.
Here is an example of how to manage it, pretty raw, assuming you are using standard Rails conventions. No special routing required.
Define a constant containing the categories, for example in concerns:
# model/concerns/expenses_stuff.rb
module ExpensesStuff
extend ActiveSupport::Concern
CATEGORIES = {category_a: 0, category_b: 100, category_c: 200 }
module ClassMethods
# class methods here
end
# instance methods here
end
In models, include the module in Expense class and define the category field as enum, picking up the constant:
# models/expense.rb
class Expense < ApplicationRecord
include ExpensesStuff
enum category: MaterialStuff::CATEGORIES
# usual stuff
end
Now in controller. Take care to accept the filter value only if included in MaterialStuff::CATEGORIES.keys.
# expenses_controller.rb
class ExpensesController < ApplicationController
# usual stuff
def index
#expenses = filtered_expenses
end
private
def sanitized_category_filter
category = params[:category].to_sym if params[:category]
return category.to_sym if MaterialStuff::CATEGORIES.keys.include? category
:all
end
def filtered_espenses
Expense.public_send(sanitized_category_filter) # call directly the method
end
end
Finally in view you can add your filtering links.
# views/expenses/index.html.erb
<style>.bg-yellow {background-color:yellow;}</style> <!-- Move to *.css -->
<% def active_filter(category); 'bg-yellow' if category.to_s == params[:category]; end # move to helpers %>
<% (ExpensesStuff::CATEGORIES.keys << :all).each do |category| %>
<%= link_to category, expenses_path(category: category), class: active_filter(category) %> |
<% end %>
You are now able to add new categories just updating the CATEGORIES Hash. You can also use it in the form as options for select.
I've just started using the Pundit gem for authorisation in a Rails 4 app.
Everything's going fine but I can get my head around how pagination will work in the index action.
My controller's index action looks like:
def index
#records = policy_scope(Record)
end
The Scope class within my RecordPolicy then goes:
class Scope < Struct.new(:user, :scope)
def resolve
if user.has_role? :admin
# get all records
else
# get user specific records
end
end
end
This all works fine. I'd like to know how I would handle pagination though. Naturally this involves passing in a page parameter etc and I'm not sure how to do this without subsclassing the Scope class.
The policy_scope(Record) method returns an ActiveRecord::Relation object, then you can chain the pagination method, depending on which gem you use (will_paginate, kaminari).
def index
#records = policy_scope(Record).paginate(params[:page])
end
For googlers. The answer above is technically correct, but with most recent versions of Kaminari (mine is 0.17), the method to chain is page(params[:page])
I have a little database with a movies that I saw. Now when I want to display a detail of a movie, so the profile of wanted movie is on the address example.com/movies/21.
But I would like to have a profile page of every movie on the nicer URL address, for example example.com/lords-of-the-rings.
How can I do it?
In your model, store the url name into a new field, like Movie.permalink
In config/routes.rb :
MyApp::Application.routes.draw do
match "movies/:permalink" => "movies#show"
end
And in your controller :
class MoviesController < ApplicationController
def show
#movie = Movie.find_by_permalink( params[:permalink] )
end
end
For more on routes in rails : http://guides.rubyonrails.org/routing.html
Consider using the slugged gem: https://github.com/Sutto/slugged
Many folks like this approach.
It's rails 3+
Just to help guide the answers, would you allow something like:
http://example.com/movies/lord-of-the-rings
If so, grabbing the params[:id] from that URL is easy.
You can automate the generation of that last :id by changing to_param of the model:
class User < ActiveRecord::Base
def to_param # overridden
name
end
end
Then you could change the show method of the controller to reflect the new :id format.
user = User.find_by_name(params[:id])
I have a category model and I'm routing it using the default scaffolding of resources :categories. I'm wondering if there's a way to change the paths from /category/:id to /category/:name. I added:
match "/categories/:name" => "categories#show"
above the resources line in routes.rb and changed the show action in the controller to do:
#category = Category.find_by_name(params[:name])
it works, but the 'magic paths' such as link_to some_category still use the :id format.
Is there a way to do this? If this is a bad idea (due to some possible way in which rails works internally), is there another way to accomplish this? So that /categories/music, for example, and /categories/3 both work?
Rails has a nifty model instance method called to_param, and it's what the paths use. It defaults to id, but you can override it and produce something like:
class Category < ActiveRecord::Base
def to_param
name
end
end
cat = Category.find_by_name('music')
category_path(cat) # => "/categories/music"
For more info, check the Rails documentation for to_param.
EDIT:
When it comes to category names which aren't ideal for URLs, you have multiple options. One is, as you say, to gsub whitespaces with hyphens and vice versa when finding the record. However, a safer option would be to create another column on the categories table called name_param (or similar). Then, you can use it instead of the name for, well, all path and URL related business. Use the parameterize inflector to create a URL-safe string. Here's how I'd do it:
class Category < ActiveRecord::Base
after_save :create_name_param
def to_param
name_param
end
private
def create_name_param
self.name_param = name.parameterize
end
end
# Hypothetical
cat = Category.create(:name => 'My. Kewl. Category!!!')
category_path(cat) # => "/categories/my-kewl-category"
# Controller
#category = Category.find_by_name_param(param[:id]) # <Category id: 123, name: 'My. Kewl. Category!!!'>
If you don't want to to break existing code that relying on model id you could define your to_param like this:
def to_param
"#{id}-#{name}"
end
so your url will be: http://path/1-some-model and you still can load your model with Model.find(params[:id]) because:
"123-hello-world".to_i
=> 123
Although possibly more than you need, you may also want to look into 'human readable urls' support like friendly_id or one of the others (for instance, if you need unicode support, etc.) that are described here at Ruby Toolbox.
I understand my question is a bit vague but I don't know how else to describe it. I've asked in numerous places and no one seems to understand why I want to do this. But please bear with me, and I'll explain why I want something like this.
I'm using Liquid Templates to allow users to make some dynamic pages on my site. And for those that don't know, Liquid uses a class of theirs called LiquidDrop to expose certain items to the user. Any method in the drop can be called by the Liquid template.
class PageDrop < Liquid::Drop
def initialize(page)
#page = page
end
def name
#page.name
end
def children
PagesDrop.new(#page.children)
end
end
class PagesDrop < Liquid::Drop
def initialize(pages)
#pages = pages
end
def group_by
GroupByDrop.new(#pages)
end
def all
#pages.all
end
def size
#pages.size
end
end
For example, I want to be able to do this:
#page_drop = PageDrop.new(#page)
#page_drop.children # to get an array of children
instead of
#page_drop.children.all
Why do I have a pages drop?
Because I want to be able to cleanly split up the methods I can do to an array of pages, and methods I can do to a single page. This allows me to group pages like so:
#page_drop.children.group_by.some_method_here_that_the_group_drop_contains
To make it simpler for my users, I don't want them to have to think about adding "all" or not to a drop instance to get the "default" object/s that it contains. To reiterate:
#pages_drop = PagesDrop.new(Page.all)
#pages_drop == #pages_drop.pages #I want this to be true, as well as
#pages_drop == #pages_drop.all
Where did I get this idea?
In Rails, a scope (association object) (#person.friends) seems to return the array when you do certain things to it: #person.friends.each, for person in #person.friends
This isn't really possible. When you write #instance you aren't really calling an instance as you describe, you're getting a reference to the object that #instance refers to.
The reason it seems to work with the collections for Rails' associations is that the the association objects are instances of Array that have had some of their methods overridden.
I would consider removing PagesDrop and using the group_by(&:method) syntax if you want a concise way to express groupings. If you do want to keep it then you can get some way towards what you want by implementing each and [] on PagesDrop and having them delegate to #pages. That will let you use #page_drop.children in for loops, for instance.
It looks like you want to implement has_many outside of rails. Will the following work?
class PageDrop < Liquid::Drop
attr_accessor :children
def initialize(page)
#page = page
#children = []
end
def name
#page.name
end
end
This allows you to do the following:
#page_drop = PageDrop.new(#page)
#page_drop.children.size # => 0
#page_drop.children # => []
This also gives you all the standard array functions (group_by, size, each, etc). If you want to add your own methods, create a class that inherits from Array and add your methods there.
class PageArray < Array
def my_method
self.each{|a| puts a}
end
end
class PageDrop < Liquid::Drop
attr_accessor :children
def initialize(page)
#page = page
#children = PageArray.new
end
[...]
end
#page_drop = PageDrop.new(#page)
#page_drop.children.size # => 0
#page_drop.children # => []
#page_drop.children.my_method # Prints all the children
Then any functions you don't define in PageArray fall through to the Ruby Array methods.