cachai 0.0.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.
- checksums.yaml +7 -0
- data/.gitignore +1 -0
- data/Gemfile +3 -0
- data/Gemfile.lock +63 -0
- data/Procfile +2 -0
- data/README.md +26 -0
- data/Rakefile +2 -0
- data/cachai.gemspec +27 -0
- data/db/schema.rb +25 -0
- data/lib/akismet.rb +154 -0
- data/lib/cachai.rb +134 -0
- data/lib/comment.rb +33 -0
- data/lib/time_ago.rb +31 -0
- data/test/app_test.rb +114 -0
- metadata +143 -0
checksums.yaml
ADDED
@@ -0,0 +1,7 @@
|
|
1
|
+
---
|
2
|
+
SHA1:
|
3
|
+
metadata.gz: 7d0befb3a839b4323421a53cd76689d3b17f631c
|
4
|
+
data.tar.gz: 53f40303c117b425d195244f8b1fe541125dd505
|
5
|
+
SHA512:
|
6
|
+
metadata.gz: 58bb54f64b485bf09c84db2afc4663640edb5e7e934508f377afe7c3bcecacff9ff9914e418bcb900fed6cafe07954bf57418964e6e0a723320151750bccc768
|
7
|
+
data.tar.gz: bd4b23febe2c4e8c36b061aaa5f3d939287f335461b6b35d5ab7ca2055f2e681a1719b050a7ab41ebbe939a3787104b10b7a362a13890d46bce5420669cf64f6
|
data/.gitignore
ADDED
@@ -0,0 +1 @@
|
|
1
|
+
db/*.sqlite3
|
data/Gemfile
ADDED
data/Gemfile.lock
ADDED
@@ -0,0 +1,63 @@
|
|
1
|
+
PATH
|
2
|
+
remote: .
|
3
|
+
specs:
|
4
|
+
cachai (0.0.1)
|
5
|
+
rake
|
6
|
+
sinatra
|
7
|
+
sinatra-activerecord
|
8
|
+
sqlite3
|
9
|
+
|
10
|
+
GEM
|
11
|
+
remote: http://rubygems.org/
|
12
|
+
specs:
|
13
|
+
activemodel (4.2.1)
|
14
|
+
activesupport (= 4.2.1)
|
15
|
+
builder (~> 3.1)
|
16
|
+
activerecord (4.2.1)
|
17
|
+
activemodel (= 4.2.1)
|
18
|
+
activesupport (= 4.2.1)
|
19
|
+
arel (~> 6.0)
|
20
|
+
activesupport (4.2.1)
|
21
|
+
i18n (~> 0.7)
|
22
|
+
json (~> 1.7, >= 1.7.7)
|
23
|
+
minitest (~> 5.1)
|
24
|
+
thread_safe (~> 0.3, >= 0.3.4)
|
25
|
+
tzinfo (~> 1.1)
|
26
|
+
arel (6.0.0)
|
27
|
+
builder (3.2.2)
|
28
|
+
diff-lcs (1.2.5)
|
29
|
+
i18n (0.7.0)
|
30
|
+
json (1.8.3)
|
31
|
+
minitest (5.7.0)
|
32
|
+
rack (1.6.1)
|
33
|
+
rack-protection (1.5.3)
|
34
|
+
rack
|
35
|
+
rake (10.4.2)
|
36
|
+
rspec (2.99.0)
|
37
|
+
rspec-core (~> 2.99.0)
|
38
|
+
rspec-expectations (~> 2.99.0)
|
39
|
+
rspec-mocks (~> 2.99.0)
|
40
|
+
rspec-core (2.99.2)
|
41
|
+
rspec-expectations (2.99.2)
|
42
|
+
diff-lcs (>= 1.1.3, < 2.0)
|
43
|
+
rspec-mocks (2.99.3)
|
44
|
+
sinatra (1.4.6)
|
45
|
+
rack (~> 1.4)
|
46
|
+
rack-protection (~> 1.4)
|
47
|
+
tilt (>= 1.3, < 3)
|
48
|
+
sinatra-activerecord (2.0.6)
|
49
|
+
activerecord (>= 3.2)
|
50
|
+
sinatra (~> 1.0)
|
51
|
+
sqlite3 (1.3.10)
|
52
|
+
thread_safe (0.3.5)
|
53
|
+
tilt (2.0.1)
|
54
|
+
tzinfo (1.2.2)
|
55
|
+
thread_safe (~> 0.1)
|
56
|
+
|
57
|
+
PLATFORMS
|
58
|
+
ruby
|
59
|
+
|
60
|
+
DEPENDENCIES
|
61
|
+
bundler (~> 1.3)
|
62
|
+
cachai!
|
63
|
+
rspec (~> 2.6)
|
data/Procfile
ADDED
data/README.md
ADDED
@@ -0,0 +1,26 @@
|
|
1
|
+
# Commentary
|
2
|
+
|
3
|
+
Add comments to your blog.
|
4
|
+
|
5
|
+
## Setup
|
6
|
+
* Edit `config/production.yml` to add your site. Specify a name and the domain where the site will be hosted. Example configuration will be like this:
|
7
|
+
|
8
|
+
```
|
9
|
+
name: Blog
|
10
|
+
domain: blog.sdqali.in
|
11
|
+
```
|
12
|
+
|
13
|
+
* Run `RACK_ENV=production setup.rb`
|
14
|
+
* Start the server with `RACK_ENV=production app.rb`
|
15
|
+
* Add the following to your HTML pages or templates. `selector` is the CSS selector for the DOM element where comments will be rendered.
|
16
|
+
|
17
|
+
```html
|
18
|
+
<script type="text/javascript" src="<server>/jquery-1.10.2.min.js"></script>
|
19
|
+
<script type="text/javascript" src="<server>//commentary.js"></script>
|
20
|
+
<script type="text/javascript">
|
21
|
+
$(document).ready(function() {
|
22
|
+
Commentary.initialize("<server>", "<selector>");
|
23
|
+
});
|
24
|
+
</script>
|
25
|
+
|
26
|
+
```
|
data/Rakefile
ADDED
data/cachai.gemspec
ADDED
@@ -0,0 +1,27 @@
|
|
1
|
+
# coding: utf-8
|
2
|
+
lib = File.expand_path('../lib', __FILE__)
|
3
|
+
$LOAD_PATH.unshift(lib) unless $LOAD_PATH.include?(lib)
|
4
|
+
|
5
|
+
Gem::Specification.new do |spec|
|
6
|
+
spec.name = "cachai"
|
7
|
+
spec.version = '0.0.1'
|
8
|
+
spec.authors = ["Tomás Pollak"]
|
9
|
+
spec.email = ["tomas@forkhq.com"]
|
10
|
+
spec.description = %q{Middleware for embedabble comments.}
|
11
|
+
spec.summary = %q{Middleware for embedabble comments.}
|
12
|
+
spec.homepage = ""
|
13
|
+
spec.license = "MIT"
|
14
|
+
|
15
|
+
spec.files = `git ls-files`.split($/)
|
16
|
+
spec.executables = spec.files.grep(%r{^bin/}) { |f| File.basename(f) }
|
17
|
+
spec.test_files = spec.files.grep(%r{^(test|spec|features)/})
|
18
|
+
spec.require_paths = ["lib"]
|
19
|
+
|
20
|
+
spec.add_development_dependency "bundler", "~> 1.3"
|
21
|
+
spec.add_development_dependency "rspec", "~> 2.6"
|
22
|
+
|
23
|
+
spec.add_dependency "sinatra"
|
24
|
+
spec.add_dependency "sinatra-activerecord"
|
25
|
+
spec.add_dependency "sqlite3"
|
26
|
+
spec.add_dependency "rake"
|
27
|
+
end
|
data/db/schema.rb
ADDED
@@ -0,0 +1,25 @@
|
|
1
|
+
# This file is auto-generated from the current state of the database. Instead
|
2
|
+
# of editing this file, please use the migrations feature of Active Record to
|
3
|
+
# incrementally modify your database, and then regenerate this schema definition.
|
4
|
+
#
|
5
|
+
# Note that this schema.rb definition is the authoritative source for your
|
6
|
+
# database schema. If you need to create the application database on another
|
7
|
+
# system, you should be using db:schema:load, not running all the migrations
|
8
|
+
# from scratch. The latter is a flawed and unsustainable approach (the more migrations
|
9
|
+
# you'll amass, the slower it'll run and the greater likelihood for issues).
|
10
|
+
#
|
11
|
+
# It's strongly recommended that you check this file into your version control system.
|
12
|
+
|
13
|
+
ActiveRecord::Schema.define(version: 20130901232253) do
|
14
|
+
create_table "comments", force: true do |t|
|
15
|
+
t.string "author_name", :null => false
|
16
|
+
t.string "author_email", :null => false
|
17
|
+
t.string "author_url"
|
18
|
+
t.text "content", :null => false
|
19
|
+
t.text "path", :null => false
|
20
|
+
t.datetime "created_at"
|
21
|
+
t.datetime "updated_at"
|
22
|
+
end
|
23
|
+
|
24
|
+
add_index :comments, :path
|
25
|
+
end
|
data/lib/akismet.rb
ADDED
@@ -0,0 +1,154 @@
|
|
1
|
+
require 'net/http'
|
2
|
+
require 'uri'
|
3
|
+
require 'set'
|
4
|
+
|
5
|
+
class Akismet
|
6
|
+
|
7
|
+
attr_accessor :options, :valid_responses, :normal_responses, :standard_headers, :host, :port
|
8
|
+
|
9
|
+
HOST = 'rest.akismet.com'
|
10
|
+
PORT = 80
|
11
|
+
TIMEOUT_THRESHOLD = 10
|
12
|
+
VALID_RESPONSES = Set.new(['false', ''])
|
13
|
+
NORMAL_RESPONSES = VALID_RESPONSES.dup << 'true'
|
14
|
+
STANDARD_HEADERS = {
|
15
|
+
'User-Agent' => "Akismet Checker",
|
16
|
+
'Content-Type' => 'application/x-www-form-urlencoded'
|
17
|
+
}
|
18
|
+
|
19
|
+
# Create a new instance of the Akismet class
|
20
|
+
#
|
21
|
+
# ==== Arguments
|
22
|
+
# Arguments are provided in the form of a Hash with the following keys
|
23
|
+
# (as Symbols) available:
|
24
|
+
#
|
25
|
+
# +api_key+:: your Akismet API key
|
26
|
+
# +blog+:: the blog associated with your api key
|
27
|
+
#
|
28
|
+
# The following keys are available and are entirely optional. They are
|
29
|
+
# available incase communication with Akismet's servers requires a
|
30
|
+
# proxy port and/or host:
|
31
|
+
#
|
32
|
+
# * +proxy_port+
|
33
|
+
# * +proxy_host+
|
34
|
+
def initialize(options)
|
35
|
+
@options = options
|
36
|
+
self.verified_key = false
|
37
|
+
end
|
38
|
+
|
39
|
+
# Returns +true+ if the API key has been verified, +false+ otherwise
|
40
|
+
def verified?
|
41
|
+
(@verified_key ||= verify_api_key) != :false
|
42
|
+
end
|
43
|
+
|
44
|
+
def invalid_options?
|
45
|
+
false
|
46
|
+
end
|
47
|
+
|
48
|
+
def check_comment(options={})
|
49
|
+
return false if invalid_options?
|
50
|
+
message = call_akismet('comment-check', options)
|
51
|
+
{:spam => !VALID_RESPONSES.include?(message), :message => message}
|
52
|
+
end
|
53
|
+
|
54
|
+
def spam?(options = {})
|
55
|
+
if resp = check_comment(options)
|
56
|
+
return resp[:spam]
|
57
|
+
end
|
58
|
+
false
|
59
|
+
end
|
60
|
+
|
61
|
+
# This call is for submitting comments that weren't marked as spam but
|
62
|
+
# should have been (i.e. false negatives). It takes identical arguments as
|
63
|
+
# +check_comment+.
|
64
|
+
def mark_as_spam(options={})
|
65
|
+
return false if invalid_options?
|
66
|
+
{:message => call_akismet('submit-spam', options)}
|
67
|
+
end
|
68
|
+
|
69
|
+
# This call is intended for the marking of false positives, things that
|
70
|
+
# were incorrectly marked as spam. It takes identical arguments as
|
71
|
+
# +check_comment+ and +mark_as_spam+.
|
72
|
+
def mark_as_ham(options={})
|
73
|
+
return false if invalid_options?
|
74
|
+
{:message => call_akismet('submit-ham', options)}
|
75
|
+
end
|
76
|
+
|
77
|
+
# Returns the URL for an Akismet request
|
78
|
+
#
|
79
|
+
# ==== Arguments
|
80
|
+
# +action+ <~to_s>:: a valid Akismet function name
|
81
|
+
#
|
82
|
+
# ==== Returns
|
83
|
+
# String
|
84
|
+
def self.url(action)
|
85
|
+
"/1.1/#{action}"
|
86
|
+
end
|
87
|
+
|
88
|
+
protected
|
89
|
+
# Internal call to Akismet. Prepares the data for posting to the Akismet
|
90
|
+
# service.
|
91
|
+
#
|
92
|
+
# ==== Arguments
|
93
|
+
# +akismet_function+ <String>::
|
94
|
+
# the Akismet function that should be called
|
95
|
+
#
|
96
|
+
# The following keys are available to configure a given call to Akismet:
|
97
|
+
#
|
98
|
+
# +user_ip+ (*required*)::
|
99
|
+
# IP address of the comment submitter.
|
100
|
+
# +user_agent+ (*required*)::
|
101
|
+
# user agent information.
|
102
|
+
# +referrer+ (<i>note spelling</i>)::
|
103
|
+
# the content of the HTTP_REFERER header should be sent here.
|
104
|
+
# +permalink+::
|
105
|
+
# the permanent location of the entry the comment was submitted to.
|
106
|
+
# +comment_type+::
|
107
|
+
# may be blank, comment, trackback, pingback, or a made up value like
|
108
|
+
# "registration".
|
109
|
+
# +comment_author+::
|
110
|
+
# submitted name with the comment
|
111
|
+
# +comment_author_email+::
|
112
|
+
# submitted email address
|
113
|
+
# +comment_author_url+::
|
114
|
+
# commenter URL
|
115
|
+
# +comment_content+::
|
116
|
+
# the content that was submitted
|
117
|
+
def call_akismet(akismet_function, options={})
|
118
|
+
http_post http_instance, akismet_function, options.update(:blog => options[:blog])
|
119
|
+
end
|
120
|
+
|
121
|
+
# Call to check and verify your API key. You may then call the
|
122
|
+
# <tt>verified?</tt> method to see if your key has been validated
|
123
|
+
def verify_api_key
|
124
|
+
return :false if invalid_options?
|
125
|
+
value = http_post http_instance, 'verify-key', :key => options[:api_key], :blog => options[:blog]
|
126
|
+
self.verified_key = (value == "valid") ? true : :false
|
127
|
+
end
|
128
|
+
|
129
|
+
def http_post(http, action, options = {})
|
130
|
+
params = options.map{ |key, val| "#{key}=#{URI.encode(val || '')}"}.join('&')
|
131
|
+
resp = http.post(self.url(action), params, STANDARD_HEADERS)
|
132
|
+
log_request(self.url(action), params, resp)
|
133
|
+
resp.body
|
134
|
+
end
|
135
|
+
|
136
|
+
def url(action)
|
137
|
+
"/1.1/#{action}"
|
138
|
+
end
|
139
|
+
|
140
|
+
private
|
141
|
+
|
142
|
+
def log_request(url, data, resp)
|
143
|
+
#
|
144
|
+
end
|
145
|
+
|
146
|
+
attr_accessor :verified_key
|
147
|
+
|
148
|
+
def http_instance
|
149
|
+
http = Net::HTTP.new([options[:api_key], HOST].join("."), options[:proxy_host], options[:proxy_port])
|
150
|
+
http.read_timeout = http.open_timeout = TIMEOUT_THRESHOLD
|
151
|
+
http
|
152
|
+
end
|
153
|
+
|
154
|
+
end
|
data/lib/cachai.rb
ADDED
@@ -0,0 +1,134 @@
|
|
1
|
+
require 'sinatra/base'
|
2
|
+
require 'json'
|
3
|
+
require 'rake'
|
4
|
+
require_relative 'comment'
|
5
|
+
require_relative 'akismet'
|
6
|
+
|
7
|
+
module Cachai
|
8
|
+
|
9
|
+
class Middleware < Sinatra::Base
|
10
|
+
|
11
|
+
# set :database_file, "config/database.yml"
|
12
|
+
# set :public_folder, File.join(settings.root, 'public')
|
13
|
+
# set :protection, true
|
14
|
+
use ActiveRecord::ConnectionAdapters::ConnectionManagement
|
15
|
+
|
16
|
+
def initialize(app, opts = nil)
|
17
|
+
opts = opts || {}
|
18
|
+
@domain = opts.delete(:domain) or raise 'Domain required.'
|
19
|
+
|
20
|
+
load_schema unless schema_loaded?
|
21
|
+
|
22
|
+
if key = opts.delete(:akismet_key)
|
23
|
+
@akismet = Akismet.new(:api_key => key, :blog => "http://#{@domain}")
|
24
|
+
else
|
25
|
+
puts "No Akismet key found! Will not check comments for spam."
|
26
|
+
end
|
27
|
+
|
28
|
+
super(app)
|
29
|
+
end
|
30
|
+
|
31
|
+
get '/comments.?:format?' do
|
32
|
+
check_domain!(params[:domain])
|
33
|
+
|
34
|
+
# puts "Comments for: #{params[:domain]}#{params[:path]}"
|
35
|
+
list = get_comments(params[:path])
|
36
|
+
|
37
|
+
if params[:callback]
|
38
|
+
content_type 'application/javascript'
|
39
|
+
"#{params[:callback]}(#{list.to_json});"
|
40
|
+
else
|
41
|
+
json(list)
|
42
|
+
end
|
43
|
+
end
|
44
|
+
|
45
|
+
post '/comments.?:format?' do
|
46
|
+
|
47
|
+
begin
|
48
|
+
data = JSON.parse(request.body.read)
|
49
|
+
check_domain!(data['domain'])
|
50
|
+
|
51
|
+
headers['Access-Control-Allow-Origin'] = data['protocol'] + '//' + data['domain']
|
52
|
+
|
53
|
+
permalink = 'http://' + data['domain'] + data['path']
|
54
|
+
halt(400, "No spam allowed") if is_spam?(data, permalink, request)
|
55
|
+
|
56
|
+
attrs = {
|
57
|
+
:path => data['path'],
|
58
|
+
:content => data['content'],
|
59
|
+
:author_name => data['author_name'],
|
60
|
+
:author_email => data['author_email'],
|
61
|
+
:author_url => data['author_url']
|
62
|
+
}
|
63
|
+
|
64
|
+
comment = Comment.create!(attrs)
|
65
|
+
json({ :status => 'ok', :comment => comment })
|
66
|
+
|
67
|
+
rescue JSON::ParserError
|
68
|
+
status 400 and json({ :error => 'Invalid JSON.' })
|
69
|
+
rescue ActiveRecord::RecordInvalid => e
|
70
|
+
status 422 and json({ :error => e.message })
|
71
|
+
end
|
72
|
+
end
|
73
|
+
|
74
|
+
private
|
75
|
+
|
76
|
+
def load_schema
|
77
|
+
require 'sinatra/activerecord/rake'
|
78
|
+
# Rake::Task['db:schema:load'].invoke
|
79
|
+
require_relative '../db/schema.rb'
|
80
|
+
end
|
81
|
+
|
82
|
+
def schema_loaded?
|
83
|
+
Comment.first
|
84
|
+
true
|
85
|
+
rescue ActiveRecord::StatementInvalid => e
|
86
|
+
# SQLite3::SQLException => e
|
87
|
+
# return !e.message['no such table']
|
88
|
+
false
|
89
|
+
end
|
90
|
+
|
91
|
+
def check_domain!(domain)
|
92
|
+
halt(400, 'Invalid domain.') unless domain == @domain
|
93
|
+
end
|
94
|
+
|
95
|
+
def not_found(message = nil)
|
96
|
+
halt(404, message || 'Not found.')
|
97
|
+
end
|
98
|
+
|
99
|
+
def json(obj)
|
100
|
+
content_type 'application/json'
|
101
|
+
obj.to_json
|
102
|
+
end
|
103
|
+
|
104
|
+
def get_comments(document_path)
|
105
|
+
Comment.where({ :path => document_path })
|
106
|
+
end
|
107
|
+
|
108
|
+
def is_spam?(data, link, request)
|
109
|
+
return false unless @akismet
|
110
|
+
# return true if blacklisted?(name, email, content)
|
111
|
+
|
112
|
+
comment = {
|
113
|
+
:user_ip => request.ip,
|
114
|
+
:referrer => request.referrer,
|
115
|
+
:user_agent => request.user_agent,
|
116
|
+
:permalink => link,
|
117
|
+
:comment_type => 'comment',
|
118
|
+
:comment_content => data['content'],
|
119
|
+
:comment_author => data['author_name'],
|
120
|
+
:comment_author_url => data['author_url'],
|
121
|
+
:comment_author_email => data['author_email']
|
122
|
+
}
|
123
|
+
|
124
|
+
if resp = @akismet.check_comment(comment)
|
125
|
+
# puts resp.inspect
|
126
|
+
return resp[:spam]
|
127
|
+
end
|
128
|
+
|
129
|
+
false
|
130
|
+
end
|
131
|
+
|
132
|
+
end
|
133
|
+
|
134
|
+
end
|
data/lib/comment.rb
ADDED
@@ -0,0 +1,33 @@
|
|
1
|
+
require 'sinatra/activerecord'
|
2
|
+
require 'digest/md5'
|
3
|
+
require 'sqlite3'
|
4
|
+
require_relative 'time_ago'
|
5
|
+
|
6
|
+
ENV_NAME = ENV['RACK_ENV'] || 'development'
|
7
|
+
|
8
|
+
module Cachai
|
9
|
+
class Comment < ActiveRecord::Base
|
10
|
+
|
11
|
+
validates_presence_of :author_name, :author_name, :author_email, :content, :path
|
12
|
+
|
13
|
+
def as_json(options = {})
|
14
|
+
{
|
15
|
+
:id => id,
|
16
|
+
:author_name => author_name,
|
17
|
+
# :author_email => author_email,
|
18
|
+
:author_img => author_img,
|
19
|
+
:author_url => author_url,
|
20
|
+
:content => content,
|
21
|
+
:timestamp => created_at.to_i,
|
22
|
+
# :created_at => created_at,
|
23
|
+
:created_ago => Timeago.since(created_at)
|
24
|
+
}
|
25
|
+
end
|
26
|
+
|
27
|
+
def author_img(size = 50)
|
28
|
+
id = Digest::MD5::hexdigest(author_email.strip.downcase)
|
29
|
+
"https://www.gravatar.com/avatar/#{id}.jpg?s=#{size}"
|
30
|
+
end
|
31
|
+
|
32
|
+
end
|
33
|
+
end
|
data/lib/time_ago.rb
ADDED
@@ -0,0 +1,31 @@
|
|
1
|
+
module Timeago
|
2
|
+
|
3
|
+
def self.in_words(time)
|
4
|
+
minutes = (((Time.now - time).abs)/60).round
|
5
|
+
return nil if minutes < 0
|
6
|
+
|
7
|
+
case minutes
|
8
|
+
when 0..1 then 'less than a minute'
|
9
|
+
when 2..4 then 'less than 5 minutes'
|
10
|
+
when 5..14 then 'less than 15 minutes'
|
11
|
+
when 15..29 then "half an hour"
|
12
|
+
when 30..59 then "#{minutes} minutes"
|
13
|
+
when 60..119 then '1 hour'
|
14
|
+
when 120..239 then '2 hours'
|
15
|
+
when 240..479 then '4 hours'
|
16
|
+
when 480..719 then '8 hours'
|
17
|
+
when 720..1439 then '12 hours'
|
18
|
+
when 1440..11519 then "#{(minutes/1440).floor} days"
|
19
|
+
when 11520..43199 then "#{(minutes/11520).floor} weeks"
|
20
|
+
when 43200..525599 then "#{(minutes/43200).floor} months"
|
21
|
+
else "#{(minutes/525600).floor} years"
|
22
|
+
end
|
23
|
+
end
|
24
|
+
|
25
|
+
def self.since(time)
|
26
|
+
if str = in_words(time)
|
27
|
+
"#{str} ago"
|
28
|
+
end
|
29
|
+
end
|
30
|
+
|
31
|
+
end
|
data/test/app_test.rb
ADDED
@@ -0,0 +1,114 @@
|
|
1
|
+
ENV["RACK_ENV"] = "test"
|
2
|
+
|
3
|
+
require "rubygems"
|
4
|
+
require "test/unit"
|
5
|
+
require "json"
|
6
|
+
require "rack"
|
7
|
+
require "rack/test"
|
8
|
+
require_relative "../app"
|
9
|
+
|
10
|
+
class AppTest < Test::Unit::TestCase
|
11
|
+
include Rack::Test::Methods
|
12
|
+
def app
|
13
|
+
Sinatra::Application
|
14
|
+
end
|
15
|
+
|
16
|
+
def teardown
|
17
|
+
Comment.delete_all
|
18
|
+
Site.delete_all
|
19
|
+
end
|
20
|
+
|
21
|
+
def test_knows_how_to_retrieve_comments
|
22
|
+
blog = Site.create!({
|
23
|
+
:name => "Blog",
|
24
|
+
:domain => "blog.example.com"
|
25
|
+
})
|
26
|
+
4.times do
|
27
|
+
Comment.create!({:nickname => "foo",
|
28
|
+
:content => "Test comment",
|
29
|
+
:document_path => "about/us",
|
30
|
+
:site_id => blog.id})
|
31
|
+
end
|
32
|
+
|
33
|
+
get "/comments.json", {:domain => "blog.example.com", :document_path => "about/us"}
|
34
|
+
assert last_response.ok?
|
35
|
+
output = JSON.parse(last_response.body)
|
36
|
+
assert_equal 4, output.size
|
37
|
+
assert_equal "foo", output.first["nickname"]
|
38
|
+
assert_equal "Test comment", output.first["content"]
|
39
|
+
end
|
40
|
+
|
41
|
+
def test_responds_with_422_if_domain_not_specified
|
42
|
+
get "/comments.json", {:document_path => "about/us"}
|
43
|
+
assert_equal 422, last_response.status
|
44
|
+
assert_match "The domain and document_path must be specified", last_response.body
|
45
|
+
end
|
46
|
+
|
47
|
+
def test_responds_with_422_if_document_path_not_specified
|
48
|
+
get "/comments.json", {:domain => "example.com"}
|
49
|
+
assert_equal 422, last_response.status
|
50
|
+
assert_match "The domain and document_path must be specified", last_response.body
|
51
|
+
end
|
52
|
+
|
53
|
+
def test_know_how_to_add_comment
|
54
|
+
blog = Site.create!({
|
55
|
+
:name => "Blog",
|
56
|
+
:domain => "blog.example.com"
|
57
|
+
})
|
58
|
+
request_body = {
|
59
|
+
:nickname => "foo",
|
60
|
+
:content => "Test comment",
|
61
|
+
:domain => "blog.example.com",
|
62
|
+
:document_path => "about/us"
|
63
|
+
}.to_json
|
64
|
+
post "/comments.json", request_body
|
65
|
+
assert_equal 201, last_response.status
|
66
|
+
assert_equal 1, Comment.all.size
|
67
|
+
end
|
68
|
+
|
69
|
+
def test_does_not_create_comment_if_params_not_sent
|
70
|
+
post "/comments.json"
|
71
|
+
assert_equal 422, last_response.status
|
72
|
+
assert_match "The JSON provided is not valid.", last_response.body
|
73
|
+
assert_equal 0, Comment.all.size
|
74
|
+
end
|
75
|
+
|
76
|
+
def test_does_not_create_comment_if_not_valid_data
|
77
|
+
blog = Site.create!({
|
78
|
+
:name => "Blog",
|
79
|
+
:domain => "blog.example.com"
|
80
|
+
})
|
81
|
+
|
82
|
+
request_body = {
|
83
|
+
:content => "Test comment",
|
84
|
+
:document_path => "about/us",
|
85
|
+
:nickname => "foo",
|
86
|
+
:domain => "foo.example.com"
|
87
|
+
}.to_json
|
88
|
+
post "/comments.json", request_body
|
89
|
+
assert_equal 422, last_response.status
|
90
|
+
assert_match "The Site provided is not valid.", last_response.body
|
91
|
+
assert_equal 0, Comment.all.size
|
92
|
+
end
|
93
|
+
|
94
|
+
def test_serves_static_files
|
95
|
+
get "/commentary.js"
|
96
|
+
assert last_response.ok?
|
97
|
+
end
|
98
|
+
|
99
|
+
def test_ensure_correct_content_type
|
100
|
+
post "/comments.json", {:foo => :bar}
|
101
|
+
assert_equal "application/json", last_response.headers["Content-Type"]
|
102
|
+
end
|
103
|
+
|
104
|
+
def test_ensure_correct_content_type
|
105
|
+
get "/comments.json", {:foo => :bar}
|
106
|
+
end
|
107
|
+
|
108
|
+
def test_renders_comment_frame_as_a_template_with_values
|
109
|
+
get "/comment_frame", {:domain => "foo", :document_path => "/bar"}
|
110
|
+
assert_match /domain: \"foo\"/, last_response.body
|
111
|
+
assert_match /documentPath: \"\/bar\"/, last_response.body
|
112
|
+
assert_equal "*", last_response.headers["Access-Control-Allow-Origin"]
|
113
|
+
end
|
114
|
+
end
|
metadata
ADDED
@@ -0,0 +1,143 @@
|
|
1
|
+
--- !ruby/object:Gem::Specification
|
2
|
+
name: cachai
|
3
|
+
version: !ruby/object:Gem::Version
|
4
|
+
version: 0.0.1
|
5
|
+
platform: ruby
|
6
|
+
authors:
|
7
|
+
- Tomás Pollak
|
8
|
+
autorequire:
|
9
|
+
bindir: bin
|
10
|
+
cert_chain: []
|
11
|
+
date: 2015-06-04 00:00:00.000000000 Z
|
12
|
+
dependencies:
|
13
|
+
- !ruby/object:Gem::Dependency
|
14
|
+
name: bundler
|
15
|
+
requirement: !ruby/object:Gem::Requirement
|
16
|
+
requirements:
|
17
|
+
- - "~>"
|
18
|
+
- !ruby/object:Gem::Version
|
19
|
+
version: '1.3'
|
20
|
+
type: :development
|
21
|
+
prerelease: false
|
22
|
+
version_requirements: !ruby/object:Gem::Requirement
|
23
|
+
requirements:
|
24
|
+
- - "~>"
|
25
|
+
- !ruby/object:Gem::Version
|
26
|
+
version: '1.3'
|
27
|
+
- !ruby/object:Gem::Dependency
|
28
|
+
name: rspec
|
29
|
+
requirement: !ruby/object:Gem::Requirement
|
30
|
+
requirements:
|
31
|
+
- - "~>"
|
32
|
+
- !ruby/object:Gem::Version
|
33
|
+
version: '2.6'
|
34
|
+
type: :development
|
35
|
+
prerelease: false
|
36
|
+
version_requirements: !ruby/object:Gem::Requirement
|
37
|
+
requirements:
|
38
|
+
- - "~>"
|
39
|
+
- !ruby/object:Gem::Version
|
40
|
+
version: '2.6'
|
41
|
+
- !ruby/object:Gem::Dependency
|
42
|
+
name: sinatra
|
43
|
+
requirement: !ruby/object:Gem::Requirement
|
44
|
+
requirements:
|
45
|
+
- - ">="
|
46
|
+
- !ruby/object:Gem::Version
|
47
|
+
version: '0'
|
48
|
+
type: :runtime
|
49
|
+
prerelease: false
|
50
|
+
version_requirements: !ruby/object:Gem::Requirement
|
51
|
+
requirements:
|
52
|
+
- - ">="
|
53
|
+
- !ruby/object:Gem::Version
|
54
|
+
version: '0'
|
55
|
+
- !ruby/object:Gem::Dependency
|
56
|
+
name: sinatra-activerecord
|
57
|
+
requirement: !ruby/object:Gem::Requirement
|
58
|
+
requirements:
|
59
|
+
- - ">="
|
60
|
+
- !ruby/object:Gem::Version
|
61
|
+
version: '0'
|
62
|
+
type: :runtime
|
63
|
+
prerelease: false
|
64
|
+
version_requirements: !ruby/object:Gem::Requirement
|
65
|
+
requirements:
|
66
|
+
- - ">="
|
67
|
+
- !ruby/object:Gem::Version
|
68
|
+
version: '0'
|
69
|
+
- !ruby/object:Gem::Dependency
|
70
|
+
name: sqlite3
|
71
|
+
requirement: !ruby/object:Gem::Requirement
|
72
|
+
requirements:
|
73
|
+
- - ">="
|
74
|
+
- !ruby/object:Gem::Version
|
75
|
+
version: '0'
|
76
|
+
type: :runtime
|
77
|
+
prerelease: false
|
78
|
+
version_requirements: !ruby/object:Gem::Requirement
|
79
|
+
requirements:
|
80
|
+
- - ">="
|
81
|
+
- !ruby/object:Gem::Version
|
82
|
+
version: '0'
|
83
|
+
- !ruby/object:Gem::Dependency
|
84
|
+
name: rake
|
85
|
+
requirement: !ruby/object:Gem::Requirement
|
86
|
+
requirements:
|
87
|
+
- - ">="
|
88
|
+
- !ruby/object:Gem::Version
|
89
|
+
version: '0'
|
90
|
+
type: :runtime
|
91
|
+
prerelease: false
|
92
|
+
version_requirements: !ruby/object:Gem::Requirement
|
93
|
+
requirements:
|
94
|
+
- - ">="
|
95
|
+
- !ruby/object:Gem::Version
|
96
|
+
version: '0'
|
97
|
+
description: Middleware for embedabble comments.
|
98
|
+
email:
|
99
|
+
- tomas@forkhq.com
|
100
|
+
executables: []
|
101
|
+
extensions: []
|
102
|
+
extra_rdoc_files: []
|
103
|
+
files:
|
104
|
+
- ".gitignore"
|
105
|
+
- Gemfile
|
106
|
+
- Gemfile.lock
|
107
|
+
- Procfile
|
108
|
+
- README.md
|
109
|
+
- Rakefile
|
110
|
+
- cachai.gemspec
|
111
|
+
- db/schema.rb
|
112
|
+
- lib/akismet.rb
|
113
|
+
- lib/cachai.rb
|
114
|
+
- lib/comment.rb
|
115
|
+
- lib/time_ago.rb
|
116
|
+
- test/app_test.rb
|
117
|
+
homepage: ''
|
118
|
+
licenses:
|
119
|
+
- MIT
|
120
|
+
metadata: {}
|
121
|
+
post_install_message:
|
122
|
+
rdoc_options: []
|
123
|
+
require_paths:
|
124
|
+
- lib
|
125
|
+
required_ruby_version: !ruby/object:Gem::Requirement
|
126
|
+
requirements:
|
127
|
+
- - ">="
|
128
|
+
- !ruby/object:Gem::Version
|
129
|
+
version: '0'
|
130
|
+
required_rubygems_version: !ruby/object:Gem::Requirement
|
131
|
+
requirements:
|
132
|
+
- - ">="
|
133
|
+
- !ruby/object:Gem::Version
|
134
|
+
version: '0'
|
135
|
+
requirements: []
|
136
|
+
rubyforge_project:
|
137
|
+
rubygems_version: 2.2.0
|
138
|
+
signing_key:
|
139
|
+
specification_version: 4
|
140
|
+
summary: Middleware for embedabble comments.
|
141
|
+
test_files:
|
142
|
+
- test/app_test.rb
|
143
|
+
has_rdoc:
|