# $ 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"')
<%= 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 %> | $} do |match|
"<%= hit.try(:highlight).try(:title) ? hit.highlight.title.join.html_safe : article.title %> | "
end
gsub_file 'app/views/articles/index.html.erb', %r{<%= article.content %> | $} do |match|
"<%= hit.try(:highlight).try(:content) ? hit.highlight.content.join('…').html_safe : article.content %> | "
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: ' |