mongo_fe 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 (71) hide show
  1. data/.DS_Store +0 -0
  2. data/.gitignore +34 -0
  3. data/.rspec +2 -0
  4. data/CHANGES.md +3 -0
  5. data/Gemfile +3 -0
  6. data/Gemfile.lock +138 -0
  7. data/LICENSE +22 -0
  8. data/Procfile +2 -0
  9. data/README.md +72 -0
  10. data/Rakefile +14 -0
  11. data/TODO.tasks +9 -0
  12. data/bin/config.ru +28 -0
  13. data/bin/mongofe +54 -0
  14. data/lib/mongo_fe/application_controller.rb +91 -0
  15. data/lib/mongo_fe/controllers/collections_controller.rb +278 -0
  16. data/lib/mongo_fe/controllers/databases_controller.rb +94 -0
  17. data/lib/mongo_fe/helpers/helpers.rb +128 -0
  18. data/lib/mongo_fe/public/app/application.js +3 -0
  19. data/lib/mongo_fe/public/bootstrap/css/bootstrap-responsive.css +567 -0
  20. data/lib/mongo_fe/public/bootstrap/css/bootstrap.css +3380 -0
  21. data/lib/mongo_fe/public/bootstrap/img/glyphicons-halflings-white.png +0 -0
  22. data/lib/mongo_fe/public/bootstrap/img/glyphicons-halflings.png +0 -0
  23. data/lib/mongo_fe/public/bootstrap/js/basic/bootstrap-alert.js +91 -0
  24. data/lib/mongo_fe/public/bootstrap/js/basic/bootstrap-button.js +98 -0
  25. data/lib/mongo_fe/public/bootstrap/js/basic/bootstrap-carousel.js +154 -0
  26. data/lib/mongo_fe/public/bootstrap/js/basic/bootstrap-collapse.js +136 -0
  27. data/lib/mongo_fe/public/bootstrap/js/basic/bootstrap-dropdown.js +92 -0
  28. data/lib/mongo_fe/public/bootstrap/js/basic/bootstrap-modal.js +209 -0
  29. data/lib/mongo_fe/public/bootstrap/js/basic/bootstrap-popover.js +95 -0
  30. data/lib/mongo_fe/public/bootstrap/js/basic/bootstrap-scrollspy.js +125 -0
  31. data/lib/mongo_fe/public/bootstrap/js/basic/bootstrap-tab.js +130 -0
  32. data/lib/mongo_fe/public/bootstrap/js/basic/bootstrap-tooltip.js +270 -0
  33. data/lib/mongo_fe/public/bootstrap/js/basic/bootstrap-transition.js +51 -0
  34. data/lib/mongo_fe/public/bootstrap/js/basic/bootstrap-typeahead.js +271 -0
  35. data/lib/mongo_fe/public/bootstrap/js/bootstrap.js +1722 -0
  36. data/lib/mongo_fe/public/bootstrap/js/bootstrap.min.js +1 -0
  37. data/lib/mongo_fe/public/bootstrap/js/jquery.js +9252 -0
  38. data/lib/mongo_fe/public/bootstrap/js/underscore-min.js +32 -0
  39. data/lib/mongo_fe/public/bootstrap/js/underscore.js +1059 -0
  40. data/lib/mongo_fe/public/css/digg_pagination.css +28 -0
  41. data/lib/mongo_fe/public/css/jsoneditor.css +70 -0
  42. data/lib/mongo_fe/public/css/styles.css +11 -0
  43. data/lib/mongo_fe/public/images/missing_avatar_small.png +0 -0
  44. data/lib/mongo_fe/public/js/collection-tools.js +63 -0
  45. data/lib/mongo_fe/public/js/db-tools.js +45 -0
  46. data/lib/mongo_fe/public/js/jsoneditor/jquery.jsoneditor.min.LICENSE +20 -0
  47. data/lib/mongo_fe/public/js/jsoneditor/jquery.jsoneditor.min.js +6 -0
  48. data/lib/mongo_fe/public/js/jsoneditor/json2.js +482 -0
  49. data/lib/mongo_fe/version.rb +6 -0
  50. data/lib/mongo_fe/views/collections/_document_attributes.haml +6 -0
  51. data/lib/mongo_fe/views/collections/_documents.haml +35 -0
  52. data/lib/mongo_fe/views/collections/_documents_page.haml +82 -0
  53. data/lib/mongo_fe/views/collections/_indexes.haml +76 -0
  54. data/lib/mongo_fe/views/collections/index.haml +80 -0
  55. data/lib/mongo_fe/views/databases/_list_users.haml +27 -0
  56. data/lib/mongo_fe/views/databases/info.haml +80 -0
  57. data/lib/mongo_fe/views/footer.haml +4 -0
  58. data/lib/mongo_fe/views/index.haml +7 -0
  59. data/lib/mongo_fe/views/layout.haml +148 -0
  60. data/lib/mongo_fe/views/navbar.haml +17 -0
  61. data/lib/mongo_fe.rb +170 -0
  62. data/mongo_fe.gemspec +50 -0
  63. data/screens/example_db_view.png +0 -0
  64. data/screens/example_doc_view.png +0 -0
  65. data/screens/example_indexes.png +0 -0
  66. data/spec/config_spec.rb +17 -0
  67. data/spec/controllers/collections_controller_spec.rb +151 -0
  68. data/spec/controllers/db_controller_spec.rb +69 -0
  69. data/spec/factories/factories.rb +19 -0
  70. data/spec/spec_helper.rb +88 -0
  71. metadata +559 -0
