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 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
@@ -0,0 +1,3 @@
1
+ source 'http://rubygems.org'
2
+
3
+ gemspec
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
@@ -0,0 +1,2 @@
1
+ # web: bundle exec ruby ./app.rb -p 4567
2
+ web: bundle exec puma -p 4567
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
@@ -0,0 +1,2 @@
1
+ require 'bundler'
2
+ Bundler::GemHelper.install_tasks
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: