mongo_browser 0.1.0

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.
Files changed (47) hide show
  1. data/.gitignore +4 -0
  2. data/.rspec +2 -0
  3. data/.travis.yml +12 -0
  4. data/Gemfile +19 -0
  5. data/LICENSE +22 -0
  6. data/README.md +31 -0
  7. data/README.rdoc +19 -0
  8. data/Rakefile +61 -0
  9. data/app/assets/images/bootstrap/glyphicons-halflings-white.png +0 -0
  10. data/app/assets/images/bootstrap/glyphicons-halflings.png +0 -0
  11. data/app/assets/javascripts/app/table_filter.js.coffee +50 -0
  12. data/app/assets/javascripts/application.js.coffee +32 -0
  13. data/app/assets/javascripts/vendor/bootbox.js +508 -0
  14. data/app/assets/javascripts/vendor/bootstrap.js +2025 -0
  15. data/app/assets/javascripts/vendor/jquery.js +9266 -0
  16. data/app/assets/stylesheets/application.css.scss +22 -0
  17. data/app/assets/stylesheets/vendor/bootstrap-responsive.css +1088 -0
  18. data/app/assets/stylesheets/vendor/bootstrap.css +5893 -0
  19. data/app/views/collections/show.erb +61 -0
  20. data/app/views/databases/show.erb +59 -0
  21. data/app/views/index.erb +29 -0
  22. data/app/views/layout.erb +19 -0
  23. data/app/views/layout/_breadcrumb.erb +13 -0
  24. data/app/views/layout/_flash_messages.erb +10 -0
  25. data/app/views/layout/_navbar.erb +21 -0
  26. data/app/views/server_info.erb +20 -0
  27. data/app/views/shared/_filter.erb +14 -0
  28. data/bin/mongo_browser +53 -0
  29. data/config.ru +4 -0
  30. data/features/mongo_browser.feature +18 -0
  31. data/features/step_definitions/mongo_browser_steps.rb +1 -0
  32. data/features/support/env.rb +18 -0
  33. data/lib/mongo_browser.rb +32 -0
  34. data/lib/mongo_browser/application.rb +108 -0
  35. data/lib/mongo_browser/middleware/sprockets_sinatra.rb +24 -0
  36. data/lib/mongo_browser/version.rb +3 -0
  37. data/mongo_browser.gemspec +32 -0
  38. data/spec/features/collections_list_spec.rb +105 -0
  39. data/spec/features/databases_list_spec.rb +60 -0
  40. data/spec/features/documents_list_spec.rb +136 -0
  41. data/spec/features/server_info_spec.rb +17 -0
  42. data/spec/fixtures/databases.json +40 -0
  43. data/spec/spec_helper.rb +56 -0
  44. data/spec/support/have_flash_message_matcher.rb +16 -0
  45. data/spec/support/integration.rb +26 -0
  46. data/spec/support/mongo_test_server.rb +50 -0
  47. metadata +313 -0
