elasticsearch-persistence-queryable 0.1.8

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (99) hide show
  1. checksums.yaml +7 -0
  2. data/.gitignore +17 -0
  3. data/CHANGELOG.md +16 -0
  4. data/Gemfile +4 -0
  5. data/LICENSE.txt +13 -0
  6. data/README.md +678 -0
  7. data/Rakefile +57 -0
  8. data/elasticsearch-persistence.gemspec +57 -0
  9. data/examples/music/album.rb +34 -0
  10. data/examples/music/artist.rb +50 -0
  11. data/examples/music/artists/_form.html.erb +8 -0
  12. data/examples/music/artists/artists_controller.rb +67 -0
  13. data/examples/music/artists/artists_controller_test.rb +53 -0
  14. data/examples/music/artists/index.html.erb +57 -0
  15. data/examples/music/artists/show.html.erb +51 -0
  16. data/examples/music/assets/application.css +226 -0
  17. data/examples/music/assets/autocomplete.css +48 -0
  18. data/examples/music/assets/blank_cover.png +0 -0
  19. data/examples/music/assets/form.css +113 -0
  20. data/examples/music/index_manager.rb +60 -0
  21. data/examples/music/search/index.html.erb +93 -0
  22. data/examples/music/search/search_controller.rb +41 -0
  23. data/examples/music/search/search_controller_test.rb +9 -0
  24. data/examples/music/search/search_helper.rb +15 -0
  25. data/examples/music/suggester.rb +45 -0
  26. data/examples/music/template.rb +392 -0
  27. data/examples/music/vendor/assets/jquery-ui-1.10.4.custom.min.css +7 -0
  28. data/examples/music/vendor/assets/jquery-ui-1.10.4.custom.min.js +6 -0
  29. data/examples/notes/.gitignore +7 -0
  30. data/examples/notes/Gemfile +28 -0
  31. data/examples/notes/README.markdown +36 -0
  32. data/examples/notes/application.rb +238 -0
  33. data/examples/notes/config.ru +7 -0
  34. data/examples/notes/test.rb +118 -0
  35. data/lib/elasticsearch/per_thread_registry.rb +53 -0
  36. data/lib/elasticsearch/persistence/client.rb +51 -0
  37. data/lib/elasticsearch/persistence/inheritence.rb +9 -0
  38. data/lib/elasticsearch/persistence/model/base.rb +95 -0
  39. data/lib/elasticsearch/persistence/model/callbacks.rb +37 -0
  40. data/lib/elasticsearch/persistence/model/errors.rb +9 -0
  41. data/lib/elasticsearch/persistence/model/find.rb +155 -0
  42. data/lib/elasticsearch/persistence/model/gateway_delegation.rb +23 -0
  43. data/lib/elasticsearch/persistence/model/hash_wrapper.rb +17 -0
  44. data/lib/elasticsearch/persistence/model/rails.rb +39 -0
  45. data/lib/elasticsearch/persistence/model/store.rb +271 -0
  46. data/lib/elasticsearch/persistence/model.rb +148 -0
  47. data/lib/elasticsearch/persistence/null_relation.rb +56 -0
  48. data/lib/elasticsearch/persistence/query_cache.rb +68 -0
  49. data/lib/elasticsearch/persistence/querying.rb +21 -0
  50. data/lib/elasticsearch/persistence/relation/delegation.rb +130 -0
  51. data/lib/elasticsearch/persistence/relation/finder_methods.rb +39 -0
  52. data/lib/elasticsearch/persistence/relation/merger.rb +179 -0
  53. data/lib/elasticsearch/persistence/relation/query_builder.rb +279 -0
  54. data/lib/elasticsearch/persistence/relation/query_methods.rb +362 -0
  55. data/lib/elasticsearch/persistence/relation/search_option_methods.rb +44 -0
  56. data/lib/elasticsearch/persistence/relation/spawn_methods.rb +61 -0
  57. data/lib/elasticsearch/persistence/relation.rb +110 -0
  58. data/lib/elasticsearch/persistence/repository/class.rb +71 -0
  59. data/lib/elasticsearch/persistence/repository/find.rb +73 -0
  60. data/lib/elasticsearch/persistence/repository/naming.rb +115 -0
  61. data/lib/elasticsearch/persistence/repository/response/results.rb +105 -0
  62. data/lib/elasticsearch/persistence/repository/search.rb +156 -0
  63. data/lib/elasticsearch/persistence/repository/serialize.rb +31 -0
  64. data/lib/elasticsearch/persistence/repository/store.rb +94 -0
  65. data/lib/elasticsearch/persistence/repository.rb +77 -0
  66. data/lib/elasticsearch/persistence/scoping/default.rb +137 -0
  67. data/lib/elasticsearch/persistence/scoping/named.rb +70 -0
  68. data/lib/elasticsearch/persistence/scoping.rb +52 -0
  69. data/lib/elasticsearch/persistence/version.rb +5 -0
  70. data/lib/elasticsearch/persistence.rb +157 -0
  71. data/lib/elasticsearch/rails_compatibility.rb +17 -0
  72. data/lib/rails/generators/elasticsearch/model/model_generator.rb +21 -0
  73. data/lib/rails/generators/elasticsearch/model/templates/model.rb.tt +9 -0
  74. data/lib/rails/generators/elasticsearch_generator.rb +2 -0
  75. data/lib/rails/instrumentation/railtie.rb +31 -0
  76. data/lib/rails/instrumentation.rb +10 -0
  77. data/test/integration/model/model_basic_test.rb +157 -0
  78. data/test/integration/repository/custom_class_test.rb +85 -0
  79. data/test/integration/repository/customized_class_test.rb +82 -0
  80. data/test/integration/repository/default_class_test.rb +114 -0
  81. data/test/integration/repository/virtus_model_test.rb +114 -0
  82. data/test/test_helper.rb +53 -0
  83. data/test/unit/model_base_test.rb +48 -0
  84. data/test/unit/model_find_test.rb +148 -0
  85. data/test/unit/model_gateway_test.rb +99 -0
  86. data/test/unit/model_rails_test.rb +88 -0
  87. data/test/unit/model_store_test.rb +514 -0
  88. data/test/unit/persistence_test.rb +32 -0
  89. data/test/unit/repository_class_test.rb +51 -0
  90. data/test/unit/repository_client_test.rb +32 -0
  91. data/test/unit/repository_find_test.rb +388 -0
  92. data/test/unit/repository_indexing_test.rb +37 -0
  93. data/test/unit/repository_module_test.rb +146 -0
  94. data/test/unit/repository_naming_test.rb +146 -0
  95. data/test/unit/repository_response_results_test.rb +98 -0
  96. data/test/unit/repository_search_test.rb +117 -0
  97. data/test/unit/repository_serialize_test.rb +57 -0
  98. data/test/unit/repository_store_test.rb +303 -0
  99. metadata +487 -0
