Commit 84590916
Changed files (4)
app
models
spec
controllers
app/models/concerns/queryable.rb
@@ -1,11 +1,70 @@
module Queryable
extend ActiveSupport::Concern
+ included { send :include, ClassMethods }
+
module ClassMethods
- def all_matching(search_filters)
- search_filters.inject(self) do |memo, next_filter|
- next_filter.call(memo)
+ def filter_by(filters)
+ return all if filters.empty?
+ filters.reduce(self) do |current_scope, filter|
+ filter.call(current_scope)
+ end
+ end
+
+ def query_builder_for(params)
+ Queryable::Builder.new(params).tap do |builder|
+ yield builder if block_given?
+ end
+ end
+ end
+
+ class Builder
+ include Enumerable
+ attr_reader :params, :filters
+ delegate :empty?, to: :filters
+
+ def initialize(params)
+ @params = params
+ @filters = []
+ end
+
+ def always(&filter)
+ add do |relation|
+ filter.call(relation)
+ end
+ end
+
+ def if_present(key, &filter)
+ return if params[key].nil? || params[key] == ''
+ add do |relation|
+ filter.call(relation, normalize(params[key]))
end
end
+
+ def each(&block)
+ @filters.each(&block)
+ end
+
+ private
+
+ def add(&filter)
+ @filters.push(filter)
+ end
+
+ def boolean?(value)
+ true?(value) || false?(value)
+ end
+
+ def true?(value)
+ "true" == value.to_s.downcase
+ end
+
+ def false?(value)
+ "false" == value.to_s.downcase
+ end
+
+ def normalize(value)
+ boolean?(value) ? true?(value) : value
+ end
end
end
app/models/creation/repository.rb
@@ -3,6 +3,9 @@ class Creation
scope :tagged, ->(tag) { tagged_with([tag]).where('photos_count > 0') }
scope :published, ->{ joins(:photos).where(photos: { image_processing: nil }) }
scope :search, ->(query) { where(["UPPER(creations.name) LIKE :query OR UPPER(creations.story) LIKE :query", { query: "%#{query.upcase}%" }]) }
+ scope :sorted_by, ->(direction) do
+ order(created_at: "oldest" == direction ? :asc : :desc)
+ end
class Repository < SimpleDelegator
def initialize(connection = Creation)
@@ -19,7 +22,7 @@ class Creation
end
def search_with(params)
- all_matching(search_filters_for(params))
+ filter_by(search_filters_for(params))
end
private
@@ -27,17 +30,23 @@ class Creation
attr_reader :connection
def search_filters_for(params)
- [
- ->(cakes) { cakes.published },
- ->(cakes) { params[:category].blank? ? cakes.all : cakes.where(category: Category.by_slug(params[:category])) },
- ->(cakes) { params[:q].blank? ? cakes.all : cakes.search(params[:q]) },
- ->(cakes) { cakes.order(created_at: sort(params)) },
- ->(cakes) { params[:tags].blank? ? cakes.all : cakes.tagged(params[:tags].downcase.parameterize) },
- ]
- end
-
- def sort(params)
- params[:sort] == "oldest" ? :asc : :desc
+ query_builder_for(params) do |builder|
+ builder.always do |relation|
+ relation.published
+ end
+ builder.if_present(:category) do |relation, category|
+ relation.where(category: Category.by_slug(category))
+ end
+ builder.if_present(:q) do |relation, query|
+ relation.search(query)
+ end
+ builder.if_present(:tags) do |relation, tags|
+ relation.tagged(tags.downcase.parameterize)
+ end
+ builder.if_present(:sort) do |relation, sort_order|
+ relation.sorted_by(sort_order)
+ end
+ end
end
end
end
app/models/creation.rb
@@ -9,8 +9,6 @@ class Creation < ActiveRecord::Base
acts_as_taggable_on :tags
alias_method :author, :user
- default_scope -> { order(created_at: :desc) }
-
def to_param
"#{id}-#{name.parameterize}"
end
spec/controllers/cakes_controller_spec.rb
@@ -3,49 +3,63 @@ require 'rails_helper'
describe CakesController do
let(:user) { create(:user) }
- before(:each) do
- photo = 'spec/fixtures/images/example.png'
- cake.photos.create(image: photo)
+ before :each do
+ #photo = 'spec/fixtures/images/example.png'
+ #cake.photos.create(image: photo)
end
describe "#index" do
let!(:cakes) { create(:category, slug: "cakes") }
let!(:cookies) { create(:category, slug: "cookies") }
- let!(:cake) { create(:published_cake, name: "cake", category: cakes) }
- let!(:cookie) do
- create(:published_cake, name: "cookie", category: cookies)
- end
-
- let!(:unpublished_cake) do
- create(:cake, name: "unpublished", category: cakes)
- end
it "returns all published cakes" do
+ cake = create(:published_cake, category: cakes)
+ cookie = create(:published_cake, category: cookies)
+ unpublished_cake = create(:cake, category: cakes)
get :index
expect(assigns(:cakes)).to match_array([cake, cookie])
end
it "returns all cakes in the category" do
+ cookie = create(:published_cake, name: "cookie", category: cookies)
get :index, category: cookie.category.slug
expect(assigns(:cakes)).to match_array([cookie])
end
it "returns all cakes matching the search query" do
+ cake = create(:published_cake, name: "cake", category: cakes)
get :index, q: cake.name[0..2]
expect(assigns(:cakes)).to match_array([cake])
end
it "returns all cakes tagged with the tag" do
+ cake = create(:published_cake, name: "cake", category: cakes)
cake.tag_list = "cakes"
cake.save!
get :index, tags: "cakes"
expect(assigns(:cakes)).to match_array([cake])
end
+
+ describe "sorting" do
+ let!(:old_cake) { create(:published_cake, name: 'old', created_at: 2.days.ago) }
+ let!(:new_cake) { create(:published_cake, name: 'new', created_at: 1.day.ago) }
+
+ it 'returns oldest cakes first' do
+ get :index, sort: "oldest"
+ expect(assigns(:cakes).to_a).to eql([ old_cake, new_cake ])
+ end
+
+ it 'returns newest cakes first' do
+ get :index, sort: "newest"
+ expect(assigns(:cakes).to_a).to eql([ new_cake, old_cake ])
+ end
+ end
end
describe "#show" do
let(:cake) { create(:cake, user: user) }
+ let(:photo) { create(:photo, imageable: cake) }
it "loads the cake" do
get :show, id: cake.id
@@ -53,7 +67,6 @@ describe CakesController do
end
it 'loads the selected image' do
- photo = cake.photos.first
get :show, id: cake.id, photo_id: photo.id
expect(assigns(:primary_image)).to eql(photo)
end