debian-mirror-gitlab/elasticsearch-rails/lib/rails/templates/02-pretty.rb
2020-03-09 13:42:32 +05:30

331 lines
10 KiB
Ruby

# $ 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: ['<em class="label label-highlight">'],
post_tags: ['</em>'],
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"')
<div class="container">
<%= yield %>
</div>
CODE
insert_into_file 'app/views/layouts/application.html.erb', <<-CODE, before: '</head>'
<link rel="stylesheet" href="https://stackpath.bootstrapcdn.com/bootstrap/3.3.7/css/bootstrap.min.css">
<script src="https://code.jquery.com/jquery-3.3.1.slim.min.js"></script>
<script src="https://stackpath.bootstrapcdn.com/bootstrap/3.3.7/js/bootstrap.min.js"></script>
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
<div class="input-group">
<%= text_field_tag :q, params[:q], class: 'form-control', placeholder: 'Search...' %>
</div>
CODE
end
# ----- Customize the header -----------------------------------------------------------------
gsub_file 'app/views/articles/index.html.erb', %r{<h1>.*Articles</h1>} do |match|
"<h1><%= controller.action_name == 'search' ? 'Search results' : 'Articles' %></h1>"
end
# ----- Customize the results listing -------------------------------------------------------------
gsub_file 'app/views/articles/index.html.erb', %r{<table>} do |match|
'<table class="table table-hover">'
end
gsub_file 'app/views/articles/index.html.erb', %r{<td><%= link_to [^%]+} do |match|
match.gsub!('<td>', '<td style="width: 50px">')
match.include?("btn") ? match : (match + ", class: 'btn btn-outline-primary btn-sm'")
end
gsub_file 'app/views/articles/index.html.erb', %r{<br>\s*(<\%= link_to 'New Article'.*)}m do |content|
replace = content.match(%r{<br>\s*(<\%= link_to 'New Article'.*)}m)[1]
<<-END.gsub(/^ /, '')
<hr>
<p style="text-align: center; margin-bottom: 21px">
#{replace}
</p>
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{<div class="field">} do |match|
%Q|<div class="form-group">|
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{<td><%= article.title %></td>$} do |match|
"<td><%= hit.try(:highlight).try(:title) ? hit.highlight.title.join.html_safe : article.title %></td>"
end
gsub_file 'app/views/articles/index.html.erb', %r{<td><%= article.content %></td>$} do |match|
"<td><%= hit.try(:highlight).try(:content) ? hit.highlight.content.join('&hellip;').html_safe : article.content %></td>"
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: '</table>' do
<<-CODE.gsub(/^ /, '')
<div class="text-center">
<%= paginate @articles %>
</div>
CODE
end
generate "kaminari:views", "bootstrap3", "--force"
gsub_file 'app/views/kaminari/_paginator.html.erb', %r{<nav>}, '<nav class="pagination">'
git add: "."
git commit: "-m 'Added pagination to articles listing'"
# ----- Custom CSS --------------------------------------------------------------------------------
puts
say_status "CSS", "Adding custom styles...\n", :yellow
puts '-'*80, ''; sleep 0.5
append_to_file 'app/assets/stylesheets/application.css' do
unless File.read('app/assets/stylesheets/application.css').include?('.label-highlight')
<<-CODE
body * {
font-size: 100% !important;
}
.table {
border-bottom: 1px solid #dee2e6;
}
.table td {
vertical-align: middle !important;
}
.label-highlight {
font-size: 100% !important;
font-weight: inherit !important;
font-style: inherit !important;
color: #333 !important;
background: #fff401 !important;
padding: 0.25em 0.5em
border-radius: 5px;
}
nav.pagination {
text-align: center;
display: inline-block;
}
ul.pagination {
margin-bottom: 0;
}
CODE
else
''
end
end
git commit: "-a -m 'Added custom style definitions into application.css'"
# ----- Generate 1,000 articles -------------------------------------------------------------------
puts
say_status "Database", "Creating 1,000 articles...", :yellow
puts '-'*80, '';
run "rails runner 'Article.__elasticsearch__.create_index! force: true'"
rake "db:seed COUNT=1_000"
# ----- Print Git log -----------------------------------------------------------------------------
puts
say_status "Git", "Details about the application:", :yellow
puts '-'*80, ''
git tag: "pretty"
git log: "--reverse --oneline pretty...basic"
# ----- Start the application ---------------------------------------------------------------------
unless ENV['RAILS_NO_SERVER_START']
require 'net/http'
if (begin; Net::HTTP.get(URI('http://localhost:3000')); rescue Errno::ECONNREFUSED; false; rescue Exception; true; end)
puts "\n"
say_status "ERROR", "Some other application is running on port 3000!\n", :red
puts '-'*80
port = ask("Please provide free port:", :bold)
else
port = '3000'
end
puts "", "="*80
say_status "DONE", "\e[1mStarting the application. Open http://localhost:#{port}\e[0m", :yellow
puts "="*80, ""
run "rails server --port=#{port}"
end