$LOAD_PATH.unshift File.expand_path('../../../lib/', __FILE__)
require 'sinatra/base'
require 'multi_json'
require 'oj'
require 'hashie/mash'
require 'elasticsearch'
require 'elasticsearch/model'
require 'elasticsearch/persistence'
class Note
attr_reader :attributes
def initialize(attributes={})
@attributes = Hashie::Mash.new(attributes)
__add_date
__extract_tags
__truncate_text
self
end
def method_missing(method_name, *arguments, &block)
attributes.respond_to?(method_name) ? attributes.__send__(method_name, *arguments, &block) : super
end
def respond_to?(method_name, include_private=false)
attributes.respond_to?(method_name) || super
end
def tags; attributes.tags || []; end
def to_hash
@attributes.to_hash
end
def __extract_tags
tags = attributes['text'].scan(/(\[\w+\])/).flatten if attributes['text']
unless tags.nil? || tags.empty?
attributes.update 'tags' => tags.map { |t| t.tr('[]', '') }
attributes['text'].gsub!(/(\[\w+\])/, '').strip!
end
end
def __add_date
attributes['created_at'] ||= Time.now.utc.iso8601
end
def __truncate_text
attributes['text'] = attributes['text'][0...80] + ' (...)' if attributes['text'] && attributes['text'].size > 80
end
end
class NoteRepository
include Elasticsearch::Persistence::Repository
client Elasticsearch::Client.new url: ENV['ELASTICSEARCH_URL'], log: true
index :notes
type :note
mapping do
indexes :text, analyzer: 'snowball'
indexes :tags, type: 'keyword'
indexes :created_at, type: 'date'
end
create_index!
def deserialize(document)
Note.new document['_source'].merge('id' => document['_id'])
end
end unless defined?(NoteRepository)
class Application < Sinatra::Base
enable :logging
enable :inline_templates
enable :method_override
configure :development do
enable :dump_errors
disable :show_exceptions
require 'sinatra/reloader'
register Sinatra::Reloader
end
set :repository, NoteRepository.new
set :per_page, 25
get '/' do
@page = [ params[:p].to_i, 1 ].max
@notes = settings.repository.search \
query: ->(q, t) do
query = if q && !q.empty?
{ match: { text: q } }
else
{ match_all: {} }
end
filter = if t && !t.empty?
{ term: { tags: t } }
end
if filter
{ bool: { must: [ query ], filter: filter } }
else
query
end
end.(params[:q], params[:t]),
sort: [{created_at: {order: 'desc'}}],
size: settings.per_page,
from: settings.per_page * (@page-1),
aggregations: { tags: { terms: { field: 'tags' } } },
highlight: { fields: { text: { fragment_size: 0, pre_tags: [''],post_tags: [''] } } }
erb :index
end
post '/' do
unless params[:text].empty?
@note = Note.new params
settings.repository.save(@note, refresh: true)
end
redirect back
end
delete '/:id' do |id|
settings.repository.delete(id, refresh: true)
redirect back
end
end
Application.run! if $0 == __FILE__
__END__
@@ layout
Notes
<%= yield %>
@@ index
All notes <%= @notes.size %>
<% @notes.response.aggregations.tags.buckets.each do |term| %>
- <%= term['key'] %> <%= term['doc_count'] %>
<% end %>
Add a note
<% if @notes.empty? %>
No notes found.
<% end %>
<% @notes.each_with_hit do |note, hit| %>
<%= hit.highlight && hit.highlight.size > 0 ? hit.highlight.text.first : note.text %>
<% note.tags.each do |tag| %> <%= tag %><% end %>
<%= Time.parse(note.created_at).strftime('%d/%m/%Y %H:%M') %>
<% end %>
<% if @notes.size > 0 && @page.next <= @notes.total / settings.per_page %>
<% end %>