@@ -0,0 +1,76 @@
1
+ %p
2
+ - unless @indexes.nil?
3
+ %table.table-striped
4
+ %thead
5
+ %tr.well
6
+ %th(width="20%") NAME
7
+ %th(width="48%" align="left") INDEXED FIELD(S)
8
+ %th(width="7%" align="center") UNIQUE
9
+ %th(width="9%" align="center") SPARSE
10
+ %th(width="10%" align="right") SIZE (MB)
11
+ %th(width="9%" nowrap)
12
+ %tbody
13
+ - @indexes.each_pair do |k,v|
14
+ %tr
15
+ %td(valign="top" align="left")
16
+ =k
17
+ %td(valign="top" align="left")
18
+ =v['key']
19
+ %td(valign="top" align="center")
20
+ =v['unique'] ? '✓': '-'
21
+ %td(valign="top" align="center")
22
+ =v['sparse'] ? '✓': '-'
23
+ %td(valign="top" align="right")
24
+ ='%.3f' % bytes_to_mb(collection.stats['indexSizes'][k])
25
+ %td(valign="top" nowrap align="right")
26
+ - unless k.eql? '_id_'
27
+ %a#confirm-delete-index-button(href="#" data-index-name="#{k}" data-toggle="modal" data-target="#confirm-delete-index-dialog")
28
+ %i.icon-remove
29
+ - else
30
+ no indexes defined.
31
+ %p
32
+ %a.btn.btn-primary.btn-small#new-index-button(href="#new-index-dialog" data-toggle="modal") new index
33
+
34
+
35
+ #new-index-dialog.modal.hide
36
+ .modal-header
37
+ %a.close(data-dismiss="modal")
38
+ %icon.icon-remove
39
+ %h3 Create a new index
40
+ .modal-body
41
+ %form.form-horizontal{:method => "post", :action => "/databases/#{db.name}/collections/#{collection.name}/indexes"}
42
+ %fieldset
43
+ .control-group
44
+ %label.control-label( for='index-fields') Indexed fields
45
+ .controls
46
+ %textarea#index-fields.input-xlarge.field.span10(name="index_fields" rows="5" placeholder='ex. name:ASC, created_at:DESC')
47
+ %p#index_fields-msg.help-block
48
+ .control-group
49
+ %label.control-label
50
+ .controls
51
+ %label.checkbox.inline
52
+ %input#is-unique{ :type=>'checkbox', :name=>'unique', :value => false} unique?
53
+ %label.checkbox.inline
54
+ %input#is-sparse{ :type=>'checkbox', :name=>'sparse', :value => false} sparse?
55
+ .form-actions.modal-footer
56
+ %a.btn(href="#" data-dismiss="modal") Close
57
+ %button#new-index-dialog-submit-button.btn.btn-primary.closeModal(type="submit") Create
58
+
59
+ #confirm-delete-index-dialog.modal.hide
60
+ .modal-header
61
+ %a.close(data-dismiss="modal")
62
+ %icon.icon-remove
63
+ %h3 Delete index
64
+ .modal-body
65
+ %p.lead
66
+ You are about to delete a collection index. This procedure is irreversible.
67
+ %p.well.well-small
68
+ delete:
69
+ %span.lead.index-name.muted
70
+ %form.form-horizontal{:method => "post", :action => "/databases/#{db.name}/collections/#{collection.name}/indexes"}
71
+ %input{:type => "hidden", :name => "_method", :value => "delete"}
72
+ %input.index-name-field{:type => "hidden", :name => "index_name", :value => ""}
73
+ %fieldset
74
+ .form-actions.modal-footer
75
+ %a.btn(href="#" data-dismiss="modal") Close
76
+ %button#new-document-modal-submit-button.btn.btn-danger.closeModal(type="submit") Delete
@@ -0,0 +1,80 @@
1
+ -unless @collection.nil?
2
+ %ul.nav.nav-tabs
3
+ %li.inactive
4
+ %button.btn.btn-medium.btn-primary.disabled
5
+ -# we know: #{current_db.name}.#{@collection.name}
6
+ collection: #{@collection.name}
7
+ .span1
8
+
9
+ %li
10
+ %a#collections-info(href="#collections-info-tab" data-toggle="tab") Info
11
+ %li
12
+ %a#collections-documents(href="#collections-documents-tab" data-toggle="tab") Documents
13
+ %li
14
+ %a#collections-indexes(href="#collections-indexes-tab" data-toggle="tab") Indexes
15
+
16
+ #collections-tab-contents.tab-content
17
+ .tab-pane.fade#collections-info-tab
18
+ %span5.well
19
+ %pre
20
+ =pretty_json(@collection.stats)
21
+ .pull-right
22
+ .span4
23
+ %button#rename-collection-button.btn.btn-medium.btn-info( data-toggle="modal" data-target="#rename-collection-dialog" data-collection-name="#{h @collection.name}" data-db-name="#{h current_db.name}")
24
+ RENAME
25
+ .span1
26
+ .span4
27
+ %button#delete-collection-button.btn.btn-medium.btn-danger( data-toggle="modal" data-target="#confirm-collection-deletion" data-collection-name="#{h @collection.name}" data-db-name="#{h current_db.name}")
28
+ DELETE
29
+ .tab-pane.fade#collections-documents-tab
30
+ =partial 'collections/documents', :locals=>{:db=>current_db, :collection=>@collection}
31
+ .tab-pane.fade#collections-indexes-tab
32
+ =partial 'collections/indexes', :locals=>{:db=>current_db, :collection=>@collection}
33
+
34
+ .row
35
+ - else
36
+ Unknown collection: #{params[:name]}
37
+
38
+ #rename-collection-dialog.modal(style="display:none;")
39
+ .modal-header
40
+ %a.close( href='#' data-dismiss='modal') ×
41
+ %h3
42
+ Rename:
43
+ %span.old_collection_name
44
+ .modal-body
45
+ %form.primary.form-horizontal{:method => "post", :action => "#"}
46
+ %input#verb-type{:type => "hidden", :name => "_method", :value => "put"}
47
+ %fieldset
48
+ .control-group
49
+ %label.control-label( for="input-name") New name:
50
+ .controls
51
+ %input.collection-name.input-xlarge( type="text" name="collection_new_name" style="height: 28px;")
52
+ %p.help-block please enter the new name of this collection.
53
+ .form-actions.modal-footer
54
+ %button#btn-create.btn.btn-medium.btn-danger.closeModal( type="submit")
55
+ RENAME
56
+ %button.btn.btn-medium.btn-success( data-dismiss = 'modal')
57
+ CANCEL
58
+
59
+ #confirm-collection-deletion.modal.fit-content( style="display:none;")
60
+ %button.close( data-dismiss='modal') x
61
+ .modal-header
62
+ %h2.no-wrap
63
+ Delete:
64
+ %span#delete-collection-dialog-title
65
+ ?
66
+ .modal-body
67
+ %p
68
+ %h3
69
+ You will delete all the documents in this collection.
70
+ This cannot be undone.
71
+ .modal-footer
72
+ %form.primary{:method => "post", :action => "#"}
73
+ %input{:type => "hidden", :name => "_method", :value => "delete"}
74
+ %input.collection-name{:type => "hidden", :name => "collection_name", :value => ""}
75
+ %button.btn.btn-medium.btn-danger.span2.closeModal( type="submit")
76
+ DELETE
77
+ #cancel.btn.btn-medium.btn-success.span3( data-dismiss = 'modal')
78
+ CANCEL
79
+
80
+ %script{ :type => "text/javascript", :src =>"/js/collection-tools.js"}
@@ -0,0 +1,27 @@
1
+ %p
2
+ -#%h6 database users
3
+ -unless users.count == 0
4
+ %table.table.table-striped.table-condensed
5
+ %thead
6
+ %tr
7
+ %th(width='75%' valign='top')
8
+ User name
9
+ %th(width='15%')
10
+ r/o
11
+ %th(width='10%')
12
+
13
+ %tbody
14
+ -users.each do |u|
15
+ %tr
16
+ %td
17
+ %a{:href => "#"}
18
+ =u['user']
19
+ %td
20
+ %span.pull-right
21
+ =u['readOnly'] ? '✓': '-'
22
+ %td(nowrap)
23
+ %a.delete-user(href="#" data-db-name="#{h db_name}"data-id="#{h u['_id']}" data-user-name="#{h u['user']}")
24
+ %i.icon-remove
25
+ -else
26
+ %h4 no users
27
+
@@ -0,0 +1,80 @@
1
+ %button.btn.btn-medium.btn-primary.disabled
2
+ db: #{@db.name}
3
+
4
+ .row
5
+ .span5.well.well-small
6
+ %h3 Info
7
+ %pre
8
+ =pretty_json(@db.stats)
9
+ .pull-right
10
+ Delete this database?
11
+ %button.btn.btn-medium.btn-danger( data-toggle="modal" data-target="#confirm-db-deletion")
12
+ DELETE
13
+ .span4
14
+ -# http://learnmongo.com/posts/quick-tip-mongodb-users/
15
+ = partial :'databases/list_users', :locals => {:users=>@db.users, :db_name => @db.name}
16
+ %button#add-user.btn.btn-small.btn-info( data-toggle="modal" data-target="#user-dialog")
17
+ Add a user
18
+ .row
19
+
20
+ #user-dialog.modal.fit-content( style="display:none;")
21
+ %button.close( data-dismiss='modal') x
22
+ .modal-header
23
+ %h2#title Add user
24
+ .modal-body
25
+ %form.form-horizontal{:method => "post", :action => "/databases/#{@db.name}/users"}
26
+ %input{:type => "hidden", :name => "_method", :value => "post"}
27
+ %fieldset
28
+ .control-group
29
+ %label.control-label( for="input-name") Name:
30
+ .controls
31
+ %input#input-name.input-xlarge( type="text" name="username" style="height: 28px;")
32
+ %p.help-block please enter the user name
33
+ .control-group
34
+ %label.control-label( for="input-password") Password:
35
+ .controls
36
+ %input#input-password.input-xlarge( type="password" name="password" style="height: 28px;")
37
+ %p.help-block please enter the user password
38
+ .control-group
39
+ %label.control-label( for='is-read-only')
40
+ .controls
41
+ %label.checkbox.inline
42
+ %input#is-read-only{ :type=>'checkbox', :name=>'readonly'} Read-only?
43
+ .form-actions.modal-footer
44
+ %button.btn.btn-medium.btn-danger( data-dismiss = 'modal')
45
+ CANCEL
46
+ %button#btn-create.btn.btn-medium.btn-primary.closeModal( type="submit")
47
+ SAVE USER
48
+
49
+ #confirm-db-deletion.modal.fit-content( style="display:none;")
50
+ %button.close( data-dismiss='modal') x
51
+ .modal-header
52
+ %h2 Delete '#{@db.name}'?
53
+ .modal-body
54
+ %p This cannot be undone.
55
+ .modal-footer
56
+ %form{:method => "post", :action => "/databases/#{@db.name}"}
57
+ %input{:type => "hidden", :name => "_method", :value => "delete"}
58
+ #cancel.btn.btn-medium.btn-success.span3( data-dismiss = 'modal')
59
+ CANCEL
60
+ %button.btn.btn-medium.btn-danger.span2.closeModal( type="submit")
61
+ DELETE
62
+
63
+ #confirm-delete-user.modal.fit-content(style="display:none;")
64
+ .modal-header
65
+ %h1#delete-dialog-title Delete user
66
+ .modal-body
67
+ #user-name
68
+ %p
69
+ %h5
70
+ Warning, this operation cannot be undone! Are you sure you want to delete this user?
71
+ .modal-footer
72
+ %form.primary{:method => "post", :action => ""}
73
+ %input{:type => "hidden", :name => "_method", :value => "delete"}
74
+ %input.username{:type => "hidden", :name => "username", :value => ""}
75
+ %button.btn.btn-medium.btn-danger.span2.closeModal( type="submit")
76
+ DELETE
77
+ #cancel.btn.btn-medium.btn-success.span3( data-dismiss = 'modal')
78
+ CANCEL
79
+
80
+ %script{ :type => "text/javascript", :src =>"/js/db-tools.js"}
@@ -0,0 +1,4 @@
1
+ .footer
2
+ %p
3
+ ©#{Time.now.year}
4
+ %a{:href => "http://simplegames.ca"} simplegames.ca
@@ -0,0 +1,7 @@
1
+ :redcarpet
2
+ ### MongoFE
3
+ - a simple MongoBD administrative front-end that helps you play and learn MongoDB or manage simple MongoDB administrative tasks.
4
+
5
+ ### Description
6
+ This is a simple Sinatra based web front-end that can be used for experimenting and learning MongoDB. The MongoFe gem can also be used for simple administrative tasks, managing collections and document basic operations such as: create new documents, delete existing ones, search by various criteria and document indexing.
7
+
@@ -0,0 +1,148 @@
1
+ !!!
2
+ %html
3
+ %head
4
+ %meta{:charset => "utf-8"}
5
+ %title MongoFE - A MongoDB simple administrative front-end
6
+ %meta{:name => "description", :content => "A very simple yet versatile MongoDB administrative user interface"}
7
+ %meta{:name => "author", :content => "Florin T.PATRASCU"}
8
+ :plain
9
+ <!-- Le HTML5 shim, for IE6-8 support of HTML elements -->
10
+ <!--[if lt IE 9]>
11
+ <script src="http://html5shim.googlecode.com/svn/trunk/html5.js"></script>
12
+ <![endif]-->
13
+
14
+ / Le styles
15
+ %link{ :href => "/bootstrap/css/bootstrap.css", :type => "text/css", :rel => "stylesheet" }
16
+ / add body padding styles
17
+ %link{ :href => "/css/styles.css", :type => "text/css", :rel => "stylesheet" }
18
+ %link{ :href => "/css/digg_pagination.css", :type => "text/css", :rel => "stylesheet" }
19
+
20
+ -# JSON Editor, the CSS
21
+ %link{ :href => "/css/jsoneditor.css", :type => "text/css", :rel => "stylesheet" }
22
+ %link{ :href => "/bootstrap/css/bootstrap-responsive.css", :type => "text/css", :rel => "stylesheet" }
23
+
24
+ / Le fav and touch icons
25
+ %link{ rel: "apple-touch-icon", href: "/bootstrap/images/apple-touch-icon.png"}
26
+ %link{ rel: "shortcut icon", href: "/bootstrap/images/favicon.ico"}
27
+ %link{ rel: "apple-touch-icon", sizes: "72x72", href: "/bootstrap/images/apple-touch-icon-72x72.png"}
28
+ %link{ rel: "apple-touch-icon", sizes: "114x114", href: "/bootstrap/images/apple-touch-icon-114x114.png"}
29
+
30
+ %script{ :type => "text/javascript", :src =>"/js/jsoneditor/json2.js"}
31
+ %script{ :type => "text/javascript", :src =>"/bootstrap/js/jquery.js"}
32
+ %script{ :type => "text/javascript", :src =>"/js/jsoneditor/jquery.jsoneditor.min.js"}
33
+
34
+
35
+ %body
36
+
37
+ %div= haml :navbar
38
+ %div
39
+ .container-fluid
40
+ .row-fluid
41
+ .span3
42
+ .well.sidebar-nav
43
+ %ul.nav-list.nav
44
+ %li.nav-header
45
+ %span
46
+ Databases
47
+ %a#new-db(href="#" data-toggle="modal" data-target="#new-db-dialog")
48
+ %i.icon-plus-sign
49
+ - dbs = MongoFe::MongoDB.available_databases
50
+ - unless dbs.nil?
51
+ - dbs.each do |d|
52
+ %li
53
+ %a{:href => "/databases/#{h d}"}
54
+ -if current_db? && d == current_db.name
55
+ %i.icon-ok-circle
56
+ -else
57
+ %i.icon-minus.icon-white
58
+ #{d}
59
+ -if current_db?
60
+ %li.nav-header
61
+ %span
62
+ Collections
63
+ -if current_db?
64
+ %a#add-collection( data-target="#new-collection-dialog" href="#" data-toggle="modal" data-db-name="#{h current_db.name}")
65
+ %i.icon-plus-sign
66
+ - collections = current_db.collection_names - %w[system.users system.indexes]
67
+ - unless collections.nil?
68
+ - collections.each do |c|
69
+ %li
70
+ %a{:href => "/databases/#{h current_db.name}/collections/#{h c}"}
71
+ -if !collection.nil? && c == collection.name
72
+ %i.icon-ok-circle
73
+ -else
74
+ %i.icon-minus.icon-white
75
+ #{c}
76
+ - else
77
+ %li empty
78
+ .span9
79
+ - flash.each do |name, msg|
80
+ .alert.fade.in{:class => "alert alert-#{name}", "data-alert" => "alert"}
81
+ %a.close{"data-dismiss" => "alert", :href => "#"}
82
+ %i.icon-remove
83
+ %p=msg
84
+ - flash.clear if flash.count > 0
85
+
86
+ .span9
87
+ = yield
88
+ %br
89
+ %hr
90
+ %h6= haml :footer
91
+
92
+ -# FIXME: the next two dialogs can be one
93
+ #new-db-dialog.modal(style="display:none;")
94
+ .modal-header
95
+ %a.close( href='#' data-dismiss='modal') &times;
96
+ %h2#dialog-title New database
97
+ .modal-body
98
+ %form.form-horizontal{:method => "post", :action => "/databases"}
99
+ %input#verb-type{:type => "hidden", :name => "_method", :value => "post"}
100
+ %fieldset
101
+ .control-group
102
+ %label.control-label( for="input-name") Name:
103
+ .controls
104
+ %input#input-name.input-xlarge( type="text" name="name" style="height: 28px;")
105
+ %p.help-block please enter the name of the database.
106
+ .form-actions.modal-footer
107
+ %button#btn-create.btn.btn-medium.btn-primary.closeModal( type="submit")
108
+ CREATE
109
+ %button.btn.btn-medium.btn-danger( data-dismiss = 'modal')
110
+ CANCEL
111
+
112
+ #new-collection-dialog.modal(style="display:none;")
113
+ .modal-header
114
+ %a.close( href='#' data-dismiss='modal') &times;
115
+ %h2#dialog-title New collection
116
+ .modal-body
117
+ %form.primary.form-horizontal{:method => "post", :action => "#"}
118
+ %input#verb-type{:type => "hidden", :name => "_method", :value => "post"}
119
+ %fieldset
120
+ .control-group
121
+ %label.control-label( for="input-name") Name:
122
+ .controls
123
+ %input.collection-name.input-xlarge( type="text" name="collection_name" style="height: 28px;")
124
+ %p.help-block please enter the name of the collection.
125
+ .form-actions.modal-footer
126
+ %button#btn-create.btn.btn-medium.btn-primary.closeModal( type="submit")
127
+ CREATE
128
+ %button.btn.btn-medium.btn-danger( data-dismiss = 'modal')
129
+ CANCEL
130
+
131
+ / Placed at the end of the document so the pages load faster
132
+ %script{ :type => "text/javascript", :src =>"/bootstrap/js/basic/bootstrap-transition.js"}
133
+ %script{ :type => "text/javascript", :src =>"/bootstrap/js/basic/bootstrap-alert.js"}
134
+ %script{ :type => "text/javascript", :src =>"/bootstrap/js/basic/bootstrap-modal.js"}
135
+ %script{ :type => "text/javascript", :src =>"/bootstrap/js/basic/bootstrap-dropdown.js"}
136
+ %script{ :type => "text/javascript", :src =>"/bootstrap/js/basic/bootstrap-scrollspy.js"}
137
+ %script{ :type => "text/javascript", :src =>"/bootstrap/js/basic/bootstrap-tab.js"}
138
+ %script{ :type => "text/javascript", :src =>"/bootstrap/js/basic/bootstrap-tooltip.js"}
139
+ %script{ :type => "text/javascript", :src =>"/bootstrap/js/basic/bootstrap-popover.js"}
140
+ %script{ :type => "text/javascript", :src =>"/bootstrap/js/basic/bootstrap-button.js"}
141
+ %script{ :type => "text/javascript", :src =>"/bootstrap/js/basic/bootstrap-collapse.js"}
142
+ %script{ :type => "text/javascript", :src =>"/bootstrap/js/basic/bootstrap-carousel.js"}
143
+ %script{ :type => "text/javascript", :src =>"/bootstrap/js/basic/bootstrap-typeahead.js"}
144
+ %script{ :type => "text/javascript", :src =>"/bootstrap/js/underscore-min.js"}
145
+
146
+ %script{ :type => "text/javascript", :src =>"/app/application.js"}
147
+ %script{ :type => "text/javascript", :src =>"/js/db-tools.js"}
148
+
@@ -0,0 +1,17 @@
1
+ .navbar.navbar-fixed-top
2
+ .navbar-inner
3
+ .container-fluid
4
+ %a.btn.btn-navbar{ "data-toggle".to_sym => "collapse", "data-target".to_sym => ".nav-collapse"}
5
+ %span.icon-bar
6
+ %span.icon-bar
7
+ %span.icon-bar
8
+ %a.brand{:href => '/'} MongoFE
9
+ .nav-collapse
10
+ %p.navbar-text.pull-right
11
+ -if current_db?
12
+ =current_db.name
13
+ - if collection?
14
+ |
15
+ =collection.name
16
+ -else
17
+ database|collection
data/lib/mongo_fe.rb ADDED
@@ -0,0 +1,170 @@
1
+ # encoding=utf-8
2
+
3
+ require File.dirname(__FILE__) + '/mongo_fe/version'
4
+ require "mongo" # API: http://api.mongodb.org/ruby/1.6.4/
5
+ require "uri"
6
+
7
+ module MongoFe
8
+
9
+ module MongoDB
10
+ @@user_db = nil
11
+
12
+ # simple proxy to Mongo::DB
13
+ class Database
14
+ instance_methods.each { |m| undef_method m unless m =~ /(^__|^send$|^object_id$)/ }
15
+
16
+ def initialize(db)
17
+ @db=db
18
+ end
19
+
20
+ def users
21
+ if db_users = @db[Mongo::DB::SYSTEM_USER_COLLECTION]
22
+ db_users.find.to_a
23
+ end
24
+ end
25
+
26
+ def find_user(username=nil)
27
+ if db_users = @db[Mongo::DB::SYSTEM_USER_COLLECTION]
28
+ db_users.find_one({:user => username}) unless username.nil?
29
+ end
30
+ end
31
+
32
+ # todo: Add more convenient methods
33
+
34
+ protected
35
+ def method_missing(name, *args, &block)
36
+ @db.send(name, *args, &block)
37
+ end
38
+ end
39
+
40
+ class SearchDocuments
41
+ def initialize(a_db, a_collection)
42
+ @db=a_db
43
+ @collection=a_collection
44
+ end
45
+
46
+ def list(user_query, page=1, per_page=10)
47
+ page_nr = page > 0 ? page : 1
48
+ query = user_query.nil? ? [{}, [], []] : user_query
49
+
50
+ q = query.first.dup
51
+
52
+ # verify if we have query containing a regexp
53
+ q.each_pair do |k, v|
54
+ if v.is_a?(String) && v.index(/^\//)
55
+ q[k] = Regexp.new(v.gsub("/", ''))
56
+ end
57
+ end
58
+
59
+ # quickly find the total hits
60
+ count = @db.collection(@collection.name).find(q).count
61
+
62
+ # get the docs and paginate.
63
+ documents_list = WillPaginate::Collection.create(page_nr, per_page, count) do |pager|
64
+ results = @collection.find( q,
65
+ :sort => sort_by(query.last),
66
+ :skip => ((page_nr-1) * per_page).to_i,
67
+ :limit => per_page,
68
+ :fields => just_these_fields(query[1])
69
+ ).to_a
70
+ pager.replace(results)
71
+ end
72
+
73
+ [count, documents_list]
74
+ end
75
+
76
+ private #---------------- private stuff
77
+
78
+ # @param [String] criteria_string
79
+ def sort_by(criteria_string)
80
+ criteria_string.empty? ? [['_id', Mongo::DESCENDING]] : MongoFe::Utils.split_index_specs(criteria_string)
81
+ end
82
+
83
+ # Retrieving a Subset of Fields info here: http://bit.ly/P2ZLiV
84
+ def just_these_fields(fields_string)
85
+ fields = nil #all
86
+
87
+ unless fields_string.nil?
88
+ fields_string = fields_string.delete(' ')
89
+ unless fields_string.nil?
90
+ fields = []
91
+ fields_string.split(/[\s,]+/).each do |f|
92
+ fields << f.to_sym unless f.nil?
93
+ end
94
+ end
95
+ end
96
+
97
+ fields
98
+ end
99
+ end
100
+
101
+ def self.use(db_name=nil)
102
+ if !@@user_db.nil? && db_name != @@user_db
103
+ raise 'Invalid database name. Not authorized, maybe?!'
104
+ end
105
+
106
+ Database.new(self.connection.db(db_name))
107
+ end
108
+
109
+ def self.uri
110
+ @@uri = nil unless defined?(@@uri)
111
+ @@uri
112
+ end
113
+
114
+ def self.uri=(uri)
115
+ begin
116
+ @@uri = URI.parse(uri)
117
+ @@host = @@uri.host
118
+ @@port = @@uri.port
119
+ rescue => e
120
+ raise "#{e.message}; #{uri}"
121
+ end
122
+ end
123
+
124
+ def self.connection
125
+ begin
126
+ unless defined?(@@connection)
127
+ uri = @@uri.to_s
128
+ @@connection = Mongo::Connection.from_uri(uri)
129
+ if @@uri.user && @@uri.password
130
+ if !@@uri.path.nil?
131
+ @@user_db = @@uri.path.gsub(/^\//, '')
132
+ @@connection.add_auth(@@user_db, @@uri.user, @@uri.password)
133
+ else
134
+ raise "username and password provided but the db name is missing. Please verify the config uri."
135
+ end
136
+ end
137
+ end
138
+
139
+ @@connection
140
+ rescue Mongo::ConnectionFailure => e
141
+ raise "Please verify that you have a MongoDB started at: #{@@host}:#{@@port} error: #{e.message}"
142
+ end
143
+ end
144
+
145
+ def self.available_databases
146
+ if @@user_db
147
+ [@@user_db]
148
+ else
149
+ self.connection.database_names - %w[admin local slave]
150
+ end
151
+ end
152
+ end
153
+
154
+ class Utils
155
+
156
+ def self.split_index_specs(fields=nil)
157
+ specs = []
158
+
159
+ unless fields.empty?
160
+ fields.delete(' ').split(/[\s,]+/).each do |s|
161
+ f= s.split(/[\s:]+/)
162
+ specs << [f[0].to_sym, f[1].downcase.start_with?('asc') ? Mongo::ASCENDING : Mongo::DESCENDING]
163
+ end
164
+ end
165
+
166
+ specs
167
+ end
168
+ end
169
+
170
+ end
data/mongo_fe.gemspec ADDED
@@ -0,0 +1,50 @@
1
+ # -*- encoding: utf-8 -*-
2
+ require File.expand_path('../lib/mongo_fe/version', __FILE__)
3
+
4
+ Gem::Specification.new do |gem|
5
+ gem.name = "mongo_fe"
6
+ gem.version = MongoFe::VERSION
7
+ gem.authors = ["Florin T.PATRASCU"]
8
+ gem.email = %W(florin.patrascu@gmail.com)
9
+ gem.summary = %q{Play and learn MongoDB or manage simple MongoDB administrative tasks}
10
+ gem.description = %q{A simple Sinatra based web front-end that can be used for experimenting and learning MongoDB. The MongoFe gem can also be used for simple administrative tasks, managing collections and document basic operations such as: create new documents, delete existing ones, search by various criteria and document indexing.}
11
+ gem.homepage = ""
12
+
13
+ gem.files = `git ls-files`.split($\)
14
+ gem.executables = gem.files.grep(%r{^bin/}).map { |f| File.basename(f) }
15
+ gem.test_files = gem.files.grep(%r{^(test|spec|features)/})
16
+ gem.require_paths = %W(lib)
17
+ gem.extra_rdoc_files = %w(LICENSE README.md)
18
+
19
+ # Gem dependencies for runtime
20
+ gem.add_runtime_dependency "hashie"
21
+ gem.add_runtime_dependency "haml", "~> 3.1.4"
22
+ gem.add_runtime_dependency "vegas", "~> 0.1.11"
23
+ gem.add_runtime_dependency "chronic", "~> 0.6.7"
24
+ gem.add_runtime_dependency "mongo", "~> 1.6.2"
25
+ gem.add_runtime_dependency "yajl-ruby"
26
+ gem.add_runtime_dependency "bson"
27
+ gem.add_runtime_dependency "bson_ext"
28
+ gem.add_runtime_dependency "json"
29
+ gem.add_runtime_dependency 'will_paginate', '>= 3.0.0'
30
+ gem.add_runtime_dependency 'will_paginate-bootstrap'
31
+ gem.add_runtime_dependency "sinatra", "~> 1.3.2"
32
+ gem.add_runtime_dependency "sinatra-contrib", "~> 1.3.1"
33
+ gem.add_runtime_dependency 'redcarpet', '~> 2.1.0'
34
+ gem.add_runtime_dependency 'coderay', '~> 1.0.5'
35
+ gem.add_runtime_dependency 'tilt', '~> 1.3.0'
36
+
37
+ # Gem dependencies for development
38
+ gem.add_development_dependency "rspec", "~> 2.9"
39
+ gem.add_development_dependency 'bundler', '~> 1.1.0'
40
+ gem.add_development_dependency 'appraisal', '~> 0.4.1'
41
+ gem.add_development_dependency 'cucumber'
42
+ gem.add_development_dependency 'rspec', '~> 2.9.0'
43
+ gem.add_development_dependency 'capybara', '~> 1.1.2'
44
+ gem.add_development_dependency 'webrat', '~> 0.7.3'
45
+ gem.add_development_dependency 'shoulda-matchers', '~> 1.1.0'
46
+ gem.add_development_dependency "factory_girl", "~> 3.0"
47
+ gem.add_development_dependency "simplecov", ">=0.4.2"
48
+ gem.add_development_dependency "logger"
49
+
50
+ end
Binary file
Binary file
Binary file