data/Rakefile ADDED
@@ -0,0 +1,57 @@
1
+ require "bundler/gem_tasks"
2
+
3
+ desc "Run unit tests"
4
+ task :default => 'test:unit'
5
+ task :test => 'test:unit'
6
+
7
+ # ----- Test tasks ------------------------------------------------------------
8
+
9
+ require 'rake/testtask'
10
+ namespace :test do
11
+ task :ci_reporter do
12
+ ENV['CI_REPORTS'] ||= 'tmp/reports'
13
+ require 'ci/reporter/rake/minitest'
14
+ Rake::Task['ci:setup:minitest'].invoke
15
+ end
16
+
17
+ Rake::TestTask.new(:unit) do |test|
18
+ Rake::Task['test:ci_reporter'].invoke if ENV['CI']
19
+ test.libs << 'lib' << 'test'
20
+ test.test_files = FileList["test/unit/**/*_test.rb"]
21
+ # test.verbose = true
22
+ # test.warning = true
23
+ end
24
+
25
+ Rake::TestTask.new(:integration) do |test|
26
+ Rake::Task['test:ci_reporter'].invoke if ENV['CI']
27
+ test.libs << 'lib' << 'test'
28
+ test.test_files = FileList["test/integration/**/*_test.rb"]
29
+ end
30
+
31
+ Rake::TestTask.new(:all) do |test|
32
+ Rake::Task['test:ci_reporter'].invoke if ENV['CI']
33
+ test.libs << 'lib' << 'test'
34
+ test.test_files = FileList["test/unit/**/*_test.rb", "test/integration/**/*_test.rb"]
35
+ end
36
+ end
37
+
38
+ # ----- Documentation tasks ---------------------------------------------------
39
+
40
+ require 'yard'
41
+ YARD::Rake::YardocTask.new(:doc) do |t|
42
+ t.options = %w| --embed-mixins --markup=markdown |
43
+ end
44
+
45
+ # ----- Code analysis tasks ---------------------------------------------------
46
+
47
+ if defined?(RUBY_VERSION) && RUBY_VERSION > '1.9'
48
+ begin
49
+ require 'cane/rake_task'
50
+ Cane::RakeTask.new(:quality) do |cane|
51
+ cane.abc_max = 15
52
+ cane.style_measure = 120
53
+ end
54
+ rescue LoadError
55
+ warn "cane not available, quality task not provided."
56
+ end
57
+ end
@@ -0,0 +1,57 @@
1
+ # coding: utf-8
2
+ lib = File.expand_path("../lib", __FILE__)
3
+ $LOAD_PATH.unshift(lib) unless $LOAD_PATH.include?(lib)
4
+ require "elasticsearch/persistence/version"
5
+
6
+ Gem::Specification.new do |s|
7
+ s.name = "elasticsearch-persistence-queryable"
8
+ s.version = Elasticsearch::Persistence::VERSION
9
+ s.authors = ["Karel Minarik"]
10
+ s.email = ["karel.minarik@elasticsearch.org"]
11
+ s.description = "Persistence layer for Ruby models and Elasticsearch."
12
+ s.summary = "Persistence layer for Ruby models and Elasticsearch."
13
+ s.homepage = "https://github.com/elasticsearch/elasticsearch-rails/"
14
+ s.license = "Apache 2"
15
+
16
+ s.files = `git ls-files -z`.split("\x0")
17
+ s.executables = s.files.grep(%r{^bin/}) { |f| File.basename(f) }
18
+ s.test_files = s.files.grep(%r{^(test|spec|features)/})
19
+ s.require_paths = ["lib"]
20
+
21
+ s.extra_rdoc_files = ["README.md", "LICENSE.txt"]
22
+ s.rdoc_options = ["--charset=UTF-8"]
23
+
24
+ s.required_ruby_version = ">= 1.9.3"
25
+
26
+ s.add_dependency "elasticsearch", "> 0.4"
27
+ s.add_dependency "elasticsearch-model", ">= 0.1"
28
+ s.add_dependency "activesupport", "> 3"
29
+ s.add_dependency "activemodel", "> 3"
30
+ s.add_dependency "hashie"
31
+ s.add_dependency "virtus"
32
+ s.add_dependency "jbuilder"
33
+
34
+ s.add_development_dependency "bundler", "~> 1.5"
35
+ s.add_development_dependency "rake"
36
+
37
+ s.add_development_dependency "oj"
38
+
39
+ s.add_development_dependency "rails"
40
+
41
+ s.add_development_dependency "elasticsearch-extensions"
42
+
43
+ s.add_development_dependency "minitest", "~> 4"
44
+ s.add_development_dependency "test-unit" if defined?(RUBY_VERSION) && RUBY_VERSION > "2.2"
45
+ s.add_development_dependency "shoulda-context"
46
+ s.add_development_dependency "mocha"
47
+ s.add_development_dependency "turn"
48
+ s.add_development_dependency "yard"
49
+ s.add_development_dependency "ruby-prof"
50
+ s.add_development_dependency "pry"
51
+ s.add_development_dependency "ci_reporter", "~> 1.9"
52
+
53
+ if defined?(RUBY_VERSION) && RUBY_VERSION > "1.9"
54
+ s.add_development_dependency "simplecov"
55
+ s.add_development_dependency "cane"
56
+ end
57
+ end
@@ -0,0 +1,34 @@
1
+ class Meta
2
+ include Virtus.model
3
+
4
+ attribute :rating
5
+ attribute :have
6
+ attribute :want
7
+ attribute :formats
8
+ end
9
+
10
+ class Album
11
+ include Elasticsearch::Persistence::Model
12
+
13
+ index_name [Rails.application.engine_name, Rails.env].join('-')
14
+
15
+ mapping _parent: { type: 'artist' } do
16
+ indexes :suggest_title, type: 'completion', payloads: true
17
+ indexes :suggest_track, type: 'completion', payloads: true
18
+ end
19
+
20
+ attribute :artist
21
+ attribute :artist_id, String, mapping: { index: 'not_analyzed' }
22
+ attribute :label, Hash, mapping: { type: 'object' }
23
+
24
+ attribute :title
25
+ attribute :suggest_title, String, default: {}, mapping: { type: 'completion', payloads: true }
26
+ attribute :released, Date
27
+ attribute :notes
28
+ attribute :uri
29
+
30
+ attribute :tracklist, Array, mapping: { type: 'object' }
31
+
32
+ attribute :styles
33
+ attribute :meta, Meta, mapping: { type: 'object' }
34
+ end
@@ -0,0 +1,50 @@
1
+ class Artist
2
+ include Elasticsearch::Persistence::Model
3
+
4
+ index_name [Rails.application.engine_name, Rails.env].join('-')
5
+
6
+ analyzed_and_raw = { fields: {
7
+ name: { type: 'string', analyzer: 'snowball' },
8
+ raw: { type: 'string', analyzer: 'keyword' }
9
+ } }
10
+
11
+ attribute :name, String, mapping: analyzed_and_raw
12
+ attribute :suggest_name, String, default: {}, mapping: { type: 'completion', payloads: true }
13
+
14
+ attribute :profile
15
+ attribute :date, Date
16
+
17
+ attribute :members, String, default: [], mapping: analyzed_and_raw
18
+ attribute :members_combined, String, default: [], mapping: { analyzer: 'snowball' }
19
+ attribute :suggest_member, String, default: {}, mapping: { type: 'completion', payloads: true }
20
+
21
+ attribute :urls, String, default: []
22
+ attribute :album_count, Integer, default: 0
23
+
24
+ validates :name, presence: true
25
+
26
+ def albums
27
+ Album.search(
28
+ { query: {
29
+ has_parent: {
30
+ type: 'artist',
31
+ query: {
32
+ filtered: {
33
+ filter: {
34
+ ids: { values: [ self.id ] }
35
+ }
36
+ }
37
+ }
38
+ }
39
+ },
40
+ sort: 'released',
41
+ size: 100
42
+ },
43
+ { type: 'album' }
44
+ )
45
+ end
46
+
47
+ def to_param
48
+ [id, name.parameterize].join('-')
49
+ end
50
+ end
@@ -0,0 +1,8 @@
1
+ <%= simple_form_for @artist do |f| %>
2
+ <%= f.input :name %>
3
+ <%= f.input :profile, as: :text %>
4
+ <%= f.input :date, as: :date %>
5
+ <%= f.input :members, hint: 'Separate names by comma', input_html: { value: f.object.members.join(', ') } %>
6
+
7
+ <%= f.button :submit %>
8
+ <% end %>
@@ -0,0 +1,67 @@
1
+ class ArtistsController < ApplicationController
2
+ before_action :set_artist, only: [:show, :edit, :update, :destroy]
3
+
4
+ rescue_from Elasticsearch::Persistence::Repository::DocumentNotFound do
5
+ render file: "public/404.html", status: 404, layout: false
6
+ end
7
+
8
+ def index
9
+ @artists = Artist.all sort: 'name.raw', _source: ['name', 'album_count']
10
+ end
11
+
12
+ def show
13
+ @albums = @artist.albums
14
+ end
15
+
16
+ def new
17
+ @artist = Artist.new
18
+ end
19
+
20
+ def edit
21
+ end
22
+
23
+ def create
24
+ @artist = Artist.new(artist_params)
25
+
26
+ respond_to do |format|
27
+ if @artist.save refresh: true
28
+ format.html { redirect_to @artist, notice: 'Artist was successfully created.' }
29
+ format.json { render :show, status: :created, location: @artist }
30
+ else
31
+ format.html { render :new }
32
+ format.json { render json: @artist.errors, status: :unprocessable_entity }
33
+ end
34
+ end
35
+ end
36
+
37
+ def update
38
+ respond_to do |format|
39
+ if @artist.update(artist_params, refresh: true)
40
+ format.html { redirect_to @artist, notice: 'Artist was successfully updated.' }
41
+ format.json { render :show, status: :ok, location: @artist }
42
+ else
43
+ format.html { render :edit }
44
+ format.json { render json: @artist.errors, status: :unprocessable_entity }
45
+ end
46
+ end
47
+ end
48
+
49
+ def destroy
50
+ @artist.destroy refresh: true
51
+ respond_to do |format|
52
+ format.html { redirect_to artists_url, notice: 'Artist was successfully destroyed.' }
53
+ format.json { head :no_content }
54
+ end
55
+ end
56
+
57
+ private
58
+ def set_artist
59
+ @artist = Artist.find(params[:id].split('-').first)
60
+ end
61
+
62
+ def artist_params
63
+ a = params.require(:artist)
64
+ a[:members] = a[:members].split(/,\s?/) unless a[:members].is_a?(Array) || a[:members].blank?
65
+ return a
66
+ end
67
+ end
@@ -0,0 +1,53 @@
1
+ require 'test_helper'
2
+
3
+ class ArtistsControllerTest < ActionController::TestCase
4
+ setup do
5
+ IndexManager.create_index force: true
6
+ @artist = Artist.create(id: 1, name: 'TEST')
7
+ Artist.gateway.refresh_index!
8
+ end
9
+
10
+ test "should get index" do
11
+ get :index
12
+ assert_response :success
13
+ assert_not_nil assigns(:artists)
14
+ end
15
+
16
+ test "should get new" do
17
+ get :new
18
+ assert_response :success
19
+ end
20
+
21
+ test "should create artist" do
22
+ assert_difference('Artist.count') do
23
+ post :create, artist: { name: @artist.name }
24
+ Artist.gateway.refresh_index!
25
+ end
26
+
27
+ assert_redirected_to artist_path(assigns(:artist))
28
+ end
29
+
30
+ test "should show artist" do
31
+ get :show, id: @artist
32
+ assert_response :success
33
+ end
34
+
35
+ test "should get edit" do
36
+ get :edit, id: @artist
37
+ assert_response :success
38
+ end
39
+
40
+ test "should update artist" do
41
+ patch :update, id: @artist, artist: { name: @artist.name }
42
+ assert_redirected_to artist_path(assigns(:artist))
43
+ end
44
+
45
+ test "should destroy artist" do
46
+ assert_difference('Artist.count', -1) do
47
+ delete :destroy, id: @artist
48
+ Artist.gateway.refresh_index!
49
+ end
50
+
51
+ assert_redirected_to artists_path
52
+ end
53
+ end
@@ -0,0 +1,57 @@
1
+ <header>
2
+ <h1>
3
+ Artists
4
+ <%= button_to 'New Artist', new_artist_path, method: 'get', tabindex: 5 %>
5
+ </h1>
6
+ </header>
7
+
8
+ <section id="searchbox">
9
+ <%= form_tag search_path, method: 'get' do %>
10
+ <input type="text" name="q" value="<%= params[:q] %>" id="q" autofocus="autofocus" placeholder="start typing to search..." tabindex="0" />
11
+ <% end %>
12
+ </section>
13
+
14
+ <section class="artists">
15
+ <% @artists.each do |artist| %>
16
+ <%= div_for artist, class: 'result clearfix' do %>
17
+ <h2>
18
+ <%= link_to artist do %>
19
+ <span class="name"><%= artist.name %></span>
20
+ <small><%= pluralize artist.album_count, 'album' %></small>
21
+ <% end %>
22
+ </h2>
23
+ <div class="actions clearfix">
24
+ <%= button_to 'Edit', edit_artist_path(artist), method: 'get' %>
25
+ <%= button_to 'Destroy', artist, method: :delete, data: { confirm: 'Are you sure?' } %>
26
+ </div>
27
+ <% end %>
28
+ <% end %>
29
+ </section>
30
+
31
+ <% if @artists.empty? %>
32
+ <section class="no-results">
33
+ <p>The search hasn't returned any results...</p>
34
+ </section>
35
+ <% end %>
36
+
37
+ <script>
38
+ $.widget( "custom.suggest", $.ui.autocomplete, {
39
+ _renderMenu: function( ul, items ) {
40
+ $.each( items, function( index, item ) {
41
+ var category = ul.append( "<li class='ui-autocomplete-category'>" + item.label + "</li>" );
42
+
43
+ $.each( item.value, function( index, item ) {
44
+ var li = $('<li class="ui-autocomplete-item"><a href="<%= Rails.application.config.relative_url_root %>'+ item.payload.url +'">'+ item.text +'</a></li>').data('ui-autocomplete-item', item )
45
+ category.append(li)
46
+ } )
47
+ });
48
+ }
49
+ });
50
+
51
+ $( "#q" ).suggest({
52
+ source: '<%= suggest_path %>',
53
+ select: function(event, ui) {
54
+ document.location.href = '<%= Rails.application.config.relative_url_root %>'+ui.item.payload.url
55
+ }
56
+ });
57
+ </script>
@@ -0,0 +1,51 @@
1
+ <header>
2
+ <h1>
3
+ <span class="back"><%= link_to "〈".html_safe, artists_path, title: "Back" %></span>
4
+ <%= @artist.name %>
5
+ <%= button_to 'Edit', edit_artist_path(@artist), method: 'get' %>
6
+ </h1>
7
+ </header>
8
+
9
+ <p id="notice"><%= notice %></p>
10
+
11
+ <section class="artist-info">
12
+ <span><%= @artist.members.to_sentence last_word_connector: ' and ' %></span> |
13
+ <span><%= pluralize @albums.size, 'album' %></span>
14
+ <p class="artist-profile"><%= @artist.profile %></p>
15
+ </section>
16
+
17
+ <section class="albums">
18
+ <% @albums.each do |album| %>
19
+ <%= div_for album, class: 'clearfix' do %>
20
+ <h3>
21
+ <span class="title"><%= album.title %></span>
22
+ <div class="info">
23
+ <small><%= album.meta.formats.join(', ') %></small>
24
+ <small><%= album.released %></small>
25
+ </div>
26
+ </h3>
27
+
28
+ <div class="cover">
29
+ <%= image_tag "http://ruby-demo-assets.s3.amazonaws.com/discogs/covers/#{album.id}.jpeg", width: '100px', class: 'cover' %>
30
+ </div>
31
+
32
+ <div class="content">
33
+ <% album.tracklist.in_groups_of(album.tracklist.size/2+1).each_with_index do |half, g| %>
34
+ <ul class=<%= cycle 'first', 'second' %> start="<%= g < 1 ? 1 : album.tracklist.size/2+2 %>">
35
+ <% half.compact.each_with_index do |track, i| %>
36
+ <li>
37
+ <i class="counter"><%= g < 1 ? i+1 : i+(g*album.tracklist.size/2+2) %></i>
38
+ <%= track['title'] %>
39
+ <small><%= track['duration'] %></small>
40
+ </li>
41
+ <% end %>
42
+ </ul>
43
+ <% end %>
44
+ </div>
45
+ <% end %>
46
+
47
+ <% end %>
48
+
49
+ <script>$('img').error(function(){ $(this).attr('src', '/images/blank_cover.png'); });</script>
50
+ <script>$(document.location.hash).effect('highlight', 1500)</script>
51
+ </section>
@@ -0,0 +1,226 @@
1
+ /*
2
+ *= require_tree .
3
+ *= require_self
4
+ *= require ui-lightness/jquery-ui-1.10.4.custom.min.css
5
+ */
6
+
7
+ .clearfix {
8
+ *zoom: 1;
9
+ }
10
+
11
+ .clearfix:before,
12
+ .clearfix:after {
13
+ display: table;
14
+ line-height: 0;
15
+ content: "";
16
+ }
17
+
18
+ .clearfix:after {
19
+ clear: both;
20
+ }
21
+
22
+ body {
23
+ font-family: 'Helvetica Neue', Helvetica, sans-serif !important;
24
+ margin: 2em 4em;
25
+ }
26
+
27
+ header {
28
+ margin: 0;
29
+ padding: 0 0 1em 0;
30
+ border-bottom: 1px solid #666;
31
+ }
32
+
33
+ header h1 {
34
+ color: #999;
35
+ font-weight: 100;
36
+ text-transform: uppercase;
37
+ margin: 0; padding: 0;
38
+ }
39
+
40
+ header a {
41
+ color: #0b6aff;
42
+ text-decoration: none;
43
+ }
44
+
45
+ header .back {
46
+ font-size: 100%;
47
+ margin: 0 0.5em 0 -0.5em;
48
+ }
49
+
50
+ h1 form {
51
+ float: right;
52
+ }
53
+
54
+ #searchbox {
55
+ border-bottom: 1px solid #666;
56
+ }
57
+
58
+ #searchbox input {
59
+ color: #444;
60
+ font-size: 100%;
61
+ font-weight: 100;
62
+ border: none;
63
+ padding: 1em 0 1em 0;
64
+ width: 100%;
65
+ }
66
+
67
+ #searchbox input:focus {
68
+ outline-width: 0;
69
+ }
70
+
71
+ .actions form {
72
+ float: right;
73
+ position: relative;
74
+ top: 0.2em;
75
+ }
76
+
77
+ .no-results {
78
+ font-weight: 200;
79
+ font-size: 200%;
80
+ }
81
+
82
+ .result,
83
+ .artist {
84
+ padding: 1em 0 1em 0;
85
+ margin: 0;
86
+ border-bottom: 1px solid #999;
87
+ }
88
+
89
+ .result:hover,
90
+ .artist:hover {
91
+ background: #f9f9f9;
92
+ }
93
+
94
+ .result h2,
95
+ .artist h2 {
96
+ color: #444;
97
+ margin: 0;
98
+ padding: 0;
99
+ }
100
+
101
+ .artist h2 {
102
+ float: left;
103
+ }
104
+
105
+ .result h2 a,
106
+ .artist h2 a {
107
+ color: #444;
108
+ }
109
+
110
+ .result h2 small,
111
+ .artist h2 small {
112
+ font-size: 70%;
113
+ font-weight: 100;
114
+ margin-left: 0.5em;
115
+ }
116
+
117
+ .result h2 a,
118
+ .artist h2 a {
119
+ text-decoration: none;
120
+ }
121
+
122
+ .result h2 a:hover name,
123
+ .artist h2 a:hover .name {
124
+ text-decoration: underline;
125
+ }
126
+
127
+ .result .small {
128
+ font-size: 90%;
129
+ font-weight: 200;
130
+ padding: 0;
131
+ margin: 0.25em 0 0.25em 0;
132
+ }
133
+
134
+ .result .small .label {
135
+ color: #999;
136
+ font-size: 80%;
137
+ min-width: 5em;
138
+ display: inline-block;
139
+ }
140
+
141
+ .artist-info {
142
+ color: #5f5f5f;
143
+ text-transform: uppercase;
144
+ font-weight: 200;
145
+ border-bottom: 1px solid #666;
146
+ padding: 0 0 1em 0;
147
+ margin: 0 0 1em 0;
148
+ }
149
+
150
+ .artist-profile {
151
+ color: #999;
152
+ font-size: 95%;
153
+ font-weight: 100;
154
+ text-transform: none;
155
+ padding: 0;
156
+ margin: 0.25em 0 0 0;
157
+ }
158
+
159
+ .album {
160
+ margin: 0 0 4em 0;
161
+ }
162
+
163
+ .album .cover {
164
+ float: left;
165
+ width: 150px;
166
+ }
167
+
168
+ .album .cover img {
169
+ border: 1px solid rgba(0,0,0,0.15);
170
+ box-shadow: 0px 0px 1px 0px rgba(0,0,0,0.05);
171
+ }
172
+
173
+ .album .content {
174
+ float: left;
175
+ margin-left: 25px;
176
+ }
177
+
178
+ .album .content ul {
179
+ float: left;
180
+ margin: 0 2em 0 0;
181
+ padding: 0;
182
+ min-width: 18em;
183
+ }
184
+
185
+ .album .content ul li {
186
+ line-height: 1.5em;
187
+ padding: 0.5em 0 0.5em 0;
188
+ border-bottom:1px solid #f8f8f8;
189
+ list-style: none;
190
+ }
191
+
192
+ .album .content ul li .counter {
193
+ color: #999;
194
+ font-style: normal;
195
+ font-size: 80%;
196
+ font-weight: 100;
197
+ margin-right: 0.5em;
198
+ }
199
+
200
+ .album h3 {
201
+ margin: 0; padding: 0;
202
+ border-bottom: 2px solid #e0e0e0;
203
+ padding: 0 0 0.5em 0;
204
+ margin: 0 0 1em 0;
205
+ }
206
+
207
+ .album h3 .title {
208
+ text-transform: uppercase;
209
+ font-weight: 200;
210
+ }
211
+
212
+ .album small {
213
+ color: #a3a3a3;
214
+ font-weight: 200;
215
+ }
216
+
217
+ .album .info {
218
+ float: right;
219
+ }
220
+
221
+ em[class^=hl] {
222
+ font-style: normal;
223
+ background: #e6efff;
224
+ padding: 0.15em 0.35em;
225
+ border-radius: 5px;
226
+ }