Skip to content

Commit

Permalink
Add article search
Browse files Browse the repository at this point in the history
  • Loading branch information
mwean committed Sep 18, 2013
1 parent 79c3bfc commit 4fafdae
Show file tree
Hide file tree
Showing 19 changed files with 438 additions and 32 deletions.
4 changes: 3 additions & 1 deletion app/assets/javascripts/application.js
Original file line number Diff line number Diff line change
@@ -1,2 +1,4 @@
//= require jquery
//= require jquery_ujs
//= require type_watch
//= require select2
//= require article_search
42 changes: 42 additions & 0 deletions app/assets/javascripts/article_search.js.coffee
Original file line number Diff line number Diff line change
@@ -0,0 +1,42 @@
container_selector = '#article-search'
container = $(container_selector)

$(document).on 'submit', "#{container_selector} form", (event) ->
event.preventDefault()
$form = $(this)

$.ajax
dataType: 'json'
data: $form.serialize()
success: replaceTable

$(document).on 'click', "#{container_selector} a", (event) ->
event.preventDefault()
toggleSearchForm()

toggleSearchForm = ->
$form_container = container.find('.form-container')
$form_container.slideToggle()

replaceTable = (data) ->
$('table').replaceWith(data.table)

performSearch = ->
$(this).closest('form').submit()

$ ->
container.find('#q_title_cont_any').typeWatch
callback: performSearch
wait: 20
captureLength: 0

container.find('#q_volume_year_eq').typeWatch
callback: performSearch
wait: 20
captureLength: 4

container.find('select').change (event) ->
performSearch.call(this)

container.find('.select2').select2
width: '250px'
22 changes: 12 additions & 10 deletions app/assets/stylesheets/application.css.scss
Original file line number Diff line number Diff line change
@@ -1,12 +1,14 @@
@charset 'utf-8';
@charset "utf-8";

@import 'normalize';
@import 'bourbon';
@import 'neat';
@import "normalize";
@import "bourbon";
@import "neat";
@import "select2";

@import 'global';
@import 'tables';
@import 'navigation';
@import 'home';
@import 'volumes';
@import 'articles';
@import "global";
@import "tables";
@import "navigation";
@import "home";
@import "volumes";
@import "articles";
@import "article_search";
22 changes: 22 additions & 0 deletions app/assets/stylesheets/article_search.css.scss
Original file line number Diff line number Diff line change
@@ -0,0 +1,22 @@
#article-search {
float: right;
margin-top: 10px;

a {
float: right;
text-decoration: none;
color: #444;

&:hover {
color: $blue;
}
}

select#q_authors_id_in {
width: 200px;
}

input.select2-input {
width: 200px;
}
}
4 changes: 4 additions & 0 deletions app/assets/stylesheets/global.css.scss
Original file line number Diff line number Diff line change
Expand Up @@ -31,3 +31,7 @@ p, th, td {
.nowrap {
white-space: nowrap
}

.hidden {
display: none;
}
2 changes: 1 addition & 1 deletion app/assets/stylesheets/tables.css.scss
Original file line number Diff line number Diff line change
Expand Up @@ -13,7 +13,7 @@ th {

td {
padding: 8px;
line-height: 1;
line-height: 1.4;

&:not(.center) {
padding-left: 20px;
Expand Down
18 changes: 16 additions & 2 deletions app/controllers/articles_controller.rb
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,21 @@ class ArticlesController < ApplicationController
respond_to :html

def index
@articles = Article.includes(:volume).page(params[:page]).decorate
respond_with(@articles)
@search = Article.includes(:volume, :authors).search(params[:q])
@articles = @search.result.sort_by_volume_number_and_first_page.page(params[:page]).decorate

respond_to do |format|
format.html
format.json { render json: { table: render_to_string(partial: 'article_list', layout: false, formats: [:html]) } }
end
end

def authors
Author.all.decorate.map { |author| [author.full_name, author.id] }
end

def keywords
Keyword.pluck(:name)
end
helper_method :authors, :keywords
end
4 changes: 4 additions & 0 deletions app/models/article.rb
Original file line number Diff line number Diff line change
Expand Up @@ -30,6 +30,10 @@ def keyword_names
keywords.pluck(:name).join(',')
end

def self.sort_by_volume_number_and_first_page
reorder('volumes.number DESC, articles.first_page ASC').references(:volume)
end

private

def pages_format
Expand Down
16 changes: 16 additions & 0 deletions app/views/articles/_article_list.html.haml
Original file line number Diff line number Diff line change
@@ -0,0 +1,16 @@
%table.articles
%thead
%tr
%th= Article.human_attribute_name(:volume)
%th= Article.human_attribute_name(:authors)
%th= Article.human_attribute_name(:title)
%th= Article.human_attribute_name(:pages)
%tbody
= content_tag_for(:tr, @articles) do |article|
%td{ class: :center }
= article.volume.roman_numeral
%td= article.author_names
%td= article.title
%td{ class: [:nowrap, :center] }
= article.pages
%tfoot= paginate @articles, remote: true
9 changes: 9 additions & 0 deletions app/views/articles/_article_search.html.haml
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
#article-search
= link_to 'Search', '#'

.form-container.hidden
= search_form_for @search, builder: SimpleForm::FormBuilder do |f|
= f.input :title_cont_any, label: Article.human_attribute_name(:title)
= f.input :authors_id_in, collection: authors, prompt: t('articles.search.authors'), label: Article.human_attribute_name(:authors), input_html: { multiple: true, class: :select2, data: { placeholder: t('articles.search.authors') } }
= f.input :volume_year_eq, label: Article.human_attribute_name(:year)
= f.input :keywords_name_in_any, collection: keywords, label: Article.human_attribute_name(:keywords), input_html: { multiple: true, class: :select2 }
18 changes: 2 additions & 16 deletions app/views/articles/index.html.haml
Original file line number Diff line number Diff line change
@@ -1,16 +1,2 @@
%table.articles
%thead
%tr
%th= Article.human_attribute_name(:volume)
%th= Article.human_attribute_name(:authors)
%th= Article.human_attribute_name(:title)
%th= Article.human_attribute_name(:pages)
%tbody
= content_tag_for(:tr, @articles) do |article|
%td{ class: :center }
= article.volume.roman_numeral
%td= article.author_names
%td= article.title
%td{ class: [:nowrap, :center] }
= article.pages
%tfoot= paginate @articles, remote: true
= render 'article_search'
= render 'article_list'
4 changes: 2 additions & 2 deletions config/initializers/draper.rb
Original file line number Diff line number Diff line change
@@ -1,2 +1,2 @@
Draper::CollectionDecorator.delegate :reorder, :page, :current_page, :total_pages, :limit_value, :total_count, :num_pages, :model_name
Draper::Decorator.delegate :id, :to_key
Draper::CollectionDecorator.delegate :reorder, :page, :current_page, :total_pages, :limit_value, :total_count, :num_pages, :model_name, :pluck
Draper::Decorator.delegate :id, :to_key, :attributes=, :save, :errors, :persisted?
7 changes: 7 additions & 0 deletions config/initializers/ransack.rb
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
cont_any_formatter = ->(query) do
query.split(/\s+/).map { |part| "%#{Ransack::Constants.escape_wildcards(part)}%" }
end

Ransack.configure do |config|
config.add_predicate 'cont_any', arel_predicate: 'matches_any', formatter: cont_any_formatter, compounds: true, type: :string
end
93 changes: 93 additions & 0 deletions config/initializers/simple_form.rb
Original file line number Diff line number Diff line change
@@ -0,0 +1,93 @@
# Use this setup block to configure all options available in SimpleForm.
SimpleForm.setup do |config|
# Components used by the form builder to generate a complete input. You can remove
# any of them, change the order, or even add your own components to the stack.
# config.components = [ :placeholder, :label_input, :hint, :error ]

# Default tag used on hints.
# config.hint_tag = :span

# CSS class to add to all hint tags.
# config.hint_class = :hint

# CSS class used on errors.
# config.error_class = :error

# Default tag used on errors.
# config.error_tag = :span

# Method used to tidy up errors.
# config.error_method = :first

# Default tag used for error notification helper.
# config.error_notification_tag = :p

# CSS class to add for error notification helper.
# config.error_notification_class = :error_notification

# ID to add for error notification helper.
# config.error_notification_id = nil

# You can wrap all inputs in a pre-defined tag.
# config.wrapper_tag = :div

# CSS class to add to all wrapper tags.
# config.wrapper_class = :input

# CSS class to add to the wrapper if the field has errors.
# config.wrapper_error_class = :field_with_errors

# You can wrap a collection of radio/check boxes in a pre-defined tag, defaulting to none.
# config.collection_wrapper_tag = nil

# You can wrap each item in a collection of radio/check boxes with a tag, defaulting to span.
# config.item_wrapper_tag = :span

# Series of attempts to detect a default label method for collection.
# config.collection_label_methods = [ :to_label, :name, :title, :to_s ]

# Series of attempts to detect a default value method for collection.
# config.collection_value_methods = [ :id, :to_s ]

# How the label text should be generated altogether with the required text.
# config.label_text = lambda { |label, required| "#{required} #{label}" }

# You can define the class to use on all labels. Default is nil.
# config.label_class = nil

# You can define the class to use on all forms. Default is simple_form.
# config.form_class = :simple_form

# Whether attributes are required by default (or not). Default is true.
config.required_by_default = false

# Tell browsers whether to use default HTML5 validations (novalidate option).
# Default is enabled.
# config.browser_validations = true

# Determines whether HTML5 types (:email, :url, :search, :tel) and attributes
# (e.g. required) are used or not. True by default.
# Having this on in non-HTML5 compliant sites can cause odd behavior in
# HTML5-aware browsers such as Chrome.
# config.html5 = true

# Custom mappings for input types. This should be a hash containing a regexp
# to match as key, and the input type that will be used when the field name
# matches the regexp as value.
# config.input_mappings = { /count/ => :integer }

# Collection of methods to detect if a file type was given.
# config.file_methods = [ :mounted_as, :file?, :public_filename ]

# Default priority for time_zone inputs.
# config.time_zone_priority = nil

# Default priority for country inputs.
# config.country_priority = nil

# Default size for text inputs.
# config.default_input_size = 50

# When false, do not use translations for labels, hints or placeholders.
# config.translate = true
end
3 changes: 3 additions & 0 deletions config/locales/en.yml
Original file line number Diff line number Diff line change
Expand Up @@ -13,3 +13,6 @@ en:
volume: Volume
year: Year
isbn: ISBN
articles:
search:
authors: Select one or more authors
24 changes: 24 additions & 0 deletions config/locales/simple_form.en.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,24 @@
en:
simple_form:
"yes": 'Yes'
"no": 'No'
required:
text: 'required'
mark: '*'
# You can uncomment the line below if you need to overwrite the whole required html.
# When using html, text and mark won't be used.
# html: '<abbr title="required">*</abbr>'
error_notification:
default_message: "Some errors were found, please take a look:"
# Labels and hints examples
# labels:
# password: 'Password'
# user:
# new:
# email: 'E-mail para efetuar o sign in.'
# edit:
# email: 'E-mail.'
# hints:
# username: 'User name to sign in.'
# password: 'No special characters, please.'

2 changes: 2 additions & 0 deletions spec/factories/authors.rb
Original file line number Diff line number Diff line change
@@ -1,4 +1,6 @@
FactoryGirl.define do
factory :author do
first_name 'Jim'
last_name 'Halpert'
end
end
Loading

0 comments on commit 4fafdae

Please sign in to comment.