cackle 0.0.2
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +7 -0
- data/MIT-LICENSE +20 -0
- data/README.md +67 -0
- data/Rakefile +32 -0
- data/lib/app/helpers/cackle_helper.rb +18 -0
- data/lib/app/models/cackle_comment.rb +5 -0
- data/lib/app/views/cackle/comments.html.haml +27 -0
- data/lib/app/views/cackle/recent.html.haml +62 -0
- data/lib/cackle.rb +13 -0
- data/lib/cackle/railtie.rb +7 -0
- data/lib/cackle/version.rb +3 -0
- data/lib/generators/cackle_migration/cackle_migration_generator.rb +19 -0
- data/lib/generators/cackle_migration/templates/migration.rb +22 -0
- data/lib/tasks/cackle_tasks.rake +90 -0
- data/test/cackle_test.rb +7 -0
- data/test/dummy/README.rdoc +28 -0
- data/test/dummy/Rakefile +6 -0
- data/test/dummy/app/assets/javascripts/application.js +13 -0
- data/test/dummy/app/assets/stylesheets/application.css +13 -0
- data/test/dummy/app/controllers/application_controller.rb +5 -0
- data/test/dummy/app/helpers/application_helper.rb +2 -0
- data/test/dummy/app/views/layouts/application.html.erb +14 -0
- data/test/dummy/bin/bundle +3 -0
- data/test/dummy/bin/rails +4 -0
- data/test/dummy/bin/rake +4 -0
- data/test/dummy/config.ru +4 -0
- data/test/dummy/config/application.rb +23 -0
- data/test/dummy/config/boot.rb +5 -0
- data/test/dummy/config/database.yml +25 -0
- data/test/dummy/config/environment.rb +5 -0
- data/test/dummy/config/environments/development.rb +29 -0
- data/test/dummy/config/environments/production.rb +80 -0
- data/test/dummy/config/environments/test.rb +36 -0
- data/test/dummy/config/initializers/backtrace_silencers.rb +7 -0
- data/test/dummy/config/initializers/filter_parameter_logging.rb +4 -0
- data/test/dummy/config/initializers/inflections.rb +16 -0
- data/test/dummy/config/initializers/mime_types.rb +5 -0
- data/test/dummy/config/initializers/secret_token.rb +12 -0
- data/test/dummy/config/initializers/session_store.rb +3 -0
- data/test/dummy/config/initializers/wrap_parameters.rb +14 -0
- data/test/dummy/config/locales/en.yml +23 -0
- data/test/dummy/config/routes.rb +56 -0
- data/test/dummy/db/test.sqlite3 +0 -0
- data/test/dummy/log/test.log +5 -0
- data/test/dummy/public/404.html +58 -0
- data/test/dummy/public/422.html +58 -0
- data/test/dummy/public/500.html +57 -0
- data/test/dummy/public/favicon.ico +0 -0
- data/test/test_helper.rb +15 -0
- metadata +155 -0
checksums.yaml
ADDED
@@ -0,0 +1,7 @@
|
|
1
|
+
---
|
2
|
+
SHA1:
|
3
|
+
metadata.gz: 795976ed8d1dad277d90976a5b9c3b50043b847b
|
4
|
+
data.tar.gz: 73aeafd4731b674daa22a5645b2251c0cbbadfca
|
5
|
+
SHA512:
|
6
|
+
metadata.gz: 8f1e8f3470be58a6abc29fc528c6aa767357c3846e4a520d7e417e60f76fc2ffb1c2c201ee950bab56a11fa917edf27864620427d9632b7f372dd4ed6d36381d
|
7
|
+
data.tar.gz: 66fe2a831c2b9f0340f736a3428c77b0e05d4c9e8674cad1174fe80d367a5b6055ccfc7cd435fe1f4267826c18109ca3a097eb0819e206491b2e641ee74e69a1
|
data/MIT-LICENSE
ADDED
@@ -0,0 +1,20 @@
|
|
1
|
+
Copyright 2014 YOURNAME
|
2
|
+
|
3
|
+
Permission is hereby granted, free of charge, to any person obtaining
|
4
|
+
a copy of this software and associated documentation files (the
|
5
|
+
"Software"), to deal in the Software without restriction, including
|
6
|
+
without limitation the rights to use, copy, modify, merge, publish,
|
7
|
+
distribute, sublicense, and/or sell copies of the Software, and to
|
8
|
+
permit persons to whom the Software is furnished to do so, subject to
|
9
|
+
the following conditions:
|
10
|
+
|
11
|
+
The above copyright notice and this permission notice shall be
|
12
|
+
included in all copies or substantial portions of the Software.
|
13
|
+
|
14
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
|
15
|
+
EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
|
16
|
+
MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
|
17
|
+
NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
|
18
|
+
LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
|
19
|
+
OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
|
20
|
+
WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
data/README.md
ADDED
@@ -0,0 +1,67 @@
|
|
1
|
+
Cackle.me integration
|
2
|
+
=====================
|
3
|
+
|
4
|
+
Simple Rails plugin implements synchronization of Cackle.me comments.
|
5
|
+
|
6
|
+
Cackle.me is a real-time comment system with the ability to login
|
7
|
+
via popular social networks such as Google+, Facebook, Twitter,
|
8
|
+
LinkedIn, Vkontakte, Odnoklassniki, Mail.ru and many others.
|
9
|
+
|
10
|
+
Storing in local database make comments visible to search robots.
|
11
|
+
|
12
|
+
Installation
|
13
|
+
------------
|
14
|
+
|
15
|
+
Add to Gemfile
|
16
|
+
```ruby
|
17
|
+
gem 'cackle'
|
18
|
+
```
|
19
|
+
|
20
|
+
Generate and run migration
|
21
|
+
```
|
22
|
+
rails g cacke_migration
|
23
|
+
rake db:migrate
|
24
|
+
```
|
25
|
+
Create config file config/cackle.yml
|
26
|
+
See http://cackle.me/help/comment-sync to get your keys
|
27
|
+
|
28
|
+
```yml
|
29
|
+
site_id: 12345 # Site ID
|
30
|
+
site_api_key: blah-blah-blah # Account API Key
|
31
|
+
account_api_key: blah-blah-blah # Site API Key
|
32
|
+
jquery_off: true # Disable including jQuery if you already have it loaded
|
33
|
+
|
34
|
+
recent: # configuretion for recent comments block
|
35
|
+
mcSize: 10 # how many comments to display
|
36
|
+
mcAvatarSize: 32 # size of avatar image in pixels
|
37
|
+
mcTextSize: 150 # length of message block if more shorten with '...'
|
38
|
+
mcTitleSize: 40 # title length
|
39
|
+
```
|
40
|
+
|
41
|
+
Helper methods
|
42
|
+
==============
|
43
|
+
|
44
|
+
Insert comments widget
|
45
|
+
```ruby
|
46
|
+
cackle_comments
|
47
|
+
```
|
48
|
+
Recent comments widget
|
49
|
+
```ruby
|
50
|
+
cackle_recent_comments
|
51
|
+
```
|
52
|
+
|
53
|
+
Rake tasks
|
54
|
+
==========
|
55
|
+
First time import. Warning clears all comments in base.
|
56
|
+
```
|
57
|
+
cackle:import
|
58
|
+
```
|
59
|
+
|
60
|
+
Syncronize changes. Should be called every 5 minutes by cron.
|
61
|
+
```
|
62
|
+
cackle:sync
|
63
|
+
```
|
64
|
+
|
65
|
+
License
|
66
|
+
-------
|
67
|
+
This project rocks and uses MIT-LICENSE.
|
data/Rakefile
ADDED
@@ -0,0 +1,32 @@
|
|
1
|
+
begin
|
2
|
+
require 'bundler/setup'
|
3
|
+
rescue LoadError
|
4
|
+
puts 'You must `gem install bundler` and `bundle install` to run rake tasks'
|
5
|
+
end
|
6
|
+
|
7
|
+
require 'rdoc/task'
|
8
|
+
|
9
|
+
RDoc::Task.new(:rdoc) do |rdoc|
|
10
|
+
rdoc.rdoc_dir = 'rdoc'
|
11
|
+
rdoc.title = 'Cackle'
|
12
|
+
rdoc.options << '--line-numbers'
|
13
|
+
rdoc.rdoc_files.include('README.rdoc')
|
14
|
+
rdoc.rdoc_files.include('lib/**/*.rb')
|
15
|
+
end
|
16
|
+
|
17
|
+
|
18
|
+
|
19
|
+
|
20
|
+
Bundler::GemHelper.install_tasks
|
21
|
+
|
22
|
+
require 'rake/testtask'
|
23
|
+
|
24
|
+
Rake::TestTask.new(:test) do |t|
|
25
|
+
t.libs << 'lib'
|
26
|
+
t.libs << 'test'
|
27
|
+
t.pattern = 'test/**/*_test.rb'
|
28
|
+
t.verbose = false
|
29
|
+
end
|
30
|
+
|
31
|
+
|
32
|
+
task default: :test
|
@@ -0,0 +1,18 @@
|
|
1
|
+
module CackleHelper
|
2
|
+
def cackle_comments
|
3
|
+
load_config
|
4
|
+
@comments = CackleComment.approved.where("url LIKE ?", "%#{request.path}")
|
5
|
+
render template: 'cackle/comments'
|
6
|
+
end
|
7
|
+
|
8
|
+
def cackle_recent_comments
|
9
|
+
load_config
|
10
|
+
@comments = CackleComment.approved.limit(@config['recent']['mcSize'])
|
11
|
+
render template: 'cackle/recent'
|
12
|
+
end
|
13
|
+
|
14
|
+
private
|
15
|
+
def load_config
|
16
|
+
@config ||= YAML::load(File.open('config/cackle.yml'))
|
17
|
+
end
|
18
|
+
end
|
@@ -0,0 +1,27 @@
|
|
1
|
+
#mc-container
|
2
|
+
#mc-content
|
3
|
+
%ul#cackle-comments
|
4
|
+
- @comments.each do |comment|
|
5
|
+
%li{ id: "cackle-comment-#{comment.comment_id}" }
|
6
|
+
#cackle-comment-header.cackle-comment-header
|
7
|
+
%cite{ id: "cackle-cite-#{comment.comment_id}" }
|
8
|
+
-if comment.author_name
|
9
|
+
= link_to comment.author_name, '#', target: '_blank', id: comment.comment_id, rel: 'nofollow'
|
10
|
+
-else
|
11
|
+
%span{ id: "cackle-author-user-#{comment.comment_id}" }= comment.author_name
|
12
|
+
.cackle-comment-body{ id: "cackle-comment-body-#{comment.comment_id}" }
|
13
|
+
.cackle-comment-message{ id: "cackle-comment-message-#{comment.comment_id}" }= comment.message
|
14
|
+
|
15
|
+
:javascript
|
16
|
+
var href = window.location.href;
|
17
|
+
if (href.indexOf('?') > 0) {
|
18
|
+
var mcChannel = href.substring(0, href.indexOf('?'));
|
19
|
+
};
|
20
|
+
var mcSite = '#{@config['site_id']}';
|
21
|
+
(function() {
|
22
|
+
var mc = document.createElement('script');
|
23
|
+
mc.type = 'text/javascript';
|
24
|
+
mc.async = true;
|
25
|
+
mc.src = 'http://cackle.me/mc.widget-min.js';
|
26
|
+
(document.getElementsByTagName('head')[0] || document.getElementsByTagName('body')[0]).appendChild(mc);
|
27
|
+
})();
|
@@ -0,0 +1,62 @@
|
|
1
|
+
:css
|
2
|
+
.mc-last-content{position:relative!important;width:100%!important;display:inline-block!important}
|
3
|
+
.mc-last-content div,.mc-last-content p,.mc-last-content h3{display:block!important}
|
4
|
+
.mc-last-content ul,.mc-last-content li,.mc-last-content ol,.mc-last-content div,.mc-last-content p,.mc-last-content a,.mc-last-content img{background:none repeat scroll 0 50% transparent!important;border:0 none!important;font-style:normal!important;margin:0!important;padding:0!important;text-align:left!important;text-indent:0!important}
|
5
|
+
.mc-last-content ul,.mc-last-content li{border:none!important;list-style:none!important;list-style-position:outside!important;list-style-type:none!important;line-height:normal!important}
|
6
|
+
.mc-last-content li>div{margin-bottom:5px!important;position:relative!important;word-wrap:break-word!important}
|
7
|
+
.mc-last-content li .mc-recent-thumb{position:relative!important;margin-right:6px!important;padding:0!important;float:left!important;width:auto!important}
|
8
|
+
.mc-last-content li .mc-recent-thumb:after{border-radius:2px!important;box-shadow:0 0 3px rgba(0,0,0,0.33) inset!important;position:absolute!important;left:0!important;top:0!important;content:" "!important;width:100%!important;height:100%!important}
|
9
|
+
.mc-last-content .mc-recent-thumb img{border-radius:2px!important;max-width:100%!important;height:auto!important;float:left!important;vertical-align:middle!important;border:0!important}
|
10
|
+
.mc-last-content .mc-recent-body{margin-left:38px!important;line-height:15px!important}
|
11
|
+
.mc-last-content .mc-recent-author{color:#8a8a8a!important;font-size:13px!important;font-weight:bold!important;line-height:14px!important;text-decoration:none!important;padding:0!important}
|
12
|
+
.mc-last-content .mc-recent-text{font-size:12px!important;line-height:16px!important;margin-top:2px!important}
|
13
|
+
.mc-last-content .mc-recent-text a,.mc-last-content .mc-recent-text a:hover{color:inherit!important;text-decoration:none!important}
|
14
|
+
.mc-last-content .mc-recent-meta{border-bottom:1px dotted #e0e0e0!important;font-size:11px!important;line-height:15px!important;margin-top:4px!important;color:#a7a7a7!important}
|
15
|
+
.mc-last-content .mc-recent-meta a{text-decoration:none!important;color:#a7a7a7!important;font-size:11px!important}
|
16
|
+
#mc-link{color:#555!important;font-size:11px!important;clear:both!important;float:right;text-decoration:none!important}
|
17
|
+
#mc-link:hover{color:#333!important}
|
18
|
+
.mc-recent-thumb img{
|
19
|
+
width: #{@config['recent']['mcAvatarSize']}px;
|
20
|
+
height: #{@config['recent']['mcAvatarSize']}px;
|
21
|
+
}
|
22
|
+
#mc-last
|
23
|
+
.mc-last-content
|
24
|
+
%ul
|
25
|
+
- @comments.each do |comment|
|
26
|
+
%li
|
27
|
+
%div
|
28
|
+
- if comment.author_name
|
29
|
+
= link_to comment.author_www, target: '_blank' do
|
30
|
+
%span.mc-recent-thumb= image_tag(comment.author_avatar, alt: comment.author_name)
|
31
|
+
- else
|
32
|
+
%span.mc-recent-thumb= image_tag('http://cackle.me/static/img/anonym.png', alt: comment.anonym_name)
|
33
|
+
.mc-recent-body
|
34
|
+
%div
|
35
|
+
- if comment.author_name
|
36
|
+
= link_to comment.author_www, target: '_blank' do
|
37
|
+
%span.mc-recent-author= comment.author_name
|
38
|
+
- else
|
39
|
+
%span.mc-recent-author= comment.anonym_name
|
40
|
+
%p.mc-recent-text= link_to comment.message.truncate(@config['recent']['mcTextSize']), comment.url + "#mc-#{comment.id}"
|
41
|
+
%p.mc-recent-meta
|
42
|
+
= link_to comment.title.truncate(@config['recent']['mcTitleSize']), comment.url, title: comment.title
|
43
|
+
- unless 1.month.ago > (date = comment.created_at)
|
44
|
+
· #{time_ago_in_words date} назад
|
45
|
+
- else
|
46
|
+
· #{date.strftime("%d.%m")}
|
47
|
+
= date.strftime("%H:%M")
|
48
|
+
|
49
|
+
:javascript
|
50
|
+
var mcSite = '#{@config['site_id']}';
|
51
|
+
var mcSize = '#{@config['recent']['mcSize']}';
|
52
|
+
var mcAvatarSize = '#{@config['recent']['mcAvatarSize']}';;
|
53
|
+
var mcTextSize = '#{@config['recent']['mcTextSize']}';
|
54
|
+
var mcTitleSize = '#{@config['recent']['mcTitleSize']}';
|
55
|
+
var mcJqueryOff = #{@config['recent']['mcJqueryOff']};
|
56
|
+
(function() {
|
57
|
+
var mc = document.createElement('script');
|
58
|
+
mc.type = 'text/javascript';
|
59
|
+
mc.async = true;
|
60
|
+
mc.src = 'http://cackle.me/mc.last-min.js';
|
61
|
+
(document.getElementsByTagName('head')[0] || document.getElementsByTagName('body')[0]).appendChild(mc);
|
62
|
+
})();
|
data/lib/cackle.rb
ADDED
@@ -0,0 +1,13 @@
|
|
1
|
+
module Cackle
|
2
|
+
require "cackle/railtie" if defined?(Rails)
|
3
|
+
require 'app/helpers/cackle_helper'
|
4
|
+
|
5
|
+
ActionView::Base.send :include, CackleHelper
|
6
|
+
|
7
|
+
path = File.join(File.dirname(__FILE__), 'app', 'models')
|
8
|
+
$LOAD_PATH << path
|
9
|
+
ActiveSupport::Dependencies.autoload_paths << path
|
10
|
+
ActiveSupport::Dependencies.autoload_once_paths.delete(path)
|
11
|
+
|
12
|
+
ActionController::Base.prepend_view_path File.join(File.dirname(__FILE__), 'app/views')
|
13
|
+
end
|
@@ -0,0 +1,19 @@
|
|
1
|
+
class CackleMigrationGenerator < Rails::Generators::Base
|
2
|
+
require 'rails/generators'
|
3
|
+
require 'rails/generators/migration'
|
4
|
+
|
5
|
+
include Rails::Generators::Migration
|
6
|
+
source_root File.expand_path('../templates', __FILE__)
|
7
|
+
|
8
|
+
def self.next_migration_number(dirname)
|
9
|
+
if ActiveRecord::Base.timestamped_migrations
|
10
|
+
Time.now.utc.strftime("%Y%m%d%H%M%S")
|
11
|
+
else
|
12
|
+
"%.3d" % (current_migration_number(dirname) + 1)
|
13
|
+
end
|
14
|
+
end
|
15
|
+
|
16
|
+
def create_migration_file
|
17
|
+
migration_template 'migration.rb', 'db/migrate/create_cackle_comments.rb'
|
18
|
+
end
|
19
|
+
end
|
@@ -0,0 +1,22 @@
|
|
1
|
+
class CreateCackleComments < ActiveRecord::Migration
|
2
|
+
def change
|
3
|
+
create_table :cackle_comments, id: false do |t|
|
4
|
+
t.integer :comment_id
|
5
|
+
t.integer :parent_id
|
6
|
+
t.string :post_id
|
7
|
+
t.string :url
|
8
|
+
t.string :title
|
9
|
+
t.text :message
|
10
|
+
t.string :status
|
11
|
+
t.string :ip
|
12
|
+
t.string :author_name
|
13
|
+
t.string :author_email
|
14
|
+
t.string :author_avatar
|
15
|
+
t.string :author_www
|
16
|
+
t.string :author_provider
|
17
|
+
t.string :anonym_name
|
18
|
+
t.string :anonym_email
|
19
|
+
t.timestamps
|
20
|
+
end
|
21
|
+
end
|
22
|
+
end
|
@@ -0,0 +1,90 @@
|
|
1
|
+
require 'open-uri'
|
2
|
+
|
3
|
+
namespace :cackle do
|
4
|
+
def load_config
|
5
|
+
@config = YAML::load(File.open('config/cackle.yml'))
|
6
|
+
@path = "http://cackle.me/api/comment/mutable_list?siteApiKey=#{@config['site_api_key']}&accountApiKey=#{@config['account_api_key']}"
|
7
|
+
raise "Create config/cackle.yml first" unless @config
|
8
|
+
|
9
|
+
puts "will use: "
|
10
|
+
puts @config['site_id']
|
11
|
+
puts @config['site_api_key']
|
12
|
+
puts @config['account_api_key']
|
13
|
+
end
|
14
|
+
|
15
|
+
def create_comment c
|
16
|
+
comment = CackleComment.new(
|
17
|
+
comment_id: c['id'],
|
18
|
+
parent_id: c['parentId'],
|
19
|
+
post_id: c['channel'],
|
20
|
+
url: c['url'],
|
21
|
+
title: c['title'],
|
22
|
+
message: c['message'],
|
23
|
+
status: c['status'],
|
24
|
+
ip: c['ip'],
|
25
|
+
created_at: Time.strptime((c['created']/1000).to_s, "%s"),
|
26
|
+
updated_at: Time.strptime(c['modified'][0..9], "%s"))
|
27
|
+
if c['author']
|
28
|
+
comment.author_name = c['author']['name']
|
29
|
+
comment.author_email = c['author']['email']
|
30
|
+
comment.author_www = c['author']['www']
|
31
|
+
comment.author_avatar = c['author']['avatar']
|
32
|
+
comment.author_provider = c['author']['provider']
|
33
|
+
else
|
34
|
+
comment.anonym_name = c['anonym']['name']
|
35
|
+
comment.anonym_email = c['anonym']['email']
|
36
|
+
end
|
37
|
+
if comment.save
|
38
|
+
puts "created #{comment.comment_id}"
|
39
|
+
end
|
40
|
+
end
|
41
|
+
|
42
|
+
desc 'Initial comment base load'
|
43
|
+
task import: :environment do
|
44
|
+
CackleComment.delete_all
|
45
|
+
load_config
|
46
|
+
begin
|
47
|
+
max = CackleComment.maximum('created_at')
|
48
|
+
@url = max ? @path + "&modified=#{max.to_i*1000}" : @path
|
49
|
+
response = open(@url).read
|
50
|
+
json = JSON.parse(response)
|
51
|
+
comments = json['comments']
|
52
|
+
comments.each do |c|
|
53
|
+
create_comment c
|
54
|
+
end
|
55
|
+
end while (comments.size == 100)
|
56
|
+
puts "Total #{CackleComment.count} in base"
|
57
|
+
end
|
58
|
+
|
59
|
+
desc 'Synchronize comments database'
|
60
|
+
task sync: :environment do
|
61
|
+
load_config
|
62
|
+
|
63
|
+
max = CackleComment.maximum('updated_at')
|
64
|
+
raise "run bootstrap first" unless max
|
65
|
+
|
66
|
+
puts "maximum found in base: #{max}"
|
67
|
+
@url = @path + "&modified=#{max.to_i*1000}"
|
68
|
+
|
69
|
+
response = open(@url).read
|
70
|
+
json = JSON.parse(response)
|
71
|
+
|
72
|
+
json['comments'].each do |c|
|
73
|
+
comment = CackleComment.find_by_comment_id(c['id'])
|
74
|
+
if comment
|
75
|
+
if (time = Time.strptime((c['modified'][0..9]).to_s, "%s")) > comment.updated_at
|
76
|
+
puts "updating: #{c['id']}"
|
77
|
+
comment.update_attributes(
|
78
|
+
message: c['message'],
|
79
|
+
status: c['status'],
|
80
|
+
updated_at: time)
|
81
|
+
comment.save
|
82
|
+
else
|
83
|
+
puts "comment: #{c['id']} already exist"
|
84
|
+
end
|
85
|
+
else
|
86
|
+
create_comment c
|
87
|
+
end
|
88
|
+
end
|
89
|
+
end
|
90
|
+
end
|
data/test/cackle_test.rb
ADDED
@@ -0,0 +1,28 @@
|
|
1
|
+
== README
|
2
|
+
|
3
|
+
This README would normally document whatever steps are necessary to get the
|
4
|
+
application up and running.
|
5
|
+
|
6
|
+
Things you may want to cover:
|
7
|
+
|
8
|
+
* Ruby version
|
9
|
+
|
10
|
+
* System dependencies
|
11
|
+
|
12
|
+
* Configuration
|
13
|
+
|
14
|
+
* Database creation
|
15
|
+
|
16
|
+
* Database initialization
|
17
|
+
|
18
|
+
* How to run the test suite
|
19
|
+
|
20
|
+
* Services (job queues, cache servers, search engines, etc.)
|
21
|
+
|
22
|
+
* Deployment instructions
|
23
|
+
|
24
|
+
* ...
|
25
|
+
|
26
|
+
|
27
|
+
Please feel free to use a different markup language if you do not plan to run
|
28
|
+
<tt>rake doc:app</tt>.
|