@@ -0,0 +1,24 @@
1
+ module MongoBrowser
2
+ module Middleware
3
+ class SprocketsSinatra
4
+ def initialize(app, options = {})
5
+ @app = app
6
+ @root = options[:root]
7
+ path = options[:path] || "assets"
8
+ @matcher = /^\/#{path}\/*/
9
+ @environment = ::Sprockets::Environment.new(@root)
10
+ @environment.append_path "assets/javascripts"
11
+ @environment.append_path "assets/javascripts/vendor"
12
+ @environment.append_path "assets/stylesheets"
13
+ @environment.append_path "assets/stylesheets/vendor"
14
+ @environment.append_path "assets/images"
15
+ end
16
+
17
+ def call(env)
18
+ return @app.call(env) unless @matcher =~ env["PATH_INFO"]
19
+ env["PATH_INFO"].sub!(@matcher, "")
20
+ @environment.call(env)
21
+ end
22
+ end
23
+ end
24
+ end
@@ -0,0 +1,3 @@
1
+ module MongoBrowser
2
+ VERSION = "0.1.0"
3
+ end
@@ -0,0 +1,32 @@
1
+ # -*- encoding: utf-8 -*-
2
+ require File.expand_path("../lib/mongo_browser/version", __FILE__)
3
+
4
+ Gem::Specification.new do |gem|
5
+ gem.authors = ["Lukasz Bandzarewicz"]
6
+ gem.email = ["lucassus@gmail.com"]
7
+ gem.description = %q{Simple but powerful tool for managing mongodb databases}
8
+ gem.summary = %q{Web-based application for managing mongodb databases}
9
+ gem.homepage = "https://github.com/lucassus/mongo_browser"
10
+
11
+ gem.files = `git ls-files`.split($\)
12
+ gem.executables = gem.files.grep(%r{^bin/}).map{ |f| File.basename(f) }
13
+ gem.test_files = gem.files.grep(%r{^(test|spec|features)/})
14
+ gem.name = "mongo_browser"
15
+ gem.require_paths = ["lib"]
16
+ gem.version = MongoBrowser::VERSION
17
+
18
+ gem.add_development_dependency("rdoc")
19
+ gem.add_development_dependency("aruba")
20
+ gem.add_development_dependency("rake", "~> 0.9.2")
21
+
22
+ gem.add_dependency("mongo", "~> 1.7.0")
23
+ gem.add_dependency("bson_ext", "~> 1.7.0")
24
+ gem.add_dependency("methadone", "~> 1.2.2")
25
+ gem.add_dependency("foreverb", "~> 0.3.2")
26
+ gem.add_dependency("sinatra", "~> 1.3.3")
27
+ gem.add_dependency("sinatra-contrib", "~> 1.3.2")
28
+ gem.add_dependency("sinatra-flash", "~> 0.3.0")
29
+ gem.add_dependency("will_paginate-bootstrap", "~> 0.2.1")
30
+ gem.add_dependency("json", "~> 1.7.5")
31
+ gem.add_dependency("awesome_print", "~> 1.1.0")
32
+ end
@@ -0,0 +1,105 @@
1
+ require "spec_helper"
2
+
3
+ describe "Collections list", type: :request do
4
+ before do
5
+ visit "/"
6
+
7
+ within("table.databases") { click_link "first_database" }
8
+ end
9
+
10
+ it "displays the breadcrumb" do
11
+ within ".breadcrumb" do
12
+ within "li:nth-child(1)" do
13
+ expect(page).to have_link("Databases")
14
+ end
15
+
16
+ within "li:nth-child(2)" do
17
+ expect(page).to have_content("db: first_database")
18
+ end
19
+ end
20
+ end
21
+
22
+ it "displays all available collections for the selected database" do
23
+ within "table" do
24
+ expect(page).to have_link("first_collection")
25
+ expect(page).to have_link("second_collection")
26
+ expect(page).to have_link("third_collection")
27
+ end
28
+ end
29
+
30
+ it "displays information about the database", js: true do
31
+ click_link "Stats"
32
+
33
+ within "table" do
34
+ %w(db collection objects avgObjSize dataSize storageSize numExtents indexes indexSize nsSizeMB ok).each do |field|
35
+ expect(page).to have_content(field)
36
+ end
37
+ end
38
+ end
39
+
40
+ describe "filtering", js: true do
41
+ it "filters collections by name" do
42
+ fill_in_filter("second")
43
+
44
+ within "table.collections" do
45
+ expect(page).to have_link("second_collection")
46
+ expect(page).to_not have_link("first_collection")
47
+ expect(page).to_not have_link("third_collection")
48
+ end
49
+ end
50
+
51
+ it "displays a notification when nothing has been found" do
52
+ fill_in_filter("fifth")
53
+
54
+ expect(page).to_not have_css("table.collections")
55
+ expect(page).to have_content("Nothing has been found.")
56
+ end
57
+ end
58
+
59
+ describe "click on delete collection button", js: true do
60
+ it "deletes a collection" do
61
+ click_delete_button_for("second_collection")
62
+ confirm_dialog
63
+
64
+ expect(page).to have_flash_message("Collection second_collection has been deleted.")
65
+
66
+ within "table.collections" do
67
+ expect(page).to have_link("first_collection")
68
+ expect(page).to_not have_link("second_collection")
69
+ expect(page).to have_link("third_collection")
70
+ end
71
+
72
+ click_delete_button_for("first_collection")
73
+ confirm_dialog
74
+ expect(page).to have_flash_message("Collection first_collection has been deleted.")
75
+
76
+ within "table.collections" do
77
+ expect(page).to_not have_link("first_collection")
78
+ expect(page).to_not have_link("second_collection")
79
+ expect(page).to have_link("third_collection")
80
+ end
81
+
82
+ click_delete_button_for("third_collection")
83
+ confirm_dialog
84
+ expect(page).to have_flash_message("Collection third_collection has been deleted.")
85
+
86
+ should_hide_the_table_and_display_a_notification
87
+ end
88
+
89
+ it "displays error message when the collection cannot be deleted" do
90
+ click_delete_button_for("system.indexes")
91
+ confirm_dialog
92
+
93
+ expect(page).to have_flash_message("Database command 'drop' failed")
94
+
95
+ within "table.collections" do
96
+ expect(page).to have_link("system.indexes")
97
+ end
98
+ end
99
+
100
+ def click_delete_button_for(collection_name)
101
+ collection_row = find(:xpath, %Q{//table//tr//*[contains(text(), "#{collection_name}")]/../..})
102
+ within(collection_row) { click_link "Delete" }
103
+ end
104
+ end
105
+ end
@@ -0,0 +1,60 @@
1
+ require "spec_helper"
2
+
3
+ describe "Databases list", type: :request do
4
+ before { visit "/" }
5
+
6
+ it "hides the breadcrumb" do
7
+ expect(page).not_to have_css(".breadcrumb")
8
+ end
9
+
10
+ it "displays list with available databases" do
11
+ expect(page).to have_content("Available databases")
12
+
13
+ within "table" do
14
+ expect(page).to have_link("first_database")
15
+ expect(page).to have_link("second_database")
16
+ end
17
+ end
18
+
19
+ describe "filtering", js: true do
20
+ it "filters databases by name" do
21
+ fill_in_filter("first")
22
+
23
+ within "table.databases" do
24
+ expect(page).to have_link("first_database")
25
+ expect(page).to_not have_link("second_database")
26
+ end
27
+ end
28
+
29
+ it "displays a notification when nothing has been found" do
30
+ fill_in_filter("third")
31
+ should_hide_the_table_and_display_a_notification
32
+ end
33
+ end
34
+
35
+ describe "click on delete database button", js: true do
36
+ it "deletes a database" do
37
+ click_delete_button_for("second_database")
38
+ confirm_dialog
39
+
40
+ expect(page).to have_flash_message("Database second_database has been deleted.")
41
+
42
+ within "table.databases" do
43
+ expect(page).to have_link("first_database")
44
+ expect(page).to_not have_link("second_database")
45
+ end
46
+
47
+ click_delete_button_for("first_database")
48
+ confirm_dialog
49
+
50
+ expect(page).to have_flash_message("Database first_database has been deleted.")
51
+
52
+ should_hide_the_table_and_display_a_notification
53
+ end
54
+
55
+ def click_delete_button_for(database_name)
56
+ database_row = find(:xpath, %Q{//table//tr//*[contains(text(), "#{database_name}")]/../..})
57
+ within(database_row) { click_link "Delete" }
58
+ end
59
+ end
60
+ end
@@ -0,0 +1,136 @@
1
+ require "spec_helper"
2
+
3
+ describe "Documents list", type: :request do
4
+ let(:connection) { MongoTestServer.connection }
5
+
6
+ before do
7
+ visit "/"
8
+
9
+ within("table.databases") { click_link "first_database" }
10
+ within("table.collections") { click_link current_collection_name }
11
+ end
12
+
13
+ shared_examples "breadcrumbs for documents list" do
14
+ it "displays the breadcrumb" do
15
+ within ".breadcrumb" do
16
+ within "li:nth-child(1)" do
17
+ expect(page).to have_link("Databases")
18
+ end
19
+
20
+ within "li:nth-child(2)" do
21
+ expect(page).to have_link("db: first_database")
22
+ end
23
+
24
+ within "li:nth-child(3)" do
25
+ expect(page).to have_content("collection: #{current_collection_name}")
26
+ end
27
+ end
28
+ end
29
+ end
30
+
31
+ context "with small number for documents" do
32
+ let(:current_collection_name) { "first_collection" }
33
+
34
+ include_examples "breadcrumbs for documents list"
35
+
36
+ it "displays all documents for the selected collection" do
37
+ expect(page).to have_css("tr.document", count: 2)
38
+
39
+ within "table" do
40
+ expect(page).to have_content("This is a sample record")
41
+ expect(page).to have_content("This is the second sample record")
42
+ end
43
+ end
44
+
45
+ it "displays information about the collection", js: true do
46
+ click_link "Stats"
47
+
48
+ within "table" do
49
+ %w(ns count size avgObjSize storageSize numExtents nindexes lastExtentSize paddingFactor systemFlags userFlags totalIndexSize indexSizes ok).each do |field|
50
+ expect(page).to have_content(field)
51
+ end
52
+ end
53
+ end
54
+
55
+ describe "click on delete document button", js: true do
56
+ let(:document) do
57
+ database = connection.db("first_database")
58
+ collection = database.collection(current_collection_name)
59
+ collection.find_one(name: "This is the second sample record")
60
+ end
61
+
62
+ it "removes a document from the collection" do
63
+ click_delete_button_for(document)
64
+ confirm_dialog
65
+
66
+ expect(page).to have_flash_message("Document #{document["_id"]} has been deleted.")
67
+
68
+ expect(page).to have_css("tr.document", count: 1)
69
+
70
+ within "table" do
71
+ expect(page).to have_content("This is a sample record")
72
+ expect(page).not_to have_content(document["name"])
73
+ end
74
+ end
75
+
76
+ def click_delete_button_for(document)
77
+ database_row = find(:xpath, %Q{//table//tr/td[1][contains(text(), "#{document["_id"]}")]/..})
78
+ within(database_row) { click_link "Delete" }
79
+ end
80
+ end
81
+ end
82
+
83
+ context "with large number of documents" do
84
+ let(:current_collection_name) { "second_collection" }
85
+
86
+ before do
87
+ database = connection.db("first_database")
88
+ collection = database.collection(current_collection_name)
89
+
90
+ 70.times do |n|
91
+ collection.insert(name: "Document #{n}", position: n)
92
+ end
93
+
94
+ visit current_path
95
+ end
96
+
97
+ include_examples "breadcrumbs for documents list"
98
+
99
+ it "displays a pagination" do
100
+ expect(page).to have_css("div.pagination", count: 2)
101
+ within "div.pagination" do
102
+ (1..3).each do |n|
103
+ expect(page).to have_link(n.to_s)
104
+ end
105
+ end
106
+ end
107
+
108
+ it "paginates documents" do
109
+ within "table.documents" do
110
+ (0...25).each do |n|
111
+ expect(page).to have_content("Document #{n}")
112
+ end
113
+ end
114
+
115
+ within "div.pagination" do
116
+ click_link "2"
117
+ end
118
+
119
+ within "table.documents" do
120
+ (25...50).each do |n|
121
+ expect(page).to have_content("Document #{n}")
122
+ end
123
+ end
124
+
125
+ within "div.pagination" do
126
+ click_link "3"
127
+ end
128
+
129
+ within "table.documents" do
130
+ (50...70).each do |n|
131
+ expect(page).to have_content("Document #{n}")
132
+ end
133
+ end
134
+ end
135
+ end
136
+ end
@@ -0,0 +1,17 @@
1
+ require "spec_helper"
2
+
3
+ describe "Server info", type: :request do
4
+ before do
5
+ visit "/"
6
+
7
+ within(".navbar") { click_link "Server Info" }
8
+ end
9
+
10
+ it "displays information about the server" do
11
+ within "table" do
12
+ %w(version gitVersion sysInfo versionArray bits debug maxBsonObjectSize ok).each do |field|
13
+ expect(page).to have_content(field)
14
+ end
15
+ end
16
+ end
17
+ end
@@ -0,0 +1,40 @@
1
+ [
2
+ {
3
+ "name": "first_database",
4
+ "collections": [
5
+ {
6
+ "name": "first_collection",
7
+ "documents": [
8
+ {
9
+ "name": "This is a sample record",
10
+ "position": {
11
+ "x": 123,
12
+ "y": "135"
13
+ }
14
+ },
15
+ {
16
+ "name": "This is the second sample record",
17
+ "position": {
18
+ "x": 567,
19
+ "y": "246"
20
+ }
21
+ }
22
+ ]
23
+ },
24
+ {
25
+ "name": "second_collection"
26
+ },
27
+ {
28
+ "name": "third_collection"
29
+ }
30
+ ]
31
+ },
32
+ {
33
+ "name": "second_database",
34
+ "collections": [
35
+ {
36
+ "name": "first_collection"
37
+ }
38
+ ]
39
+ }
40
+ ]
@@ -0,0 +1,56 @@
1
+ $:.unshift(File.dirname(__FILE__))
2
+ $:.unshift(File.join(File.dirname(__FILE__), "..", "lib"))
3
+
4
+ require "simplecov"
5
+ SimpleCov.start do
6
+ add_group "Application", "lib"
7
+ end
8
+
9
+ require "mongo_browser"
10
+
11
+ require "debugger"
12
+ require "rspec"
13
+ require "capybara"
14
+ require "capybara/rspec"
15
+ require "socket"
16
+
17
+ def find_available_port
18
+ server = TCPServer.new("127.0.0.1", 0)
19
+ server.addr[1]
20
+ ensure
21
+ server.close if server
22
+ end
23
+
24
+ MongoBrowser.mongodb_host = "localhost"
25
+ MongoBrowser.mongodb_port = find_available_port
26
+
27
+ require "capybara/webkit"
28
+ Capybara.javascript_driver = :webkit
29
+
30
+ Capybara.ignore_hidden_elements = true
31
+ Capybara.app = MongoBrowser::Application
32
+
33
+ # Requires supporting ruby files with custom matchers and macros, etc,
34
+ # from spec/support/ and its subdirectories.
35
+ Dir[File.expand_path("spec/support/**/*.rb")].each { |f| require f }
36
+
37
+ RSpec.configure do |config|
38
+ config.include Integration
39
+
40
+ config.before do
41
+ MongoTestServer.ensure_test_server_is_running
42
+ MongoTestServer.load_fixtures
43
+ end
44
+
45
+ config.after do
46
+ # Take a screenshot when the scenario has failed
47
+ if example.metadata[:js] and example.exception
48
+ file_name = example.full_description.downcase.gsub(/\s/, "-")
49
+ page.driver.render("/tmp/#{file_name}.png", full: true)
50
+ end
51
+ end
52
+ end
53
+
54
+ at_exit do
55
+ MongoTestServer.clean_up
56
+ end