elasticsearch-rails 0.1.0 → 0.1.1

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.
@@ -0,0 +1,6 @@
1
+ ## 0.1.1
2
+
3
+ * Improved the Rake tasks
4
+ * Improved the example application templates
5
+
6
+ ## 0.1.1 (Initial Version)
data/README.md CHANGED
@@ -78,30 +78,32 @@ You should see the duration of the request to Elasticsearch as part of each log
78
78
 
79
79
  You can generate a fully working example Ruby on Rails application, with an `Article` model and a search form,
80
80
  to play with (it even downloads _Elasticsearch_ itself, generates the application skeleton and leaves you with
81
- a _Git_ repository to explore the steps and the code):
81
+ a _Git_ repository to explore the steps and the code) with the
82
+ [`01-basic.rb`](https://github.com/elasticsearch/elasticsearch-rails/blob/master/elasticsearch-rails/lib/rails/templates/01-basic.rb) template:
82
83
 
83
84
  ```bash
84
85
  rails new searchapp --skip --skip-bundle --template https://raw.github.com/elasticsearch/elasticsearch-rails/master/elasticsearch-rails/lib/rails/templates/01-basic.rb
85
86
  ```
86
87
 
87
- Run the same command again, in the same folder, with the `02-pretty` template to add features such as
88
- a custom `Article.search` method, result highlighting and [_Bootstrap_](http://getbootstrap.com) integration:
88
+ Run the same command again, in the same folder, with the
89
+ [`02-pretty`](https://github.com/elasticsearch/elasticsearch-rails/blob/master/elasticsearch-rails/lib/rails/templates/02-pretty.rb)
90
+ template to add features such as a custom `Article.search` method, result highlighting and
91
+ [_Bootstrap_](http://getbootstrap.com) integration:
89
92
 
90
93
  ```bash
91
94
  rails new searchapp --skip --skip-bundle --template https://raw.github.com/elasticsearch/elasticsearch-rails/master/elasticsearch-rails/lib/rails/templates/02-pretty.rb
92
95
  ```
93
96
 
94
- NOTE: A third, much more complex template, demonstrating other features such as faceted navigation or
95
- query suggestions is being worked on.
97
+ Run the same command with the [`03-expert.rb`](https://github.com/elasticsearch/elasticsearch-rails/blob/master/elasticsearch-rails/lib/rails/templates/03-expert.rb)
98
+ template to refactor the application into a more complex use case,
99
+ with couple of hundreds of The New York Times articles as the example content.
100
+ The template will extract the Elasticsearch integration into a `Searchable` "concern" module,
101
+ define complex mapping, custom serialization, implement faceted navigation and suggestions as a part of
102
+ a complex query, and add a _Sidekiq_-based worker for updating the index in the background.
96
103
 
97
- ## TODO
98
-
99
- This is an initial release of the `elasticsearch-rails` library. Many more features are planned and/or
100
- being worked on, such as:
101
-
102
- * Rake tasks for convenient (re)indexing your models from the command line
103
- * Hooking into Rails' notification system to display Elasticsearch related statistics in the application log
104
- * Instrumentation support for NewRelic integration
104
+ ```bash
105
+ rails new searchapp --skip --skip-bundle --template https://raw.github.com/elasticsearch/elasticsearch-rails/master/elasticsearch-rails/lib/rails/templates/03-expert.rb
106
+ ```
105
107
 
106
108
  ## License
107
109
 
@@ -31,6 +31,7 @@ Gem::Specification.new do |s|
31
31
 
32
32
  s.add_development_dependency "lograge"
33
33
 
34
+ s.add_development_dependency "minitest", "~> 4.0"
34
35
  s.add_development_dependency "shoulda-context"
35
36
  s.add_development_dependency "mocha"
36
37
  s.add_development_dependency "turn"
@@ -56,16 +56,17 @@ namespace :elasticsearch do
56
56
  rescue NoMethodError; end
57
57
  end
58
58
 
59
- klass.import force: ENV.fetch('FORCE', false),
60
- batch_size: ENV.fetch('BATCH', 1000).to_i,
61
- index: ENV.fetch('INDEX', nil),
62
- type: ENV.fetch('TYPE', nil) do |response|
59
+ total_errors = klass.import force: ENV.fetch('FORCE', false),
60
+ batch_size: ENV.fetch('BATCH', 1000).to_i,
61
+ index: ENV.fetch('INDEX', nil),
62
+ type: ENV.fetch('TYPE', nil) do |response|
63
63
  pbar.inc response['items'].size if pbar
64
64
  STDERR.flush
65
65
  STDOUT.flush
66
66
  end
67
67
  pbar.finish if pbar
68
68
 
69
+ puts "[IMPORT] #{total_errors} errors occurred" unless total_errors.zero?
69
70
  puts '[IMPORT] Done'
70
71
  end
71
72
 
@@ -96,6 +97,7 @@ namespace :elasticsearch do
96
97
 
97
98
  ENV['CLASS'] = klass.to_s
98
99
  Rake::Task["elasticsearch:import:model"].invoke
100
+ Rake::Task["elasticsearch:import:model"].reenable
99
101
  puts
100
102
  end
101
103
  end
@@ -1,5 +1,5 @@
1
1
  module Elasticsearch
2
2
  module Rails
3
- VERSION = "0.1.0"
3
+ VERSION = "0.1.1"
4
4
  end
5
5
  end
@@ -20,8 +20,8 @@
20
20
  #
21
21
  # =====================================================================================================
22
22
 
23
- require 'elasticsearch'
24
- client = Elasticsearch::Client.new
23
+ require 'uri'
24
+ require 'net/http'
25
25
 
26
26
  at_exit do
27
27
  pid = File.read("#{destination_root}/tmp/pids/elasticsearch.pid") rescue nil
@@ -33,7 +33,7 @@ end
33
33
 
34
34
  run "touch tmp/.gitignore"
35
35
 
36
- append_to_file ".gitignore", "vendor/elasticsearch-0.90.7/\n"
36
+ append_to_file ".gitignore", "vendor/elasticsearch-1.0.1/\n"
37
37
 
38
38
  git :init
39
39
  git add: "."
@@ -41,12 +41,12 @@ git commit: "-m 'Initial commit: Clean application'"
41
41
 
42
42
  # ----- Download Elasticsearch --------------------------------------------------------------------
43
43
 
44
- unless (client.ping rescue false)
44
+ unless (Net::HTTP.get(URI.parse('http://localhost:9200')) rescue false)
45
45
  COMMAND = <<-COMMAND.gsub(/^ /, '')
46
- curl -# -O "http://download.elasticsearch.org/elasticsearch/elasticsearch/elasticsearch-0.90.7.tar.gz"
47
- tar -zxf elasticsearch-0.90.7.tar.gz
48
- rm -f elasticsearch-0.90.7.tar.gz
49
- ./elasticsearch-0.90.7/bin/elasticsearch -p #{destination_root}/tmp/pids/elasticsearch.pid
46
+ curl -# -O "http://download.elasticsearch.org/elasticsearch/elasticsearch/elasticsearch-1.0.1.tar.gz"
47
+ tar -zxf elasticsearch-1.0.1.tar.gz
48
+ rm -f elasticsearch-1.0.1.tar.gz
49
+ ./elasticsearch-1.0.1/bin/elasticsearch -d -p #{destination_root}/tmp/pids/elasticsearch.pid
50
50
  COMMAND
51
51
 
52
52
  puts "\n"
@@ -70,7 +70,7 @@ unless (client.ping rescue false)
70
70
  run "(#{exec})" # Launch Elasticsearch in subshell
71
71
  end
72
72
  end
73
- end
73
+ end unless ENV['RAILS_NO_ES_INSTALL']
74
74
 
75
75
  # ----- Add README --------------------------------------------------------------------------------
76
76
 
@@ -123,6 +123,7 @@ gem 'mocha', group: 'test', require: 'mocha/setup'
123
123
  comment_lines 'Gemfile', /gem 'coffee/
124
124
  comment_lines 'Gemfile', /gem 'sass/
125
125
  comment_lines 'Gemfile', /gem 'uglifier/
126
+ uncomment_lines 'Gemfile', /gem 'therubyracer/
126
127
 
127
128
  # ----- Add gems into Gemfile ---------------------------------------------------------------------
128
129
 
@@ -134,6 +135,23 @@ gem 'elasticsearch', git: 'git://github.com/elasticsearch/elasticsearch-ru
134
135
  gem 'elasticsearch-model', git: 'git://github.com/elasticsearch/elasticsearch-rails.git'
135
136
  gem 'elasticsearch-rails', git: 'git://github.com/elasticsearch/elasticsearch-rails.git'
136
137
 
138
+
139
+ git add: "Gemfile*"
140
+ git commit: "-m 'Added libraries into Gemfile'"
141
+
142
+ # ----- Disable asset logging in development ------------------------------------------------------
143
+
144
+ puts
145
+ say_status "Application", "Disabling asset logging in development...\n", :yellow
146
+ puts '-'*80, ''; sleep 0.25
147
+
148
+ environment 'config.assets.logger = false', env: 'development'
149
+ gem 'quiet_assets', group: "development"
150
+
151
+ git add: "Gemfile*"
152
+ git add: "config/"
153
+ git commit: "-m 'Disabled asset logging in development'"
154
+
137
155
  # ----- Install gems ------------------------------------------------------------------------------
138
156
 
139
157
  puts
@@ -142,9 +160,6 @@ puts '-'*80, ''
142
160
 
143
161
  run "bundle install"
144
162
 
145
- git add: "Gemfile*"
146
- git commit: "-m 'Added libraries into Gemfile'"
147
-
148
163
  # ----- Generate Article resource -----------------------------------------------------------------
149
164
 
150
165
  puts
@@ -169,6 +184,7 @@ file 'app/models/article.rb', <<-CODE
169
184
  class Article < ActiveRecord::Base
170
185
  include Elasticsearch::Model
171
186
  include Elasticsearch::Model::Callbacks
187
+ #{'attr_accessible :title, :content, :published_on' if Rails::VERSION::STRING < '4'}
172
188
  end
173
189
  CODE
174
190
 
@@ -221,7 +237,7 @@ resources :articles do
221
237
  end
222
238
  CODE
223
239
 
224
- gsub_file 'test/controllers/articles_controller_test.rb', %r{setup do.*?end}m, <<-CODE
240
+ gsub_file "#{Rails::VERSION::STRING > '4' ? 'test/controllers' : 'test/functional'}/articles_controller_test.rb", %r{setup do.*?end}m, <<-CODE
225
241
  setup do
226
242
  @article = articles(:one)
227
243
 
@@ -230,7 +246,7 @@ setup do
230
246
  end
231
247
  CODE
232
248
 
233
- inject_into_file 'test/controllers/articles_controller_test.rb', after: %r{test "should get index" do.*?end}m do
249
+ inject_into_file "#{Rails::VERSION::STRING > '4' ? 'test/controllers' : 'test/functional'}/articles_controller_test.rb", after: %r{test "should get index" do.*?end}m do
234
250
  <<-CODE
235
251
 
236
252
 
@@ -300,19 +316,21 @@ git log: "--reverse --oneline"
300
316
 
301
317
  # ----- Start the application ---------------------------------------------------------------------
302
318
 
303
- require 'net/http'
304
- if (begin; Net::HTTP.get(URI('http://localhost:3000')); rescue Errno::ECONNREFUSED; false; rescue Exception; true; end)
305
- puts "\n"
306
- say_status "ERROR", "Some other application is running on port 3000!\n", :red
307
- puts '-'*80
319
+ unless ENV['RAILS_NO_SERVER_START']
320
+ require 'net/http'
321
+ if (begin; Net::HTTP.get(URI('http://localhost:3000')); rescue Errno::ECONNREFUSED; false; rescue Exception; true; end)
322
+ puts "\n"
323
+ say_status "ERROR", "Some other application is running on port 3000!\n", :red
324
+ puts '-'*80
308
325
 
309
- port = ask("Please provide free port:", :bold)
310
- else
311
- port = '3000'
312
- end
326
+ port = ask("Please provide free port:", :bold)
327
+ else
328
+ port = '3000'
329
+ end
313
330
 
314
- puts "", "="*80
315
- say_status "DONE", "\e[1mStarting the application.\e[0m", :yellow
316
- puts "="*80, ""
331
+ puts "", "="*80
332
+ say_status "DONE", "\e[1mStarting the application.\e[0m", :yellow
333
+ puts "="*80, ""
317
334
 
318
- run "rails server --port=#{port}"
335
+ run "rails server --port=#{port}"
336
+ end
@@ -22,6 +22,19 @@ README
22
22
  git add: "README.rdoc"
23
23
  git commit: "-m '[02] Updated the application README'"
24
24
 
25
+ # ----- Update application.rb ---------------------------------------------------------------------
26
+
27
+ puts
28
+ say_status "Rubygems", "Adding Rails logger integration...\n", :yellow
29
+ puts '-'*80, ''; sleep 0.25
30
+
31
+ insert_into_file 'config/application.rb',
32
+ "\n\nrequire 'elasticsearch/rails/instrumentation'\n",
33
+ after: 'Bundler.require(:default, Rails.env)'
34
+
35
+ git add: "config/application.rb"
36
+ git commit: "-m 'Added the Rails logger integration to application.rb'"
37
+
25
38
  # ----- Add gems into Gemfile ---------------------------------------------------------------------
26
39
 
27
40
  puts
@@ -73,7 +86,7 @@ insert_into_file 'app/models/article.rb', <<-CODE, after: 'include Elasticsearch
73
86
  end
74
87
  CODE
75
88
 
76
- gsub_file "test/models/article_test.rb", %r{# test "the truth" do.*?# end}m, <<-CODE
89
+ gsub_file "#{Rails::VERSION::STRING > '4' ? 'test/models' : 'test/unit' }/article_test.rb", %r{# test "the truth" do.*?# end}m, <<-CODE
77
90
 
78
91
  test "has a search method delegating to __elasticsearch__" do
79
92
  Article.__elasticsearch__.expects(:search).with do |definition|
@@ -85,7 +98,7 @@ gsub_file "test/models/article_test.rb", %r{# test "the truth" do.*?# end}m, <<-
85
98
  CODE
86
99
 
87
100
  git add: "app/models/article.rb"
88
- git add: "test/models/article_test.rb"
101
+ git add: "test/**/article_test.rb"
89
102
  git commit: "-m 'Added an `Article.search` method'"
90
103
 
91
104
  # ----- Add loading Bootstrap assets --------------------------------------------------------------
@@ -266,19 +279,21 @@ git log: "--reverse --oneline pretty...basic"
266
279
 
267
280
  # ----- Start the application ---------------------------------------------------------------------
268
281
 
269
- require 'net/http'
270
- if (begin; Net::HTTP.get(URI('http://localhost:3000')); rescue Errno::ECONNREFUSED; false; rescue Exception; true; end)
271
- puts "\n"
272
- say_status "ERROR", "Some other application is running on port 3000!\n", :red
273
- puts '-'*80
282
+ unless ENV['RAILS_NO_SERVER_START']
283
+ require 'net/http'
284
+ if (begin; Net::HTTP.get(URI('http://localhost:3000')); rescue Errno::ECONNREFUSED; false; rescue Exception; true; end)
285
+ puts "\n"
286
+ say_status "ERROR", "Some other application is running on port 3000!\n", :red
287
+ puts '-'*80
274
288
 
275
- port = ask("Please provide free port:", :bold)
276
- else
277
- port = '3000'
278
- end
289
+ port = ask("Please provide free port:", :bold)
290
+ else
291
+ port = '3000'
292
+ end
279
293
 
280
- puts "", "="*80
281
- say_status "DONE", "\e[1mStarting the application. Open http://localhost:#{port}\e[0m", :yellow
282
- puts "="*80, ""
294
+ puts "", "="*80
295
+ say_status "DONE", "\e[1mStarting the application. Open http://localhost:#{port}\e[0m", :yellow
296
+ puts "="*80, ""
283
297
 
284
- run "rails server --port=#{port}"
298
+ run "rails server --port=#{port}"
299
+ end
@@ -2,113 +2,98 @@
2
2
 
3
3
  # (See: 01-basic.rb, 02-pretty.rb)
4
4
 
5
- # ----- Move the search form into partial ---------------------------------------------------------
5
+ append_to_file 'README.rdoc', <<-README
6
+
7
+ == [3] Expert
8
+
9
+ The `expert` template changes to a complex database schema with model relationships: article belongs
10
+ to a category, has many authors and comments.
11
+
12
+ * The Elasticsearch integration is refactored into the `Searchable` concern
13
+ * A complex mapping for the index is defined
14
+ * A custom serialization is defined in `Article#as_indexed_json`
15
+ * The `search` method is amended with facets and suggestions
16
+ * A [Sidekiq](http://sidekiq.org) worker for handling index updates in background is added
17
+ * A custom `SearchController` with associated view is added
18
+ * A Rails initializer is added to customize the Elasticsearch client configuration
19
+ * Seed script and example data from New York Times is added
20
+
21
+ README
22
+
23
+ git add: "README.rdoc"
24
+ git commit: "-m '[03] Updated the application README'"
25
+
26
+ # ----- Add gems into Gemfile ---------------------------------------------------------------------
6
27
 
7
28
  puts
8
- say_status "View", "Moving the search form into partial template...\n", :yellow
9
- puts '-'*80, ''; sleep 0.5
29
+ say_status "Rubygems", "Adding Rubygems into Gemfile...\n", :yellow
30
+ puts '-'*80, ''; sleep 0.25
10
31
 
11
- gsub_file 'app/views/articles/index.html.erb', %r{\n<hr>.*<hr>\n}m do |match|
12
- create_file "app/views/articles/_search_form.html.erb", match
13
- "\n<%= render partial: 'search_form' %>\n"
14
- end
32
+ gem "oj"
15
33
 
16
- git :add => 'app/views/articles/index.html.erb app/views/articles/_search_form.html.erb'
17
- git :commit => "-m 'Moved the search form into a partial template'"
34
+ git add: "Gemfile*"
35
+ git commit: "-m 'Added Ruby gems'"
18
36
 
19
- # ----- Move the model integration into a concern -------------------------------------------------
37
+ # ----- Customize the Rails console ---------------------------------------------------------------
20
38
 
21
39
  puts
22
- say_status "Model", "Refactoring the model integration...\n", :yellow
23
- puts '-'*80, ''; sleep 0.5
40
+ say_status "Rails", "Customizing `rails console`...\n", :yellow
41
+ puts '-'*80, ''; sleep 0.25
24
42
 
25
- create_file 'app/models/concerns/searchable.rb', <<-CODE
26
- module Searchable
27
- extend ActiveSupport::Concern
28
43
 
29
- included do
30
- include Elasticsearch::Model
31
- end
44
+ gem "pry", group: 'development'
32
45
 
33
- module ClassMethods
34
- def search(query)
35
- __elasticsearch__.search(
36
- {
37
- query: {
38
- multi_match: {
39
- query: query,
40
- fields: ['title^10', 'content']
41
- }
42
- },
43
- highlight: {
44
- pre_tags: ['<em class="label label-highlight">'],
45
- post_tags: ['</em>'],
46
- fields: {
47
- title: { number_of_fragments: 0 },
48
- content: { fragment_size: 25 }
49
- }
50
- }
51
- }
52
- )
53
- end
46
+ environment nil, env: 'development' do
47
+ %q{
48
+ console do
49
+ config.console = Pry
50
+ Pry.config.history.file = Rails.root.join('tmp/console_history.rb').to_s
51
+ Pry.config.prompt = [ proc { |obj, nest_level, _| "(#{obj})> " },
52
+ proc { |obj, nest_level, _| ' '*obj.to_s.size + ' '*(nest_level+1) + '| ' } ]
54
53
  end
54
+ }
55
55
  end
56
- CODE
57
56
 
58
- remove_file 'app/models/article.rb'
59
- create_file 'app/models/article.rb', <<-CODE
60
- class Article < ActiveRecord::Base
61
- include Searchable
62
- end
63
- CODE
57
+ git add: "Gemfile*"
58
+ git add: "config/"
59
+ git commit: "-m 'Added Pry as the console for development'"
64
60
 
65
- git :add => 'app/models/'
66
- git :commit => "-m 'Refactored the Elasticsearch integration into a concern\n\nSee:\n\n* http://37signals.com/svn/posts/3372-put-chubby-models-on-a-diet-with-concerns\n* http://joshsymonds.com/blog/2012/10/25/rails-concerns-v-searchable-with-elasticsearch/'"
67
-
68
- # ----- Add initializer ---------------------------------------------------------------------------
61
+ # ----- Disable asset logging in development ------------------------------------------------------
69
62
 
70
63
  puts
71
- say_status "Application", "Adding configuration in an initializer...\n", :yellow
72
- puts '-'*80, ''; sleep 0.5
64
+ say_status "Application", "Disabling asset logging in development...\n", :yellow
65
+ puts '-'*80, ''; sleep 0.25
73
66
 
74
- create_file 'config/initializers/elasticsearch.rb', <<-CODE
75
- # Connect to specific Elasticsearch cluster
76
- ELASTICSEARCH_URL = ENV['ELASTICSEARCH_URL'] || 'http://localhost:9200'
67
+ environment 'config.assets.logger = false', env: 'development'
68
+ gem 'quiet_assets', group: "development"
77
69
 
78
- # Print Curl-formatted traces in development
79
- #
80
- if Rails.env.development?
81
- tracer = ActiveSupport::Logger.new(STDERR)
82
- tracer.level = Logger::INFO
83
- end
70
+ git add: "Gemfile*"
71
+ git add: "config/"
72
+ git commit: "-m 'Disabled asset logging in development'"
84
73
 
85
- Elasticsearch::Model.client Elasticsearch::Client.new tracer: tracer, host: ELASTICSEARCH_URL
86
- CODE
74
+ # ----- Run bundle install ------------------------------------------------------------------------
87
75
 
88
- git :add => 'config/initializers'
89
- git :commit => "-m 'Added application initializer with Elasticsearch configuration'"
76
+ run "bundle install"
90
77
 
91
- # ----- Generate and define data model for books --------------------------------------------------
78
+ # ----- Define and generate schema ----------------------------------------------------------------
92
79
 
93
80
  puts
94
- say_status "Database", "Adding complex schema and data for books...\n", :yellow
95
- puts '-'*80, ''; sleep 0.5
81
+ say_status "Models", "Adding complex schema...\n", :yellow
82
+ puts '-'*80, ''
96
83
 
97
84
  generate :scaffold, "Category title"
98
- generate :scaffold, "Book title:string content:text downloads:integer category:references"
99
85
  generate :scaffold, "Author first_name last_name"
100
- generate :scaffold, "Authorship book:references author:references"
86
+ generate :scaffold, "Authorship article:references author:references"
101
87
 
102
- insert_into_file "app/models/category.rb", :before => "end" do
103
- <<-CODE
104
- has_many :books
105
- CODE
106
- end
88
+ generate :model, "Comment body:text user:string user_location:string stars:integer pick:boolean article:references"
89
+ generate :migration, "CreateArticlesCategories article:references category:references"
90
+
91
+ rake "db:drop"
92
+ rake "db:migrate"
107
93
 
108
- insert_into_file "app/models/book.rb", :before => "end" do
94
+ insert_into_file "app/models/category.rb", :before => "end" do
109
95
  <<-CODE
110
- has_many :authorships
111
- has_many :authors, through: :authorships
96
+ has_and_belongs_to_many :articles
112
97
  CODE
113
98
  end
114
99
 
@@ -122,764 +107,192 @@ insert_into_file "app/models/author.rb", :before => "end" do
122
107
  CODE
123
108
  end
124
109
 
125
- # ----- Migrate the database --------------------------------------------------------------------------
110
+ gsub_file "app/models/authorship.rb", %r{belongs_to :article$}, <<-CODE
111
+ belongs_to :article, touch: true
112
+ CODE
113
+
114
+ insert_into_file "app/models/article.rb", after: "ActiveRecord::Base" do
115
+ <<-CODE
116
+
117
+ has_and_belongs_to_many :categories, after_add: [ lambda { |a,c| Indexer.perform_async(:update, a.class.to_s, a.id) } ],
118
+ after_remove: [ lambda { |a,c| Indexer.perform_async(:update, a.class.to_s, a.id) } ]
119
+ has_many :authorships
120
+ has_many :authors, through: :authorships
121
+ has_many :comments
122
+ CODE
123
+ end
124
+
125
+ gsub_file "app/models/comment.rb", %r{belongs_to :article$}, <<-CODE
126
+ belongs_to :article, touch: true
127
+ CODE
126
128
 
127
- rake "db:migrate"
129
+ git add: "."
130
+ git commit: "-m 'Generated Category, Author and Comment resources'"
128
131
 
129
- # ----- Create the seed data --------------------------------------------------------------------------
132
+ # ----- Add the `abstract` column -----------------------------------------------------------------
130
133
 
131
- remove_file 'db/seeds.rb'
132
- create_file 'db/seeds.rb', <<-CODE
133
- # encoding: UTF-8
134
-
135
- require 'yaml'
136
-
137
- books = YAML.parse <<-DATA
138
- ---
139
- - :title: Dracula
140
- :authors:
141
- - :last_name: Stoker
142
- :first_name: Bram
143
- :downloads: 12197
144
- :category: Fiction
145
- :content: |
146
- _3 May. Bistritz._--Left Munich at 8:35 P. M., on 1st May, arriving at
147
- Vienna early next morning; should have arrived at 6:46, but train was an
148
- hour late. Buda-Pesth seems a wonderful place, from the glimpse which I
149
- got of it from the train and the little I could walk through the
150
- streets. I feared to go very far from the station, as we had arrived
151
- late and would start as near the correct time as possible. The
152
- impression I had was that we were leaving the West and entering the
153
- East; the most western of splendid bridges over the Danube, which is
154
- here of noble width and depth, took us among the traditions of Turkish
155
- rule.
156
-
157
- We left in pretty good time, and came after nightfall to Klausenburgh.
158
- Here I stopped for the night at the Hotel Royale. I had for dinner, or
159
- rather supper, a chicken done up some way with red pepper, which was
160
- very good but thirsty. (_Mem._, get recipe for Mina.) I asked the
161
- waiter, and he said it was called "paprika hendl," and that, as it was a
162
- national dish, I should be able to get it anywhere along the
163
- Carpathians. I found my smattering of German very useful here; indeed, I
164
- don't know how I should be able to get on without it.
165
-
166
- Having had some time at my disposal when in London, I had visited the
167
- British Museum, and made search among the books and maps in the library
168
- regarding Transylvania; it had struck me that some foreknowledge of the
169
- country could hardly fail to have some importance in dealing with a
170
- nobleman of that country. I find that the district he named is in the
171
- extreme east of the country, just on the borders of three states,
172
- Transylvania, Moldavia and Bukovina, in the midst of the Carpathian
173
- mountains; one of the wildest and least known portions of Europe. I was
174
- not able to light on any map or work giving the exact locality of the
175
- Castle Dracula, as there are no maps of this country as yet to compare
176
- with our own Ordnance Survey maps; but I found that Bistritz, the post
177
- town named by Count Dracula, is a fairly well-known place. I shall enter
178
- here some of my notes, as they may refresh my memory when I talk over my
179
- travels with Mina.
180
-
181
- In the population of Transylvania there are four distinct nationalities:
182
- Saxons in the South, and mixed with them the Wallachs, who are the
183
- descendants of the Dacians; Magyars in the West, and Szekelys in the
184
- East and North. I am going among the latter, who claim to be descended
185
- from Attila and the Huns. This may be so, for when the Magyars conquered
186
- the country in the eleventh century they found the Huns settled in it. I
187
- read that every known superstition in the world is gathered into the
188
- horseshoe of the Carpathians, as if it were the centre of some sort of
189
- imaginative whirlpool; if so my stay may be very interesting. (_Mem._, I
190
- must ask the Count all about them.)
191
-
192
- I did not sleep well, though my bed was comfortable enough, for I had
193
- all sorts of queer dreams. There was a dog howling all night under my
194
- window, which may have had something to do with it; or it may have been
195
- the paprika, for I had to drink up all the water in my carafe, and was
196
- still thirsty. Towards morning I slept and was wakened by the continuous
197
- knocking at my door, so I guess I must have been sleeping soundly then.
198
- I had for breakfast more paprika, and a sort of porridge of maize flour
199
- which they said was "mamaliga," and egg-plant stuffed with forcemeat, a
200
- very excellent dish, which they call "impletata." (_Mem._, get recipe
201
- for this also.) I had to hurry breakfast, for the train started a little
202
- before eight, or rather it ought to have done so, for after rushing to
203
- the station at 7:30 I had to sit in the carriage for more than an hour
204
- before we began to move. It seems to me that the further east you go the
205
- more unpunctual are the trains. What ought they to be in China?
206
-
207
- All day long we seemed to dawdle through a country which was full of
208
- beauty of every kind. Sometimes we saw little towns or castles on the
209
- top of steep hills such as we see in old missals; sometimes we ran by
210
- rivers and streams which seemed from the wide stony margin on each side
211
- of them to be subject to great floods. It takes a lot of water, and
212
- running strong, to sweep the outside edge of a river clear. At every
213
- station there were groups of people, sometimes crowds, and in all sorts
214
- of attire. Some of them were just like the peasants at home or those I
215
- saw coming through France and Germany, with short jackets and round hats
216
- and home-made trousers; but others were very picturesque. The women
217
- looked pretty, except when you got near them, but they were very clumsy
218
- about the waist. They had all full white sleeves of some kind or other,
219
- and most of them had big belts with a lot of strips of something
220
- fluttering from them like the dresses in a ballet, but of course there
221
- were petticoats under them. The strangest figures we saw were the
222
- Slovaks, who were more barbarian than the rest, with their big cow-boy
223
- hats, great baggy dirty-white trousers, white linen shirts, and enormous
224
- heavy leather belts, nearly a foot wide, all studded over with brass
225
- nails. They wore high boots, with their trousers tucked into them, and
226
- had long black hair and heavy black moustaches. They are very
227
- picturesque, but do not look prepossessing. On the stage they would be
228
- set down at once as some old Oriental band of brigands. They are,
229
- however, I am told, very harmless and rather wanting in natural
230
- self-assertion.
231
-
232
- It was on the dark side of twilight when we got to Bistritz, which is a
233
- very interesting old place. Being practically on the frontier--for the
234
- Borgo Pass leads from it into Bukovina--it has had a very stormy
235
- existence, and it certainly shows marks of it. Fifty years ago a series
236
- of great fires took place, which made terrible havoc on five separate
237
- occasions. At the very beginning of the seventeenth century it underwent
238
- a siege of three weeks and lost 13,000 people, the casualties of war
239
- proper being assisted by famine and disease.
240
-
241
- Count Dracula had directed me to go to the Golden Krone Hotel, which I
242
- found, to my great delight, to be thoroughly old-fashioned, for of
243
- course I wanted to see all I could of the ways of the country. I was
244
- evidently expected, for when I got near the door I faced a
245
- cheery-looking elderly woman in the usual peasant dress--white
246
- undergarment with long double apron, front, and back, of coloured stuff
247
- fitting almost too tight for modesty. When I came close she bowed and
248
- said, "The Herr Englishman?" "Yes," I said, "Jonathan Harker." She
249
- smiled, and gave some message to an elderly man in white shirt-sleeves,
250
- who had followed her to the door. He went, but immediately returned with
251
- a letter:--
252
-
253
- "My Friend.--Welcome to the Carpathians. I am anxiously expecting
254
- you. Sleep well to-night. At three to-morrow the diligence will
255
- start for Bukovina; a place on it is kept for you. At the Borgo
256
- Pass my carriage will await you and will bring you to me. I trust
257
- that your journey from London has been a happy one, and that you
258
- will enjoy your stay in my beautiful land.
259
-
260
- "Your friend,
261
-
262
- "DRACULA."
263
-
264
- - :title: Beyond Good and Evil
265
- :authors:
266
- - :last_name: Nietzsche
267
- :first_name: Friedrich Wilhelm
268
- :downloads: 8222
269
- :category: Philosophy
270
- :content: |
271
- SUPPOSING that Truth is a woman--what then? Is there not ground
272
- for suspecting that all philosophers, in so far as they have been
273
- dogmatists, have failed to understand women--that the terrible
274
- seriousness and clumsy importunity with which they have usually paid
275
- their addresses to Truth, have been unskilled and unseemly methods for
276
- winning a woman? Certainly she has never allowed herself to be won; and
277
- at present every kind of dogma stands with sad and discouraged mien--IF,
278
- indeed, it stands at all! For there are scoffers who maintain that it
279
- has fallen, that all dogma lies on the ground--nay more, that it is at
280
- its last gasp. But to speak seriously, there are good grounds for hoping
281
- that all dogmatizing in philosophy, whatever solemn, whatever conclusive
282
- and decided airs it has assumed, may have been only a noble puerilism
283
- and tyronism; and probably the time is at hand when it will be once
284
- and again understood WHAT has actually sufficed for the basis of such
285
- imposing and absolute philosophical edifices as the dogmatists have
286
- hitherto reared: perhaps some popular superstition of immemorial time
287
- (such as the soul-superstition, which, in the form of subject- and
288
- ego-superstition, has not yet ceased doing mischief): perhaps some
289
- play upon words, a deception on the part of grammar, or an
290
- audacious generalization of very restricted, very personal, very
291
- human--all-too-human facts. The philosophy of the dogmatists, it is to
292
- be hoped, was only a promise for thousands of years afterwards, as was
293
- astrology in still earlier times, in the service of which probably more
294
- labour, gold, acuteness, and patience have been spent than on any
295
- actual science hitherto: we owe to it, and to its "super-terrestrial"
296
- pretensions in Asia and Egypt, the grand style of architecture. It seems
297
- that in order to inscribe themselves upon the heart of humanity with
298
- everlasting claims, all great things have first to wander about the
299
- earth as enormous and awe-inspiring caricatures: dogmatic philosophy has
300
- been a caricature of this kind--for instance, the Vedanta doctrine in
301
- Asia, and Platonism in Europe. Let us not be ungrateful to it, although
302
- it must certainly be confessed that the worst, the most tiresome,
303
- and the most dangerous of errors hitherto has been a dogmatist
304
- error--namely, Plato's invention of Pure Spirit and the Good in Itself.
305
- But now when it has been surmounted, when Europe, rid of this nightmare,
306
- can again draw breath freely and at least enjoy a healthier--sleep,
307
- we, WHOSE DUTY IS WAKEFULNESS ITSELF, are the heirs of all the strength
308
- which the struggle against this error has fostered. It amounted to
309
- the very inversion of truth, and the denial of the PERSPECTIVE--the
310
- fundamental condition--of life, to speak of Spirit and the Good as Plato
311
- spoke of them; indeed one might ask, as a physician: "How did such a
312
- malady attack that finest product of antiquity, Plato? Had the wicked
313
- Socrates really corrupted him? Was Socrates after all a corrupter of
314
- youths, and deserved his hemlock?" But the struggle against Plato,
315
- or--to speak plainer, and for the "people"--the struggle against
316
- the ecclesiastical oppression of millenniums of Christianity (FOR
317
- CHRISTIANITY IS PLATONISM FOR THE "PEOPLE"), produced in Europe
318
- a magnificent tension of soul, such as had not existed anywhere
319
- previously; with such a tensely strained bow one can now aim at the
320
- furthest goals. As a matter of fact, the European feels this tension as
321
- a state of distress, and twice attempts have been made in grand style to
322
- unbend the bow: once by means of Jesuitism, and the second time by means
323
- of democratic enlightenment--which, with the aid of liberty of the press
324
- and newspaper-reading, might, in fact, bring it about that the spirit
325
- would not so easily find itself in "distress"! (The Germans invented
326
- gunpowder--all credit to them! but they again made things square--they
327
- invented printing.) But we, who are neither Jesuits, nor democrats,
328
- nor even sufficiently Germans, we GOOD EUROPEANS, and free, VERY free
329
- spirits--we have it still, all the distress of spirit and all the
330
- tension of its bow! And perhaps also the arrow, the duty, and, who
331
- knows? THE GOAL TO AIM AT....
332
-
333
- Sils Maria Upper Engadine, JUNE, 1885.
334
-
335
- - :title: Ulysses
336
- :authors:
337
- - :last_name: Joyce
338
- :first_name: James
339
- :downloads: 14679
340
- :category: Fiction
341
- :content: |
342
- Stately, plump Buck Mulligan came from the stairhead, bearing a bowl of
343
- lather on which a mirror and a razor lay crossed. A yellow dressinggown,
344
- ungirdled, was sustained gently behind him on the mild morning air. He
345
- held the bowl aloft and intoned:
346
-
347
- --_Introibo ad altare Dei_.
348
-
349
- Halted, he peered down the dark winding stairs and called out coarsely:
350
-
351
- --Come up, Kinch! Come up, you fearful jesuit!
352
-
353
- Solemnly he came forward and mounted the round gunrest. He faced about
354
- and blessed gravely thrice the tower, the surrounding land and the
355
- awaking mountains. Then, catching sight of Stephen Dedalus, he bent
356
- towards him and made rapid crosses in the air, gurgling in his throat
357
- and shaking his head. Stephen Dedalus, displeased and sleepy, leaned
358
- his arms on the top of the staircase and looked coldly at the shaking
359
- gurgling face that blessed him, equine in its length, and at the light
360
- untonsured hair, grained and hued like pale oak.
361
-
362
- Buck Mulligan peeped an instant under the mirror and then covered the
363
- bowl smartly.
364
-
365
- --Back to barracks! he said sternly.
366
-
367
- He added in a preacher's tone:
368
-
369
- --For this, O dearly beloved, is the genuine Christine: body and soul
370
- and blood and ouns. Slow music, please. Shut your eyes, gents. One
371
- moment. A little trouble about those white corpuscles. Silence, all.
372
-
373
- He peered sideways up and gave a long slow whistle of call, then paused
374
- awhile in rapt attention, his even white teeth glistening here and there
375
- with gold points. Chrysostomos. Two strong shrill whistles answered
376
- through the calm.
377
-
378
- --Thanks, old chap, he cried briskly. That will do nicely. Switch off
379
- the current, will you?
380
-
381
- He skipped off the gunrest and looked gravely at his watcher, gathering
382
- about his legs the loose folds of his gown. The plump shadowed face and
383
- sullen oval jowl recalled a prelate, patron of arts in the middle ages.
384
- A pleasant smile broke quietly over his lips.
385
-
386
- --The mockery of it! he said gaily. Your absurd name, an ancient Greek!
387
-
388
- He pointed his finger in friendly jest and went over to the parapet,
389
- laughing to himself. Stephen Dedalus stepped up, followed him wearily
390
- halfway and sat down on the edge of the gunrest, watching him still as
391
- he propped his mirror on the parapet, dipped the brush in the bowl and
392
- lathered cheeks and neck.
393
-
394
- Buck Mulligan's gay voice went on.
395
-
396
- --My name is absurd too: Malachi Mulligan, two dactyls. But it has a
397
- Hellenic ring, hasn't it? Tripping and sunny like the buck himself.
398
- We must go to Athens. Will you come if I can get the aunt to fork out
399
- twenty quid?
400
-
401
- He laid the brush aside and, laughing with delight, cried:
402
-
403
- --Will he come? The jejune jesuit!
404
-
405
- Ceasing, he began to shave with care.
406
-
407
- --Tell me, Mulligan, Stephen said quietly.
408
-
409
- --Yes, my love?
410
-
411
- --How long is Haines going to stay in this tower?
412
-
413
- Buck Mulligan showed a shaven cheek over his right shoulder.
414
-
415
- - :title: Metamorphosis
416
- :authors:
417
- - :last_name: Kafka
418
- :first_name: Franz
419
- :downloads: 22697
420
- :category: Fiction
421
- :content: |
422
- One morning, when Gregor Samsa woke from troubled dreams, he found
423
- himself transformed in his bed into a horrible vermin. He lay on
424
- his armour-like back, and if he lifted his head a little he could
425
- see his brown belly, slightly domed and divided by arches into stiff
426
- sections. The bedding was hardly able to cover it and seemed ready
427
- to slide off any moment. His many legs, pitifully thin compared
428
- with the size of the rest of him, waved about helplessly as he
429
- looked.
430
-
431
- "What's happened to me?" he thought. It wasn't a dream. His room,
432
- a proper human room although a little too small, lay peacefully
433
- between its four familiar walls. A collection of textile samples
434
- lay spread out on the table - Samsa was a travelling salesman - and
435
- above it there hung a picture that he had recently cut out of an
436
- illustrated magazine and housed in a nice, gilded frame. It showed
437
- a lady fitted out with a fur hat and fur boa who sat upright,
438
- raising a heavy fur muff that covered the whole of her lower arm
439
- towards the viewer.
440
-
441
- Gregor then turned to look out the window at the dull weather.
442
- Drops of rain could be heard hitting the pane, which made him feel
443
- quite sad. "How about if I sleep a little bit longer and forget all
444
- this nonsense", he thought, but that was something he was unable to
445
- do because he was used to sleeping on his right, and in his present
446
- state couldn't get into that position. However hard he threw
447
- himself onto his right, he always rolled back to where he was. He
448
- must have tried it a hundred times, shut his eyes so that he
449
- wouldn't have to look at the floundering legs, and only stopped when
450
- he began to feel a mild, dull pain there that he had never felt
451
- before.
452
-
453
- "Oh, God", he thought, "what a strenuous career it is that I've
454
- chosen! Travelling day in and day out. Doing business like this
455
- takes much more effort than doing your own business at home, and on
456
- top of that there's the curse of travelling, worries about making
457
- train connections, bad and irregular food, contact with different
458
- people all the time so that you can never get to know anyone or
459
- become friendly with them. It can all go to Hell!" He felt a
460
- slight itch up on his belly; pushed himself slowly up on his back
461
- towards the headboard so that he could lift his head better; found
462
- where the itch was, and saw that it was covered with lots of little
463
- white spots which he didn't know what to make of; and when he tried
464
- to feel the place with one of his legs he drew it quickly back
465
- because as soon as he touched it he was overcome by a cold shudder.
466
-
467
- He slid back into his former position. "Getting up early all the
468
- time", he thought, "it makes you stupid. You've got to get enough
469
- sleep. Other travelling salesmen live a life of luxury. For
470
- instance, whenever I go back to the guest house during the morning
471
- to copy out the contract, these gentlemen are always still sitting
472
- there eating their breakfasts. I ought to just try that with my
473
- boss; I'd get kicked out on the spot. But who knows, maybe that
474
- would be the best thing for me. If I didn't have my parents to
475
- think about I'd have given in my notice a long time ago, I'd have
476
- gone up to the boss and told him just what I think, tell him
477
- everything I would, let him know just what I feel. He'd fall right
478
- off his desk! And it's a funny sort of business to be sitting up
479
- there at your desk, talking down at your subordinates from up there,
480
- especially when you have to go right up close because the boss is
481
- hard of hearing. Well, there's still some hope; once I've got the
482
- money together to pay off my parents' debt to him - another five or
483
- six years I suppose - that's definitely what I'll do. That's when
484
- I'll make the big change. First of all though, I've got to get up,
485
- my train leaves at five."
486
-
487
- - :title: Crime and Punishment
488
- :authors:
489
- - :last_name: Dostoyevsky
490
- :first_name: Fyodor
491
- :downloads: 4590
492
- :category: Fiction
493
- :content: |
494
- On an exceptionally hot evening early in July a young man came out of
495
- the garret in which he lodged in S. Place and walked slowly, as though
496
- in hesitation, towards K. bridge.
497
-
498
- He had successfully avoided meeting his landlady on the staircase. His
499
- garret was under the roof of a high, five-storied house and was more
500
- like a cupboard than a room. The landlady who provided him with garret,
501
- dinners, and attendance, lived on the floor below, and every time
502
- he went out he was obliged to pass her kitchen, the door of which
503
- invariably stood open. And each time he passed, the young man had a
504
- sick, frightened feeling, which made him scowl and feel ashamed. He was
505
- hopelessly in debt to his landlady, and was afraid of meeting her.
506
-
507
- This was not because he was cowardly and abject, quite the contrary; but
508
- for some time past he had been in an overstrained irritable condition,
509
- verging on hypochondria. He had become so completely absorbed in
510
- himself, and isolated from his fellows that he dreaded meeting, not
511
- only his landlady, but anyone at all. He was crushed by poverty, but the
512
- anxieties of his position had of late ceased to weigh upon him. He had
513
- given up attending to matters of practical importance; he had lost all
514
- desire to do so. Nothing that any landlady could do had a real terror
515
- for him. But to be stopped on the stairs, to be forced to listen to her
516
- trivial, irrelevant gossip, to pestering demands for payment, threats
517
- and complaints, and to rack his brains for excuses, to prevaricate, to
518
- lie--no, rather than that, he would creep down the stairs like a cat and
519
- slip out unseen.
520
-
521
- This evening, however, on coming out into the street, he became acutely
522
- aware of his fears.
523
-
524
- "I want to attempt a thing _like that_ and am frightened by these
525
- trifles," he thought, with an odd smile. "Hm... yes, all is in a man's
526
- hands and he lets it all slip from cowardice, that's an axiom. It would
527
- be interesting to know what it is men are most afraid of. Taking a new
528
- step, uttering a new word is what they fear most.... But I am talking
529
- too much. It's because I chatter that I do nothing. Or perhaps it is
530
- that I chatter because I do nothing. I've learned to chatter this
531
- last month, lying for days together in my den thinking... of Jack the
532
- Giant-killer. Why am I going there now? Am I capable of _that_? Is
533
- _that_ serious? It is not serious at all. It's simply a fantasy to amuse
534
- myself; a plaything! Yes, maybe it is a plaything."
535
-
536
- The heat in the street was terrible: and the airlessness, the bustle
537
- and the plaster, scaffolding, bricks, and dust all about him, and that
538
- special Petersburg stench, so familiar to all who are unable to get out
539
- of town in summer--all worked painfully upon the young man's already
540
- overwrought nerves. The insufferable stench from the pot-houses, which
541
- are particularly numerous in that part of the town, and the drunken men
542
- whom he met continually, although it was a working day, completed
543
- the revolting misery of the picture. An expression of the profoundest
544
- disgust gleamed for a moment in the young man's refined face. He was,
545
- by the way, exceptionally handsome, above the average in height, slim,
546
- well-built, with beautiful dark eyes and dark brown hair. Soon he sank
547
- into deep thought, or more accurately speaking into a complete blankness
548
- of mind; he walked along not observing what was about him and not caring
549
- to observe it. From time to time, he would mutter something, from the
550
- habit of talking to himself, to which he had just confessed. At these
551
- moments he would become conscious that his ideas were sometimes in a
552
- tangle and that he was very weak; for two days he had scarcely tasted
553
- food.
554
-
555
- - :title: The Hound of the Baskervilles
556
- :authors:
557
- - :last_name: Doyle
558
- :first_name: Arthur Conan
559
- :downloads: 5021
560
- :category: Fiction
561
- :content: |
562
- Mr. Sherlock Holmes, who was usually very late in the mornings, save
563
- upon those not infrequent occasions when he was up all night, was seated
564
- at the breakfast table. I stood upon the hearth-rug and picked up the
565
- stick which our visitor had left behind him the night before. It was a
566
- fine, thick piece of wood, bulbous-headed, of the sort which is known as
567
- a "Penang lawyer." Just under the head was a broad silver band nearly
568
- an inch across. "To James Mortimer, M.R.C.S., from his friends of the
569
- C.C.H.," was engraved upon it, with the date "1884." It was just such a
570
- stick as the old-fashioned family practitioner used to carry--dignified,
571
- solid, and reassuring.
572
-
573
- "Well, Watson, what do you make of it?"
574
-
575
- Holmes was sitting with his back to me, and I had given him no sign of
576
- my occupation.
577
-
578
- "How did you know what I was doing? I believe you have eyes in the back
579
- of your head."
580
-
581
- "I have, at least, a well-polished, silver-plated coffee-pot in front of
582
- me," said he. "But, tell me, Watson, what do you make of our visitor's
583
- stick? Since we have been so unfortunate as to miss him and have no
584
- notion of his errand, this accidental souvenir becomes of importance.
585
- Let me hear you reconstruct the man by an examination of it."
586
-
587
- "I think," said I, following as far as I could the methods of my
588
- companion, "that Dr. Mortimer is a successful, elderly medical man,
589
- well-esteemed since those who know him give him this mark of their
590
- appreciation."
591
-
592
- "Good!" said Holmes. "Excellent!"
593
-
594
- "I think also that the probability is in favour of his being a country
595
- practitioner who does a great deal of his visiting on foot."
596
-
597
- "Why so?"
598
-
599
- "Because this stick, though originally a very handsome one has been so
600
- knocked about that I can hardly imagine a town practitioner carrying it.
601
- The thick-iron ferrule is worn down, so it is evident that he has done a
602
- great amount of walking with it."
603
-
604
- "Perfectly sound!" said Holmes.
605
-
606
- "And then again, there is the 'friends of the C.C.H.' I should guess
607
- that to be the Something Hunt, the local hunt to whose members he has
608
- possibly given some surgical assistance, and which has made him a small
609
- presentation in return."
610
-
611
- "Really, Watson, you excel yourself," said Holmes, pushing back his
612
- chair and lighting a cigarette. "I am bound to say that in all the
613
- accounts which you have been so good as to give of my own small
614
- achievements you have habitually underrated your own abilities. It may
615
- be that you are not yourself luminous, but you are a conductor of
616
- light. Some people without possessing genius have a remarkable power of
617
- stimulating it. I confess, my dear fellow, that I am very much in your
618
- debt."
619
-
620
- He had never said as much before, and I must admit that his words gave
621
- me keen pleasure, for I had often been piqued by his indifference to my
622
- admiration and to the attempts which I had made to give publicity to
623
- his methods. I was proud, too, to think that I had so far mastered his
624
- system as to apply it in a way which earned his approval. He now took
625
- the stick from my hands and examined it for a few minutes with his naked
626
- eyes. Then with an expression of interest he laid down his cigarette,
627
- and carrying the cane to the window, he looked over it again with a
628
- convex lens.
629
-
630
- "Interesting, though elementary," said he as he returned to his
631
- favourite corner of the settee. "There are certainly one or two
632
- indications upon the stick. It gives us the basis for several
633
- deductions."
634
-
635
- - :title: Madame Bovary
636
- :authors:
637
- - :last_name: Flaubert
638
- :first_name: Gustave
639
- :downloads: 4090
640
- :category: Fiction
641
- :content: |
642
- We were in class when the head-master came in, followed by a "new
643
- fellow," not wearing the school uniform, and a school servant carrying a
644
- large desk. Those who had been asleep woke up, and every one rose as if
645
- just surprised at his work.
646
-
647
- The head-master made a sign to us to sit down. Then, turning to the
648
- class-master, he said to him in a low voice--
649
-
650
- "Monsieur Roger, here is a pupil whom I recommend to your care; he'll be
651
- in the second. If his work and conduct are satisfactory, he will go into
652
- one of the upper classes, as becomes his age."
653
-
654
- The "new fellow," standing in the corner behind the door so that he
655
- could hardly be seen, was a country lad of about fifteen, and taller
656
- than any of us. His hair was cut square on his forehead like a village
657
- chorister's; he looked reliable, but very ill at ease. Although he was
658
- not broad-shouldered, his short school jacket of green cloth with black
659
- buttons must have been tight about the arm-holes, and showed at the
660
- opening of the cuffs red wrists accustomed to being bare. His legs, in
661
- blue stockings, looked out from beneath yellow trousers, drawn tight by
662
- braces, He wore stout, ill-cleaned, hob-nailed boots.
663
-
664
- We began repeating the lesson. He listened with all his ears, as
665
- attentive as if at a sermon, not daring even to cross his legs or lean
666
- on his elbow; and when at two o'clock the bell rang, the master was
667
- obliged to tell him to fall into line with the rest of us.
668
-
669
- When we came back to work, we were in the habit of throwing our caps on
670
- the ground so as to have our hands more free; we used from the door to
671
- toss them under the form, so that they hit against the wall and made a
672
- lot of dust: it was "the thing."
673
-
674
- But, whether he had not noticed the trick, or did not dare to attempt
675
- it, the "new fellow," was still holding his cap on his knees even after
676
- prayers were over. It was one of those head-gears of composite order, in
677
- which we can find traces of the bearskin, shako, billycock hat, sealskin
678
- cap, and cotton night-cap; one of those poor things, in fine, whose
679
- dumb ugliness has depths of expression, like an imbecile's face. Oval,
680
- stiffened with whalebone, it began with three round knobs; then came in
681
- succession lozenges of velvet and rabbit-skin separated by a red band;
682
- after that a sort of bag that ended in a cardboard polygon covered with
683
- complicated braiding, from which hung, at the end of a long thin cord,
684
- small twisted gold threads in the manner of a tassel. The cap was new;
685
- its peak shone.
686
-
687
- "Rise," said the master.
688
-
689
- He stood up; his cap fell. The whole class began to laugh. He stooped to
690
- pick it up. A neighbor knocked it down again with his elbow; he picked
691
- it up once more.
692
-
693
- - :title: Tractatus Logico-Philosophicus
694
- :authors:
695
- - :last_name: Wittgenstein
696
- :first_name: Ludwig
697
- :downloads: 4036
698
- :category: Philosophy
699
- :content: |
700
- 1 The world is everything that is the case.∗
701
- 1.1 The world is the totality of facts, not of things.
702
- 1.11 The world is determined by the facts, and by these being all the facts.
703
- 1.12 For the totality of facts determines both what is the case, and also all that is not the case.
704
- 1.13 The facts in logical space are the world.
705
- 1.2 The world divides into facts.
706
- 1.21 Any one can either be the case or not be the case, and everything else remain the same.
707
- - :title: A General Introduction to Psychoanalysis
708
- :authors:
709
- - :last_name: Freud
710
- :first_name: Sigmund
711
- :downloads: 1355
712
- :category: Psychology
713
- :content: |
714
- I do not know how familiar some of you may be, either from your reading
715
- or from hearsay, with psychoanalysis. But, in keeping with the title of
716
- these lectures--_A General Introduction to Psychoanalysis_--I am obliged
717
- to proceed as though you knew nothing about this subject, and stood in
718
- need of preliminary instruction.
719
-
720
- To be sure, this much I may presume that you do know, namely, that
721
- psychoanalysis is a method of treating nervous patients medically. And
722
- just at this point I can give you an example to illustrate how the
723
- procedure in this field is precisely the reverse of that which is the
724
- rule in medicine. Usually when we introduce a patient to a medical
725
- technique which is strange to him we minimize its difficulties and give
726
- him confident promises concerning the result of the treatment. When,
727
- however, we undertake psychoanalytic treatment with a neurotic patient
728
- we proceed differently. We hold before him the difficulties of the
729
- method, its length, the exertions and the sacrifices which it will cost
730
- him; and, as to the result, we tell him that we make no definite
731
- promises, that the result depends on his conduct, on his understanding,
732
- on his adaptability, on his perseverance. We have, of course, excellent
733
- motives for conduct which seems so perverse, and into which you will
734
- perhaps gain insight at a later point in these lectures.
735
-
736
- Do not be offended, therefore, if, for the present, I treat you as I
737
- treat these neurotic patients. Frankly, I shall dissuade you from coming
738
- to hear me a second time. With this intention I shall show what
739
- imperfections are necessarily involved in the teaching of psychoanalysis
740
- and what difficulties stand in the way of gaining a personal judgment. I
741
- shall show you how the whole trend of your previous training and all
742
- your accustomed mental habits must unavoidably have made you opponents
743
- of psychoanalysis, and how much you must overcome in yourselves in
744
- order to master this instinctive opposition. Of course I cannot predict
745
- how much psychoanalytic understanding you will gain from my lectures,
746
- but I can promise this, that by listening to them you will not learn how
747
- to undertake a psychoanalytic treatment or how to carry one to
748
- completion. Furthermore, should I find anyone among you who does not
749
- feel satisfied with a cursory acquaintance with psychoanalysis, but who
750
- would like to enter into a more enduring relationship with it, I shall
751
- not only dissuade him, but I shall actually warn him against it. As
752
- things now stand, a person would, by such a choice of profession, ruin
753
- his every chance of success at a university, and if he goes out into the
754
- world as a practicing physician, he will find himself in a society which
755
- does not understand his aims, which regards him with suspicion and
756
- hostility, and which turns loose upon him all the malicious spirits
757
- which lurk within it.
758
- - :title: Grimms' Fairy Tales
759
- :authors:
760
- - :last_name: Grimm
761
- :first_name: Jacob
762
- - :last_name: Grimm
763
- :first_name: Wilhelm
764
- :downloads: 25050
765
- :content: |
766
- A certain king had a beautiful garden, and in the garden stood a tree
767
- which bore golden apples. These apples were always counted, and about
768
- the time when they began to grow ripe it was found that every night one
769
- of them was gone. The king became very angry at this, and ordered the
770
- gardener to keep watch all night under the tree. The gardener set his
771
- eldest son to watch; but about twelve o'clock he fell asleep, and in
772
- the morning another of the apples was missing. Then the second son was
773
- ordered to watch; and at midnight he too fell asleep, and in the morning
774
- another apple was gone. Then the third son offered to keep watch; but
775
- the gardener at first would not let him, for fear some harm should come
776
- to him: however, at last he consented, and the young man laid himself
777
- under the tree to watch. As the clock struck twelve he heard a rustling
778
- noise in the air, and a bird came flying that was of pure gold; and as
779
- it was snapping at one of the apples with its beak, the gardener's son
780
- jumped up and shot an arrow at it. But the arrow did the bird no harm;
781
- only it dropped a golden feather from its tail, and then flew away.
782
- The golden feather was brought to the king in the morning, and all the
783
- council was called together. Everyone agreed that it was worth more than
784
- all the wealth of the kingdom: but the king said, 'One feather is of no
785
- use to me, I must have the whole bird.'
786
-
787
- - :title: An English Grammar
788
- :authors:
789
- - :last_name: Baskervill
790
- :first_name: William Malone
791
- - :last_name: Sewell
792
- :first_name: James Witt
793
- :downloads: 1211
794
- :category: Linguistics
795
- :content: |
796
- Of making many English grammars there is no end; nor should there be
797
- till theoretical scholarship and actual practice are more happily
798
- wedded. In this field much valuable work has already been
799
- accomplished; but it has been done largely by workers accustomed to
800
- take the scholar's point of view, and their writings are addressed
801
- rather to trained minds than to immature learners. To find an advanced
802
- grammar unencumbered with hard words, abstruse thoughts, and difficult
803
- principles, is not altogether an easy matter. These things enhance the
804
- difficulty which an ordinary youth experiences in grasping and
805
- assimilating the facts of grammar, and create a distaste for the
806
- study. It is therefore the leading object of this book to be both as
807
- scholarly and as practical as possible. In it there is an attempt to
808
- present grammatical facts as simply, and to lead the student to
809
- assimilate them as thoroughly, as possible, and at the same time to do
810
- away with confusing difficulties as far as may be.
811
- DATA
812
-
813
- [Book, Author, Authorship, Category].each { |model| model.delete_all }
814
-
815
- books.to_ruby.each do |b|
816
- book = Book.create \
817
- title: b[:title],
818
- downloads: b[:downloads],
819
- content: b[:content]
820
-
821
- b[:authors].each do |a|
822
- author = Author.where(first_name: a[:first_name], last_name: a[:last_name]).first_or_create
823
- book.authors << author
824
- end
134
+ puts
135
+ say_status "Model", "Adding the `abstract` column to Article...\n", :yellow
136
+ puts '-'*80, ''
137
+
138
+ generate :migration, "AddColumnsToArticle abstract:text url:string shares:integer"
139
+ rake "db:migrate"
825
140
 
826
- category = Category.where(title: b[:category]).first_or_create
827
- book.category = category
141
+ git add: "db/"
142
+ git commit: "-m 'Added additional columns to Article'"
143
+
144
+ # ----- Move the model integration into a concern -------------------------------------------------
828
145
 
829
- book.save
146
+ puts
147
+ say_status "Model", "Refactoring the model integration...\n", :yellow
148
+ puts '-'*80, ''; sleep 0.25
149
+
150
+ remove_file 'app/models/article.rb'
151
+ create_file 'app/models/article.rb', <<-CODE
152
+ class Article < ActiveRecord::Base
153
+ include Searchable
830
154
  end
831
155
  CODE
832
156
 
833
- git :add => '.'
834
- git :commit => "-m 'Added data model and seed script (books, categories, authors)'"
157
+ # copy_file File.expand_path('../searchable.rb', __FILE__), 'app/models/concerns/searchable.rb'
158
+ get 'https://raw.github.com/elasticsearch/elasticsearch-rails/templates/elasticsearch-rails/lib/rails/templates/searchable.rb',
159
+ 'app/models/concerns/searchable.rb'
835
160
 
836
- # === TODO: ===
837
- #
838
- # * Update views (show authors, category name, bootstrap)
839
- # <table class="table table-hover">
840
- # class: 'btn btn-default btn-xs'
841
- # class: 'btn btn-primary btn-xs', style: 'color: #fff'
842
- # <td><%= book.authors.map(&:full_name).to_sentence %></td>
843
- # <td><%= book.category.try(:title) || 'n/a' %></td>
844
- # Update controller (fight n+1)
845
- # @books = Book.includes(:authors, :category)
846
- #
161
+ insert_into_file "app/models/article.rb", after: "ActiveRecord::Base" do
162
+ <<-CODE
847
163
 
848
- # ----- Add search support into Book model ---------------------------------------------------------
164
+ has_and_belongs_to_many :categories, after_add: [ lambda { |a,c| Indexer.perform_async(:update, a.class.to_s, a.id) } ],
165
+ after_remove: [ lambda { |a,c| Indexer.perform_async(:update, a.class.to_s, a.id) } ]
166
+ has_many :authorships
167
+ has_many :authors, through: :authorships
168
+ has_many :comments
849
169
 
850
- insert_into_file "app/models/book.rb", :before => "end" do
851
- <<-CODE
170
+ CODE
171
+ end
172
+
173
+ git add: "app/models/"
174
+ git commit: "-m 'Refactored the Elasticsearch integration into a concern\n\nSee:\n\n* http://37signals.com/svn/posts/3372-put-chubby-models-on-a-diet-with-concerns\n* http://joshsymonds.com/blog/2012/10/25/rails-concerns-v-searchable-with-elasticsearch/'"
175
+
176
+ # ----- Add Sidekiq indexer -----------------------------------------------------------------------
177
+
178
+ puts
179
+ say_status "Application", "Adding Sidekiq worker for updating the index...\n", :yellow
180
+ puts '-'*80, ''; sleep 0.25
181
+
182
+ gem "sidekiq"
183
+
184
+ run "bundle install"
185
+
186
+ # copy_file File.expand_path('../indexer.rb', __FILE__), 'app/workers/indexer.rb'
187
+ get 'https://raw.github.com/elasticsearch/elasticsearch-rails/templates/elasticsearch-rails/lib/rails/templates/indexer.rb',
188
+ 'app/workers/indexer.rb'
189
+
190
+ git add: "Gemfile* app/workers/"
191
+ git commit: "-m 'Added a Sidekiq indexer\n\nRun:\n\n $ bundle exec sidekiq --queue elasticsearch --verbose\n\nSee http://sidekiq.org'"
192
+
193
+ # ----- Add SearchController -----------------------------------------------------------------------
194
+
195
+ puts
196
+ say_status "Controllers", "Adding SearchController...\n", :yellow
197
+ puts '-'*80, ''; sleep 0.25
198
+
199
+ create_file 'app/controllers/search_controller.rb' do
200
+ <<-CODE.gsub(/^ /, '')
201
+ class SearchController < ApplicationController
202
+ respond_to :json, :html
203
+
204
+ def index
205
+ options = {
206
+ category: params[:c],
207
+ author: params[:a],
208
+ published_week: params[:w],
209
+ published_day: params[:d],
210
+ sort: params[:s],
211
+ comments: params[:comments]
212
+ }
213
+ @articles = Article.search(params[:q], options).page(params[:page]).results
214
+
215
+ respond_with @articles
216
+ end
217
+
218
+ end
852
219
 
853
- include Searchable
854
220
  CODE
855
221
  end
856
222
 
857
- git :add => 'app/models/book.rb'
858
- git :commit => "-m 'Added search support into the Book model'"
223
+ route "get '/search', to: 'search#index', as: 'search'"
224
+ gsub_file 'config/routes.rb', %r{root to: 'articles#index'$}, "root to: 'search#index'"
859
225
 
860
- # === TODO: ===
861
- #
862
- # * Create search action or controller
863
- # @books = Book.search(params[:q]).records.includes(:authors, :category)
864
- # * Create view
226
+ # copy_file File.expand_path('../index.html.erb', __FILE__), 'app/views/search/index.html.erb'
227
+ get 'https://raw.github.com/elasticsearch/elasticsearch-rails/templates/elasticsearch-rails/lib/rails/templates/index.html.erb',
228
+ 'app/views/search/index.html.erb'
229
+
230
+ # copy_file File.expand_path('../search.css', __FILE__), 'app/assets/stylesheets/search.css'
231
+ get 'https://raw.github.com/elasticsearch/elasticsearch-rails/templates/elasticsearch-rails/lib/rails/templates/search.css',
232
+ 'app/assets/stylesheets/search.css'
233
+
234
+ git add: "app/controllers/ config/routes.rb"
235
+ git add: "app/views/search/ app/assets/stylesheets/search.css"
236
+ git commit: "-m 'Added SearchController#index'"
237
+
238
+ # ----- Add initializer ---------------------------------------------------------------------------
239
+
240
+ puts
241
+ say_status "Application", "Adding Elasticsearch configuration in an initializer...\n", :yellow
242
+ puts '-'*80, ''; sleep 0.5
243
+
244
+ create_file 'config/initializers/elasticsearch.rb', <<-CODE
245
+ # Connect to specific Elasticsearch cluster
246
+ ELASTICSEARCH_URL = ENV['ELASTICSEARCH_URL'] || 'http://localhost:9200'
247
+
248
+ Elasticsearch::Model.client = Elasticsearch::Client.new host: ELASTICSEARCH_URL
249
+
250
+ # Print Curl-formatted traces in development into a file
865
251
  #
252
+ if Rails.env.development?
253
+ tracer = ActiveSupport::Logger.new('log/elasticsearch.log')
254
+ tracer.level = Logger::DEBUG
255
+ end
866
256
 
867
- # ----- Insert seed data into the database ---------------------------------------------------------
257
+ Elasticsearch::Model.client.transport.tracer = tracer
258
+ CODE
259
+
260
+ git add: "config/initializers"
261
+ git commit: "-m 'Added Rails initializer with Elasticsearch configuration'"
262
+
263
+ # ----- Add Rake tasks ----------------------------------------------------------------------------
868
264
 
869
265
  puts
870
- say_status "Database", "Seeding the database with data...", :yellow
871
- puts '-'*80, ''; sleep 0.25
266
+ say_status "Application", "Adding Elasticsearch Rake tasks...\n", :yellow
267
+ puts '-'*80, ''; sleep 0.5
268
+
269
+ create_file 'lib/tasks/elasticsearch.rake', <<-CODE
270
+ require 'elasticsearch/rails/tasks/import'
271
+ CODE
872
272
 
873
- rake "db:seed"
273
+ git add: "lib/tasks"
274
+ git commit: "-m 'Added Rake tasks for Elasticsearch'"
874
275
 
875
- # ----- Import data into Elasticsearch ------------------------------------------------------------
276
+ # ----- Insert and index data ---------------------------------------------------------------------
876
277
 
877
278
  puts
878
- say_status "Index", "Indexing the database...", :yellow
279
+ say_status "Database", "Re-creating the database with data and importing into Elasticsearch...", :yellow
879
280
  puts '-'*80, ''; sleep 0.25
880
281
 
881
- # rake "environment elasticsearch:import:model CLASS='Article' FORCE=true"
882
- run "rails runner 'Book.__elasticsearch__.client.indices.delete index: Book.__elasticsearch__.index_name rescue nil; Book.__elasticsearch__.client.indices.create index: Book.__elasticsearch__.index_name; Book.__elasticsearch__.import'"
282
+ # copy_file File.expand_path('../articles.yml.gz', __FILE__), 'db/articles.yml.gz'
283
+ get 'https://raw.github.com/elasticsearch/elasticsearch-rails/templates/elasticsearch-rails/lib/rails/templates/articles.yml.gz',
284
+ 'db/articles.yml.gz'
285
+
286
+ remove_file 'db/seeds.rb'
287
+ # copy_file File.expand_path('../seeds.rb', __FILE__), 'db/seeds.rb'
288
+ get 'https://raw.github.com/elasticsearch/elasticsearch-rails/templates/elasticsearch-rails/lib/rails/templates/seeds.rb',
289
+ 'db/seeds.rb'
290
+
291
+ rake "db:reset"
292
+ rake "environment elasticsearch:import:model CLASS='Article' BATCH=100 FORCE=y"
293
+
294
+ git add: "db/seeds.rb db/articles.yml.gz"
295
+ git commit: "-m 'Added a seed script and source data'"
883
296
 
884
297
  # ----- Print Git log -----------------------------------------------------------------------------
885
298
 
@@ -887,24 +300,26 @@ puts
887
300
  say_status "Git", "Details about the application:", :yellow
888
301
  puts '-'*80, ''
889
302
 
890
- git :tag => "complex"
891
- git :log => "--reverse --oneline HEAD...pretty"
303
+ git tag: "expert"
304
+ git log: "--reverse --oneline HEAD...pretty"
892
305
 
893
306
  # ----- Start the application ---------------------------------------------------------------------
894
307
 
895
- require 'net/http'
896
- if (begin; Net::HTTP.get(URI('http://localhost:3000')); rescue Errno::ECONNREFUSED; false; rescue Exception; true; end)
897
- puts "\n"
898
- say_status "ERROR", "Some other application is running on port 3000!\n", :red
899
- puts '-'*80
308
+ unless ENV['RAILS_NO_SERVER_START']
309
+ require 'net/http'
310
+ if (begin; Net::HTTP.get(URI('http://localhost:3000')); rescue Errno::ECONNREFUSED; false; rescue Exception; true; end)
311
+ puts "\n"
312
+ say_status "ERROR", "Some other application is running on port 3000!\n", :red
313
+ puts '-'*80
900
314
 
901
- port = ask("Please provide free port:", :bold)
902
- else
903
- port = '3000'
904
- end
315
+ port = ask("Please provide free port:", :bold)
316
+ else
317
+ port = '3000'
318
+ end
905
319
 
906
- puts "", "="*80
907
- say_status "DONE", "\e[1mStarting the application. Open http://localhost:#{port}\e[0m", :yellow
908
- puts "="*80, ""
320
+ puts "", "="*80
321
+ say_status "DONE", "\e[1mStarting the application. Open http://localhost:#{port}\e[0m", :yellow
322
+ puts "="*80, ""
909
323
 
910
- run "rails server --port=#{port}"
324
+ run "rails server --port=#{port}"
325
+ end