notable_web 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: 902f5f6885cfba1cc9ff01f2fe722946f6649cc5
4
+ data.tar.gz: d469613856482c398e7667ec2426e4a9256a4838
5
+ SHA512:
6
+ metadata.gz: 45bb114548b59f29f2c213f1cb388a9074825bba71579d86a77797d39d334973ad0aacce42b0690649fc54c8dfcba0ea52684be87e561b94b2a906eb5f9930cc
7
+ data.tar.gz: d0a480918cc03c3af1116753c2bd5f6ae89c1fa498459fa2f1969b3ec63ca0f90d3f98de6d26020d907d281288a77e45634f7e28bcf5392e7819b3e8d39ebf65
data/.gitignore ADDED
@@ -0,0 +1,14 @@
1
+ /.bundle/
2
+ /.yardoc
3
+ /Gemfile.lock
4
+ /_yardoc/
5
+ /coverage/
6
+ /doc/
7
+ /pkg/
8
+ /spec/reports/
9
+ /tmp/
10
+ *.bundle
11
+ *.so
12
+ *.o
13
+ *.a
14
+ mkmf.log
data/Gemfile ADDED
@@ -0,0 +1,4 @@
1
+ source 'https://rubygems.org'
2
+
3
+ # Specify your gem's dependencies in notable_web.gemspec
4
+ gemspec
data/LICENSE.txt ADDED
@@ -0,0 +1,22 @@
1
+ Copyright (c) 2014 Andrew Kane
2
+
3
+ MIT License
4
+
5
+ Permission is hereby granted, free of charge, to any person obtaining
6
+ a copy of this software and associated documentation files (the
7
+ "Software"), to deal in the Software without restriction, including
8
+ without limitation the rights to use, copy, modify, merge, publish,
9
+ distribute, sublicense, and/or sell copies of the Software, and to
10
+ permit persons to whom the Software is furnished to do so, subject to
11
+ the following conditions:
12
+
13
+ The above copyright notice and this permission notice shall be
14
+ included in all copies or substantial portions of the Software.
15
+
16
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
17
+ EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
18
+ MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
19
+ NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
20
+ LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
21
+ OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
22
+ WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
data/README.md ADDED
@@ -0,0 +1,45 @@
1
+ # Notable Web
2
+
3
+ A web interface for [Notable](https://github.com/ankane/notable)
4
+
5
+ ## Installation
6
+
7
+ Add this line to your application’s Gemfile:
8
+
9
+ ```ruby
10
+ gem 'notable_web'
11
+ ```
12
+
13
+ And add it to your `config/routes.rb`.
14
+
15
+ ```ruby
16
+ mount NotableWeb::Engine, at: "notable"
17
+ ```
18
+
19
+ Be sure to secure the dashboard in production.
20
+
21
+ #### Basic Authentication
22
+
23
+ Set the following variables in your environment or an initializer.
24
+
25
+ ```ruby
26
+ ENV["NOTABLE_USERNAME"] = "andrew"
27
+ ENV["NOTABLE_PASSWORD"] = "secret"
28
+ ```
29
+
30
+ #### Devise
31
+
32
+ ```ruby
33
+ authenticate :user, lambda {|user| user.admin? } do
34
+ mount NotableWeb::Engine, at: "notable"
35
+ end
36
+ ```
37
+
38
+ ## Contributing
39
+
40
+ Everyone is encouraged to help improve this project. Here are a few ways you can help:
41
+
42
+ - [Report bugs](https://github.com/ankane/notable_web/issues)
43
+ - Fix bugs and [submit pull requests](https://github.com/ankane/notable_web/pulls)
44
+ - Write, clarify, or fix documentation
45
+ - Suggest or add new features
data/Rakefile ADDED
@@ -0,0 +1,2 @@
1
+ require "bundler/gem_tasks"
2
+
@@ -0,0 +1,68 @@
1
+ module NotableWeb
2
+ class HomeController < ActionController::Base
3
+ layout "notable_web/application"
4
+
5
+ http_basic_authenticate_with name: ENV["NOTABLE_USERNAME"], password: ENV["NOTABLE_PASSWORD"] if ENV["NOTABLE_PASSWORD"]
6
+
7
+ def index
8
+ where = Hash[ params.slice(:status, :note_type, :note, :user_id, :user_type).permit!.map{|k,v| ["notable_requests.#{k}", v] } ]
9
+
10
+ # https://github.com/rails/rails/issues/9055
11
+ @requests = Notable::Request.order("notable_requests.id DESC").where(where).preload(:user).page(params[:page]).per(100)
12
+
13
+ if params[:action_name]
14
+ @requests = @requests.where(action: params[:action_name])
15
+ end
16
+
17
+ if params[:status]
18
+ @requests = @requests.where("note_type != 'Slow Request'")
19
+ end
20
+
21
+ @requests =
22
+ case params[:scope]
23
+ when "slow_requests"
24
+ @requests.where("action IS NOT NULL AND (status = 503 OR note_type = 'Slow Request')")
25
+ when "referrer"
26
+ @requests.where("referrer IS NOT NULL")
27
+ when "external_referrer"
28
+ domain = PublicSuffix.parse(request.host).domain rescue request.host
29
+ @requests.where("referrer IS NOT NULL").where("referrer NOT LIKE ?", "%#{domain}%")
30
+ else
31
+ @requests
32
+ end
33
+ end
34
+
35
+ def users
36
+ requests = Notable::Request.where("created_at > ?", 1.day.ago)
37
+ # yuck make this better
38
+ user_ids = []
39
+ counts = Hash.new(0)
40
+ requests.select("user_id, user_type").order("created_at desc").where("user_id IS NOT NULL").each do |request|
41
+ keys = [request.user_id, request.user_type]
42
+ counts[keys] += 1
43
+ user_ids << keys if counts[keys] == 3
44
+ break if user_ids.size >= 20
45
+ end
46
+ groups = user_ids.group_by{|v| v[1] }
47
+ where = [groups.map{|_, v| "(user_type = ? AND user_id IN (?))" }.join(" OR ")] + groups.flat_map{|v| [v[0], v[1].map{|v1| v1[0] }] }
48
+ @users = requests.includes(:user).where(where).order("created_at DESC").group_by(&:user)
49
+ end
50
+
51
+ def slow_actions
52
+ @top_actions = slow_requests.where("created_at > ?", 3.days.ago).select("action, SUM(request_time) AS total_time, AVG(request_time) AS average_time, COUNT(*) AS requests, SUM(CASE WHEN status = 503 THEN 1 ELSE 0 END) AS timeouts").group("action").order("total_time DESC").limit(50)
53
+ @total_time_by_day = [{name: "Total Time (min)", data: slow_requests.group_by_day(:created_at, last: 14).sum("request_time").map{|k, v| [k, (v / 60.0).round] }}]
54
+ end
55
+
56
+ def slow_action
57
+ @action = params[:action_name]
58
+ @total_time_by_day = [{name: "Total Time (min)", data: slow_requests.where(action: @action).group_by_day(:created_at, last: 14).sum("request_time").map{|k, v| [k, (v / 60.0).round] }}]
59
+ end
60
+
61
+ protected
62
+
63
+ def slow_requests
64
+ Notable::Request.where("action IS NOT NULL AND (status = 503 OR note_type = 'Slow Request')")
65
+ end
66
+
67
+ end
68
+ end
@@ -0,0 +1,105 @@
1
+ <!DOCTYPE html>
2
+ <html>
3
+ <head>
4
+ <title>Notable</title>
5
+
6
+ <meta charset="utf-8" />
7
+
8
+ <style>
9
+ body {
10
+ font-family: "Helvetica Neue", Arial, Helvetica, sans-serif;
11
+ margin: 0;
12
+ padding: 20px;
13
+ font-size: 14px;
14
+ line-height: 1.4;
15
+ color: #333;
16
+ }
17
+
18
+ a, a:visited, a:active {
19
+ color: #428bca;
20
+ text-decoration: none;
21
+ }
22
+
23
+ a:hover {
24
+ text-decoration: underline;
25
+ }
26
+
27
+ table {
28
+ width: 100%;
29
+ border-collapse: collapse;
30
+ border-spacing: 0;
31
+ margin-bottom: 20px;
32
+ }
33
+
34
+ th {
35
+ text-align: left;
36
+ border-bottom: solid 2px #ddd;
37
+ }
38
+
39
+ table td, table th {
40
+ padding: 8px;
41
+ }
42
+
43
+ td {
44
+ border-top: solid 1px #ddd;
45
+ }
46
+
47
+ .nav li {
48
+ display: inline;
49
+ margin-right: 20px;
50
+ }
51
+
52
+ ul {
53
+ padding-left: 0;
54
+ }
55
+
56
+ .stream li {
57
+ display: block;
58
+ border-top: solid 1px #eee;
59
+ }
60
+
61
+ .more {
62
+ display: none;
63
+ padding-bottom: 1em;
64
+ }
65
+ </style>
66
+
67
+ <script src="//ajax.googleapis.com/ajax/libs/jquery/1.10.2/jquery.min.js"></script>
68
+ <%= javascript_include_tag "//www.google.com/jsapi", "chartkick" %>
69
+ </head>
70
+ <body>
71
+ <div class="container">
72
+ <ul class="nav">
73
+ <li><%= link_to "Stream", root_path %></li>
74
+ <li><%= link_to "Slow Requests", root_path(scope: "slow_requests") %></li>
75
+ <li><%= link_to "Timeouts", root_path(note_type: "Timeout") %></li>
76
+ <li><%= link_to "Not Found", root_path(note_type: "Not Found") %></li>
77
+ <li><%= link_to "Errors", root_path(note_type: "Error") %></li>
78
+ <li><%= link_to "Validation Errors", root_path(note_type: "Validation Errors") %></li>
79
+ <li><%= link_to "Unpermitted Parameters", root_path(note_type: "Unpermitted Parameters") %></li>
80
+ <li><%= link_to "Unverified Request", root_path(note_type: "Unverified Request") %></li>
81
+ </ul>
82
+ <ul class="nav">
83
+ <li><%= link_to "Users", users_path %></li>
84
+ <li><%= link_to "Slow Actions", slow_actions_path %></li>
85
+ </ul>
86
+ <form method="get" action="<%= root_path %>">
87
+ <!-- <%= select_tag :user_type, options_for_select([["User", "User"]]) %> -->
88
+ User
89
+ <%= hidden_field_tag :user_type, "User" %>
90
+ <%= text_field_tag :user_id %>
91
+ <input type="submit" value="Go" />
92
+ </form>
93
+ <%= yield %>
94
+ </div>
95
+
96
+ <script>
97
+ $(".stream li").click( function(e) {
98
+ if (e.target.tagName != "A") {
99
+ e.preventDefault();
100
+ $(this).find(".more").slideToggle();
101
+ }
102
+ });
103
+ </script>
104
+ </body>
105
+ </html>
@@ -0,0 +1,33 @@
1
+ <ul class="stream">
2
+ <% requests.each do |request| %>
3
+ <li>
4
+ <p>
5
+ <%= request.created_at.in_time_zone(NotableWeb.time_zone).strftime("#{request.created_at < 24.hours.ago ? "%b %-e, " : nil}%-l:%M %P") %>
6
+ <span style="color: #999;"><%= request.status %></span>
7
+ <strong><%= request.note_type %></strong>
8
+ <!-- <% if request.note_type == "Not Found" %>
9
+ /<%= request.url.sub(/https?:\/\//i, "").split("/", 2).last %>
10
+ <% end %> -->
11
+ <%= request.note %>
12
+ <span style="color: #999;"><%= request.action %></span>
13
+ <% if request.user and show_user %>
14
+ <%= link_to NotableWeb.user_name_method.call(request.user), params.slice(:status, :note_type, :scope).merge(user_id: request.user_id, user_type: request.user_type) %>
15
+ <% end %>
16
+ <% if params[:scope] == "referrer" %>
17
+ <br /><%= link_to nil, request.referrer, target: "_blank" %>
18
+ <% end %>
19
+ </p>
20
+
21
+ <div class="more">
22
+ <%= link_to nil, request.url, target: "_blank" %><br />
23
+ <% if request.referrer.present? %>
24
+ from <%= link_to nil, request.referrer, target: "_blank" %><br />
25
+ <% end %>
26
+ <%= request.ip %><br />
27
+ <%= request.user_agent %><br />
28
+ <%= request.request_id %><br />
29
+ <%= request.request_time.round(3) %>
30
+ </div>
31
+ </li>
32
+ <% end %>
33
+ </ul>
@@ -0,0 +1,38 @@
1
+ <% if (params[:note_type] == "Validation Errors" or params[:note_type] == "Error") && !params[:action_name] %>
2
+ <h3>Top</h3>
3
+
4
+ <table>
5
+ <thead>
6
+ <tr>
7
+ <th>Errors</th>
8
+ <th style="width: 15%; text-align: right;">Users</th>
9
+ <th style="width: 15%; text-align: right;">Count</th>
10
+ </tr>
11
+ </thead>
12
+ <tbody>
13
+ <% Notable::Request.select("note, action, COUNT(DISTINCT (user_type || user_id)) as count_user, COUNT(*) as count_all").where(note_type: params[:note_type]).group([:note, :action]).order("count_all desc").each do |agg| %>
14
+ <tr>
15
+ <td>
16
+ <%= agg.note %> <span style="color: #999;"><%= agg.action %></span>
17
+ <%= link_to "view", params.merge(note: agg.note, action_name: agg.action) %>
18
+ </td>
19
+ <td style="text-align: right;"><%= agg.count_user %></td>
20
+ <td style="text-align: right;"><%= agg.count_all %></td>
21
+ </tr>
22
+ <% end %>
23
+ </tbody>
24
+ </table>
25
+ <% end %>
26
+
27
+ <% if params[:user_type] and params[:user_id] %>
28
+ <h3><%= params[:user_type] %> <%= params[:user_id] %></h3>
29
+ <% end %>
30
+
31
+ <%= render partial: "stream", locals: {requests: @requests, show_user: !(params[:user_id] && params[:user_type])} %>
32
+
33
+ <% if @requests.current_page != 1 %>
34
+ <%= link_to "Prev", params.merge(page: @requests.current_page - 1), style: "margin-right: 20px;" %>
35
+ <% end %>
36
+ <% if @requests.size == @requests.limit_value %>
37
+ <%= link_to "Next", params.merge(page: @requests.current_page + 1) %>
38
+ <% end %>
@@ -0,0 +1,3 @@
1
+ <h4><%= @action %></h4>
2
+
3
+ <%= line_chart @total_time_by_day %>
@@ -0,0 +1,26 @@
1
+ <%= line_chart @total_time_by_day %>
2
+
3
+ <h4>Last 3 Days</h4>
4
+
5
+ <table>
6
+ <thead>
7
+ <tr>
8
+ <th style="width: 40%;">Action</th>
9
+ <th style="width: 15%;">Total Time (min)</th>
10
+ <th style="width: 15%;">Average Time (sec)</th>
11
+ <th style="width: 15%;">Requests</th>
12
+ <th style="width: 15%;">Timeouts</th>
13
+ </tr>
14
+ </thead>
15
+ <tbody>
16
+ <% @top_actions.each do |action| %>
17
+ <tr>
18
+ <td><%= link_to action.action, slow_action_path(action_name: action.action) %></td>
19
+ <td><%= (action.total_time / 60.0).round %></td>
20
+ <td><%= action.average_time.round(1) %></td>
21
+ <td><%= action.requests %></td>
22
+ <td><%= action.timeouts %></td>
23
+ </tr>
24
+ <% end %>
25
+ </tbody>
26
+ </table>
@@ -0,0 +1,7 @@
1
+ <% @users.each do |user, requests| %>
2
+ <% next if !user %>
3
+ <% requests = requests.sort_by(&:created_at).reverse.first(5) %>
4
+ <h3><%= NotableWeb.user_name_method.call(user) %></h3>
5
+ <%= render partial: "stream", locals: {requests: requests, show_user: false} %>
6
+ <p style="margin-bottom: 40px;"><%= link_to "more", root_path(user_id: user.id, user_type: user.class.name) %></p>
7
+ <% end %>
data/config/routes.rb ADDED
@@ -0,0 +1,6 @@
1
+ NotableWeb::Engine.routes.draw do
2
+ get "users", to: "home#users"
3
+ get "slow_actions", to: "home#slow_actions"
4
+ get "slow_action", to: "home#slow_action"
5
+ root to: "home#index"
6
+ end
@@ -0,0 +1,9 @@
1
+ module NotableWeb
2
+ class Engine < ::Rails::Engine
3
+ isolate_namespace NotableWeb
4
+
5
+ initializer "notable_web" do |app|
6
+ NotableWeb.time_zone ||= Time.zone
7
+ end
8
+ end
9
+ end
@@ -0,0 +1,3 @@
1
+ module NotableWeb
2
+ VERSION = "0.0.1"
3
+ end
@@ -0,0 +1,19 @@
1
+ require "notable_web/version"
2
+
3
+ # dependencies
4
+ require "notable"
5
+ require "groupdate"
6
+ require "chartkick"
7
+ require "kaminari"
8
+ require "public_suffix"
9
+
10
+ # engine
11
+ require "notable_web/engine"
12
+
13
+ module NotableWeb
14
+ class << self
15
+ attr_accessor :user_name_method
16
+ attr_accessor :time_zone
17
+ end
18
+ self.user_name_method = proc{|user| user.try(:name) || "#{user.class.name} #{user.id}" }
19
+ end
@@ -0,0 +1,29 @@
1
+ # coding: utf-8
2
+ lib = File.expand_path('../lib', __FILE__)
3
+ $LOAD_PATH.unshift(lib) unless $LOAD_PATH.include?(lib)
4
+ require 'notable_web/version'
5
+
6
+ Gem::Specification.new do |spec|
7
+ spec.name = "notable_web"
8
+ spec.version = NotableWeb::VERSION
9
+ spec.authors = ["Andrew Kane"]
10
+ spec.email = ["andrew@chartkick.com"]
11
+ spec.summary = %q{A web interface for Notable}
12
+ spec.description = %q{A web interface for Notable}
13
+ spec.homepage = "https://github.com/ankane/notable_web"
14
+ spec.license = "MIT"
15
+
16
+ spec.files = `git ls-files -z`.split("\x0")
17
+ spec.executables = spec.files.grep(%r{^bin/}) { |f| File.basename(f) }
18
+ spec.test_files = spec.files.grep(%r{^(test|spec|features)/})
19
+ spec.require_paths = ["lib"]
20
+
21
+ spec.add_dependency "notable"
22
+ spec.add_dependency "groupdate"
23
+ spec.add_dependency "chartkick"
24
+ spec.add_dependency "kaminari"
25
+ spec.add_dependency "public_suffix"
26
+
27
+ spec.add_development_dependency "bundler", "~> 1.7"
28
+ spec.add_development_dependency "rake", "~> 10.0"
29
+ end
metadata ADDED
@@ -0,0 +1,159 @@
1
+ --- !ruby/object:Gem::Specification
2
+ name: notable_web
3
+ version: !ruby/object:Gem::Version
4
+ version: 0.0.1
5
+ platform: ruby
6
+ authors:
7
+ - Andrew Kane
8
+ autorequire:
9
+ bindir: bin
10
+ cert_chain: []
11
+ date: 2014-12-22 00:00:00.000000000 Z
12
+ dependencies:
13
+ - !ruby/object:Gem::Dependency
14
+ name: notable
15
+ requirement: !ruby/object:Gem::Requirement
16
+ requirements:
17
+ - - ">="
18
+ - !ruby/object:Gem::Version
19
+ version: '0'
20
+ type: :runtime
21
+ prerelease: false
22
+ version_requirements: !ruby/object:Gem::Requirement
23
+ requirements:
24
+ - - ">="
25
+ - !ruby/object:Gem::Version
26
+ version: '0'
27
+ - !ruby/object:Gem::Dependency
28
+ name: groupdate
29
+ requirement: !ruby/object:Gem::Requirement
30
+ requirements:
31
+ - - ">="
32
+ - !ruby/object:Gem::Version
33
+ version: '0'
34
+ type: :runtime
35
+ prerelease: false
36
+ version_requirements: !ruby/object:Gem::Requirement
37
+ requirements:
38
+ - - ">="
39
+ - !ruby/object:Gem::Version
40
+ version: '0'
41
+ - !ruby/object:Gem::Dependency
42
+ name: chartkick
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: kaminari
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: public_suffix
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: bundler
85
+ requirement: !ruby/object:Gem::Requirement
86
+ requirements:
87
+ - - "~>"
88
+ - !ruby/object:Gem::Version
89
+ version: '1.7'
90
+ type: :development
91
+ prerelease: false
92
+ version_requirements: !ruby/object:Gem::Requirement
93
+ requirements:
94
+ - - "~>"
95
+ - !ruby/object:Gem::Version
96
+ version: '1.7'
97
+ - !ruby/object:Gem::Dependency
98
+ name: rake
99
+ requirement: !ruby/object:Gem::Requirement
100
+ requirements:
101
+ - - "~>"
102
+ - !ruby/object:Gem::Version
103
+ version: '10.0'
104
+ type: :development
105
+ prerelease: false
106
+ version_requirements: !ruby/object:Gem::Requirement
107
+ requirements:
108
+ - - "~>"
109
+ - !ruby/object:Gem::Version
110
+ version: '10.0'
111
+ description: A web interface for Notable
112
+ email:
113
+ - andrew@chartkick.com
114
+ executables: []
115
+ extensions: []
116
+ extra_rdoc_files: []
117
+ files:
118
+ - ".gitignore"
119
+ - Gemfile
120
+ - LICENSE.txt
121
+ - README.md
122
+ - Rakefile
123
+ - app/controllers/notable_web/home_controller.rb
124
+ - app/views/layouts/notable_web/application.html.erb
125
+ - app/views/notable_web/home/_stream.html.erb
126
+ - app/views/notable_web/home/index.html.erb
127
+ - app/views/notable_web/home/slow_action.html.erb
128
+ - app/views/notable_web/home/slow_actions.html.erb
129
+ - app/views/notable_web/home/users.html.erb
130
+ - config/routes.rb
131
+ - lib/notable_web.rb
132
+ - lib/notable_web/engine.rb
133
+ - lib/notable_web/version.rb
134
+ - notable_web.gemspec
135
+ homepage: https://github.com/ankane/notable_web
136
+ licenses:
137
+ - MIT
138
+ metadata: {}
139
+ post_install_message:
140
+ rdoc_options: []
141
+ require_paths:
142
+ - lib
143
+ required_ruby_version: !ruby/object:Gem::Requirement
144
+ requirements:
145
+ - - ">="
146
+ - !ruby/object:Gem::Version
147
+ version: '0'
148
+ required_rubygems_version: !ruby/object:Gem::Requirement
149
+ requirements:
150
+ - - ">="
151
+ - !ruby/object:Gem::Version
152
+ version: '0'
153
+ requirements: []
154
+ rubyforge_project:
155
+ rubygems_version: 2.2.2
156
+ signing_key:
157
+ specification_version: 4
158
+ summary: A web interface for Notable
159
+ test_files: []