# $ rails new searchapp --skip --skip-bundle --template https://raw.github.com/elasticsearch/elasticsearch-rails/master/elasticsearch-rails/lib/rails/templates/02-pretty.rb unless File.read('README.md').include? '## [1] Basic' say_status "ERROR", "You have to run the 01-basic.rb template first.", :red exit(1) end puts say_status "README", "Updating Readme...\n", :yellow puts '-'*80, ''; sleep 0.25 append_to_file 'README.md', <<-README ## [2] Pretty The `pretty` template builds on the `basic` version and brings couple of improvements: * Using the [Bootstrap](http://getbootstrap.com) framework to enhance the visual style of the application * Using an `Article.search` class method to customize the default search definition * Highlighting matching phrases in search results * Paginating results with Kaminari README git add: "README.md" git commit: "-m '[02] Updated the application README'" # ----- Update application.rb --------------------------------------------------------------------- puts say_status "Rubygems", "Adding Rails logger integration...\n", :yellow puts '-'*80, ''; sleep 0.25 insert_into_file 'config/application.rb', "\n\nrequire 'elasticsearch/rails/instrumentation'", after: /Bundler\.require.+$/ git add: "config/application.rb" git commit: "-m 'Added the Rails logger integration to application.rb'" # ----- Add gems into Gemfile --------------------------------------------------------------------- puts say_status "Rubygems", "Adding Rubygems into Gemfile...\n", :yellow puts '-'*80, ''; sleep 0.25 # NOTE: Kaminari has to be loaded before Elasticsearch::Model so the callbacks are executed # insert_into_file 'Gemfile', <<-CODE, before: /gem ["']elasticsearch["'].+$/ # NOTE: Kaminari has to be loaded before Elasticsearch::Model so the callbacks are executed gem 'kaminari' CODE run "bundle install" git add: "Gemfile*" git commit: "-m 'Added the Kaminari gem'" # ----- Add `Article.search` class method --------------------------------------------------------- puts say_status "Model", "Adding a `Article.search` class method...\n", :yellow puts '-'*80, ''; sleep 0.5 insert_into_file 'app/models/article.rb', <<-CODE, after: 'include Elasticsearch::Model::Callbacks' def self.search(query) __elasticsearch__.search( { query: { multi_match: { query: query, fields: ['title^10', 'content'] } }, highlight: { pre_tags: [''], post_tags: [''], fields: { title: { number_of_fragments: 0 }, content: { fragment_size: 25 } } } } ) end CODE insert_into_file "#{Rails::VERSION::STRING > '4' ? 'test/models' : 'test/unit' }/article_test.rb", <<-CODE, after: /class ArticleTest < ActiveSupport::TestCase$/ teardown do Article.__elasticsearch__.unstub(:search) end CODE gsub_file "#{Rails::VERSION::STRING > '4' ? 'test/models' : 'test/unit' }/article_test.rb", %r{# test "the truth" do.*?# end}m, <<-CODE test "has a search method delegating to __elasticsearch__" do Article.__elasticsearch__.expects(:search).with do |definition| assert_equal 'foo', definition[:query][:multi_match][:query] true end Article.search 'foo' end CODE insert_into_file "test/test_helper.rb", "require 'mocha/minitest'\n\n", before: "class ActiveSupport::TestCase\n" git add: "app/models/article.rb" git add: "test/**/article_test.rb" git add: "test/test_helper.rb" git commit: "-m 'Added an `Article.search` method'" # ----- Add loading Bootstrap assets -------------------------------------------------------------- puts say_status "Bootstrap", "Adding Bootstrap asset links into the 'application' layout...\n", :yellow puts '-'*80, ''; sleep 0.5 gsub_file 'app/views/layouts/application.html.erb', %r{<%= yield %>}, <<-CODE unless File.read('app/views/layouts/application.html.erb').include?('class="container"')
<%= yield %>
CODE insert_into_file 'app/views/layouts/application.html.erb', <<-CODE, before: '' CODE git commit: "-a -m 'Added loading Bootstrap assets in the application layout'" # ----- Customize the search form ----------------------------------------------------------------- puts say_status "Bootstrap", "Customizing the index page...\n", :yellow puts '-'*80, ''; sleep 0.5 gsub_file 'app/views/articles/index.html.erb', %r{<%= label_tag .* :search %>}m do |match| <<-CODE
<%= text_field_tag :q, params[:q], class: 'form-control', placeholder: 'Search...' %>
CODE end # ----- Customize the header ----------------------------------------------------------------- gsub_file 'app/views/articles/index.html.erb', %r{

.*Articles

} do |match| "

<%= controller.action_name == 'search' ? 'Search results' : 'Articles' %>

" end # ----- Customize the results listing ------------------------------------------------------------- gsub_file 'app/views/articles/index.html.erb', %r{} do |match| '
' end gsub_file 'app/views/articles/index.html.erb', %r{$} do |match| "" end gsub_file 'app/views/articles/index.html.erb', %r{$} do |match| "" end git commit: "-a -m 'Added highlighting for matches'" # ----- Paginate the results ---------------------------------------------------------------------- gsub_file 'app/controllers/articles_controller.rb', %r{@articles = Article.all} do |match| "@articles = Article.page(params[:page])" end gsub_file 'app/controllers/articles_controller.rb', %r{@articles = Article.search\(params\[\:q\]\).records} do |match| "@articles = Article.search(params[:q]).page(params[:page]).records" end insert_into_file 'app/views/articles/index.html.erb', after: '
<%= link_to [^%]+} do |match| match.gsub!('', '') match.include?("btn") ? match : (match + ", class: 'btn btn-outline-primary btn-sm'") end gsub_file 'app/views/articles/index.html.erb', %r{
\s*(<\%= link_to 'New Article'.*)}m do |content| replace = content.match(%r{
\s*(<\%= link_to 'New Article'.*)}m)[1] <<-END.gsub(/^ /, '')

#{replace}

END end gsub_file 'app/views/articles/index.html.erb', %r{<%= link_to 'New Article',\s*new_article_path} do |match| return match if match.include?('btn') match + ", class: 'btn btn-primary btn-xs', style: 'color: #fff'" end gsub_file 'app/views/articles/index.html.erb', %r{<%= link_to 'All Articles',\s*articles_path} do |match| return match if match.include?('btn') "\n " + match + ", class: 'btn btn-primary btn-xs', style: 'color: #fff'" end # ----- Customize the form ----------------------------------------------------------------- gsub_file 'app/views/articles/_form.html.erb', %r{
} do |match| %Q|
| end git add: "app/views" git commit: "-m 'Refactored the articles listing to use Bootstrap components'" # ----- Use highlighted excerpts in the listing --------------------------------------------------- gsub_file 'app/views/articles/index.html.erb', %r{<% @articles.each do \|article\| %>$} do |match| "<% @articles.__send__ controller.action_name == 'search' ? :each_with_hit : :each do |article, hit| %>" end gsub_file 'app/views/articles/index.html.erb', %r{
<%= article.title %><%= hit.try(:highlight).try(:title) ? hit.highlight.title.join.html_safe : article.title %><%= article.content %><%= hit.try(:highlight).try(:content) ? hit.highlight.content.join('…').html_safe : article.content %>
' do <<-CODE.gsub(/^ /, '')
<%= paginate @articles %>
CODE end generate "kaminari:views", "bootstrap3", "--force" gsub_file 'app/views/kaminari/_paginator.html.erb', %r{