oculus 0.5.0 → 0.8.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.
- data/.travis.yml +1 -0
- data/Rakefile +25 -0
- data/TODO.md +0 -1
- data/features/step_definitions/query_steps.rb +5 -1
- data/features/support/env.rb +2 -1
- data/lib/oculus/presenters/query_presenter.rb +28 -2
- data/lib/oculus/query.rb +36 -7
- data/lib/oculus/server/public/css/style.css +12 -0
- data/lib/oculus/server/public/js/application.js +16 -0
- data/lib/oculus/server/views/history.erb +1 -7
- data/lib/oculus/server/views/layout.erb +3 -0
- data/lib/oculus/server/views/show.erb +46 -18
- data/lib/oculus/server/views/starred.erb +29 -0
- data/lib/oculus/server.rb +23 -6
- data/lib/oculus/storage/file_store.rb +28 -5
- data/lib/oculus/version.rb +1 -1
- data/spec/connection_spec.rb +2 -21
- data/spec/file_store_spec.rb +45 -20
- data/spec/query_presenter_spec.rb +14 -3
- data/spec/query_spec.rb +60 -16
- metadata +17 -16
    
        data/.travis.yml
    CHANGED
    
    
    
        data/Rakefile
    CHANGED
    
    | @@ -3,6 +3,7 @@ require "bundler/gem_tasks" | |
| 3 3 |  | 
| 4 4 | 
             
            require 'rspec/core/rake_task'
         | 
| 5 5 | 
             
            require 'cucumber/rake/task'
         | 
| 6 | 
            +
            require 'mysql2'
         | 
| 6 7 |  | 
| 7 8 | 
             
            desc 'Run RSpec tests'
         | 
| 8 9 | 
             
            RSpec::Core::RakeTask.new(:spec) do |task|
         | 
| @@ -15,4 +16,28 @@ Cucumber::Rake::Task.new(:cucumber) do |task| | |
| 15 16 | 
             
              task.cucumber_opts = 'features --format pretty'
         | 
| 16 17 | 
             
            end
         | 
| 17 18 |  | 
| 19 | 
            +
            namespace :db do
         | 
| 20 | 
            +
              namespace :test do
         | 
| 21 | 
            +
                desc "Populate the test database"
         | 
| 22 | 
            +
                task :populate do
         | 
| 23 | 
            +
                  client = Mysql2::Client.new(:host => "localhost", :username => "root")
         | 
| 24 | 
            +
                  client.query "CREATE DATABASE IF NOT EXISTS oculus_test"
         | 
| 25 | 
            +
                  client.query "USE oculus_test"
         | 
| 26 | 
            +
                  client.query %[
         | 
| 27 | 
            +
                    CREATE TABLE IF NOT EXISTS oculus_users (
         | 
| 28 | 
            +
                      id MEDIUMINT NOT NULL AUTO_INCREMENT,
         | 
| 29 | 
            +
                      name VARCHAR(255),
         | 
| 30 | 
            +
                      PRIMARY KEY (id)
         | 
| 31 | 
            +
                    );
         | 
| 32 | 
            +
                  ]
         | 
| 33 | 
            +
             | 
| 34 | 
            +
                  client.query 'TRUNCATE oculus_users'
         | 
| 35 | 
            +
             | 
| 36 | 
            +
                  client.query %[
         | 
| 37 | 
            +
                    INSERT INTO oculus_users (name) VALUES ('Paul'), ('Amy'), ('Peter')
         | 
| 38 | 
            +
                  ]
         | 
| 39 | 
            +
                end
         | 
| 40 | 
            +
              end
         | 
| 41 | 
            +
            end
         | 
| 42 | 
            +
             | 
| 18 43 | 
             
            task :default => [:spec, :cucumber]
         | 
    
        data/TODO.md
    CHANGED
    
    
| @@ -1,5 +1,9 @@ | |
| 1 1 | 
             
            Given /^a query is cached with results:$/ do |results|
         | 
| 2 | 
            -
              Oculus::Query.create(:name | 
| 2 | 
            +
              Oculus::Query.create(:name        => "all users",
         | 
| 3 | 
            +
                                   :query       => "SELECT * FROM oculus_users",
         | 
| 4 | 
            +
                                   :results     => results.raw,
         | 
| 5 | 
            +
                                   :started_at  => Time.now,
         | 
| 6 | 
            +
                                   :finished_at => Time.now)
         | 
| 3 7 | 
             
            end
         | 
| 4 8 |  | 
| 5 9 | 
             
            Given /^I am on the history page$/ do
         | 
    
        data/features/support/env.rb
    CHANGED
    
    | @@ -5,12 +5,13 @@ require 'oculus/server' | |
| 5 5 | 
             
            require 'capybara/cucumber'
         | 
| 6 6 |  | 
| 7 7 | 
             
            Capybara.app = Oculus::Server
         | 
| 8 | 
            +
            Capybara.default_wait_time = 10
         | 
| 8 9 |  | 
| 9 10 | 
             
            Oculus.cache_path = 'tmp/test_cache'
         | 
| 10 11 | 
             
            Oculus.connection_options = {
         | 
| 11 12 | 
             
              :host => 'localhost',
         | 
| 12 13 | 
             
              :username => 'root',
         | 
| 13 | 
            -
              :database => ' | 
| 14 | 
            +
              :database => 'oculus_test'
         | 
| 14 15 | 
             
            }
         | 
| 15 16 |  | 
| 16 17 | 
             
            Before do
         | 
| @@ -3,8 +3,34 @@ require 'delegate' | |
| 3 3 | 
             
            module Oculus
         | 
| 4 4 | 
             
              module Presenters
         | 
| 5 5 | 
             
                class QueryPresenter < SimpleDelegator
         | 
| 6 | 
            -
                  def  | 
| 7 | 
            -
                     | 
| 6 | 
            +
                  def formatted_start_time
         | 
| 7 | 
            +
                    started_at.strftime("%Y-%m-%d %I:%M %p") if started_at
         | 
| 8 | 
            +
                  end
         | 
| 9 | 
            +
             | 
| 10 | 
            +
                  def formatted_finish_time
         | 
| 11 | 
            +
                    finished_at.strftime("%Y-%m-%d %I:%M %p") if finished_at
         | 
| 12 | 
            +
                  end
         | 
| 13 | 
            +
             | 
| 14 | 
            +
                  def elapsed_time
         | 
| 15 | 
            +
                    return "" unless started_at && finished_at
         | 
| 16 | 
            +
             | 
| 17 | 
            +
                    seconds = (finished_at - started_at).round
         | 
| 18 | 
            +
             | 
| 19 | 
            +
                    if seconds < 60
         | 
| 20 | 
            +
                      "#{seconds} seconds"
         | 
| 21 | 
            +
                    else
         | 
| 22 | 
            +
                      minutes = (seconds / 60).floor
         | 
| 23 | 
            +
                      seconds %= 60
         | 
| 24 | 
            +
             | 
| 25 | 
            +
                      if minutes < 60
         | 
| 26 | 
            +
                        "#{minutes} minutes #{seconds} seconds"
         | 
| 27 | 
            +
                      else
         | 
| 28 | 
            +
                        hours = (minutes / 60).floor
         | 
| 29 | 
            +
                        minutes %= 60
         | 
| 30 | 
            +
             | 
| 31 | 
            +
                        "#{hours} hours #{minutes} minutes"
         | 
| 32 | 
            +
                      end
         | 
| 33 | 
            +
                    end
         | 
| 8 34 | 
             
                  end
         | 
| 9 35 |  | 
| 10 36 | 
             
                  def status
         | 
    
        data/lib/oculus/query.rb
    CHANGED
    
    | @@ -6,7 +6,9 @@ module Oculus | |
| 6 6 | 
             
                attr_accessor :query
         | 
| 7 7 | 
             
                attr_accessor :results
         | 
| 8 8 | 
             
                attr_accessor :error
         | 
| 9 | 
            -
                attr_accessor : | 
| 9 | 
            +
                attr_accessor :started_at
         | 
| 10 | 
            +
                attr_accessor :finished_at
         | 
| 11 | 
            +
                attr_accessor :starred
         | 
| 10 12 | 
             
                attr_accessor :thread_id
         | 
| 11 13 |  | 
| 12 14 | 
             
                def initialize(attributes = {})
         | 
| @@ -20,7 +22,9 @@ module Oculus | |
| 20 22 | 
             
                    :name        => name,
         | 
| 21 23 | 
             
                    :author      => author,
         | 
| 22 24 | 
             
                    :query       => query,
         | 
| 23 | 
            -
                    : | 
| 25 | 
            +
                    :started_at  => started_at,
         | 
| 26 | 
            +
                    :finished_at => finished_at,
         | 
| 27 | 
            +
                    :starred     => starred,
         | 
| 24 28 | 
             
                    :thread_id   => thread_id
         | 
| 25 29 | 
             
                  }
         | 
| 26 30 | 
             
                  attrs[:error] = error if error
         | 
| @@ -28,24 +32,40 @@ module Oculus | |
| 28 32 | 
             
                end
         | 
| 29 33 |  | 
| 30 34 | 
             
                def execute(connection)
         | 
| 31 | 
            -
                  self. | 
| 35 | 
            +
                  self.started_at = Time.now
         | 
| 36 | 
            +
                  self.thread_id  = connection.thread_id
         | 
| 37 | 
            +
                  self.save
         | 
| 38 | 
            +
                  results = connection.execute(query)
         | 
| 32 39 | 
             
                rescue Connection::Error => e
         | 
| 33 | 
            -
                   | 
| 40 | 
            +
                  error = e.message
         | 
| 41 | 
            +
                ensure
         | 
| 42 | 
            +
                  reload
         | 
| 43 | 
            +
                  self.results = results if results
         | 
| 44 | 
            +
                  self.error   = error   if error
         | 
| 45 | 
            +
                  self.finished_at = Time.now
         | 
| 46 | 
            +
                  self.save
         | 
| 34 47 | 
             
                end
         | 
| 35 48 |  | 
| 36 49 | 
             
                def save
         | 
| 37 | 
            -
                  @date = Time.now
         | 
| 38 50 | 
             
                  Oculus.data_store.save_query(self)
         | 
| 39 51 | 
             
                end
         | 
| 40 52 |  | 
| 41 53 | 
             
                def complete?
         | 
| 42 | 
            -
                  !! | 
| 54 | 
            +
                  !!finished_at
         | 
| 43 55 | 
             
                end
         | 
| 44 56 |  | 
| 45 57 | 
             
                def succeeded?
         | 
| 46 58 | 
             
                  complete? && !error
         | 
| 47 59 | 
             
                end
         | 
| 48 60 |  | 
| 61 | 
            +
                def to_csv
         | 
| 62 | 
            +
                  CSV.generate do |csv|
         | 
| 63 | 
            +
                    results.each do |row|
         | 
| 64 | 
            +
                      csv << row
         | 
| 65 | 
            +
                    end
         | 
| 66 | 
            +
                  end
         | 
| 67 | 
            +
                end
         | 
| 68 | 
            +
             | 
| 49 69 | 
             
                class << self
         | 
| 50 70 | 
             
                  def create(attributes)
         | 
| 51 71 | 
             
                    query = new(attributes)
         | 
| @@ -54,8 +74,17 @@ module Oculus | |
| 54 74 | 
             
                  end
         | 
| 55 75 |  | 
| 56 76 | 
             
                  def find(id)
         | 
| 57 | 
            -
                    Oculus.data_store.load_query(id)
         | 
| 77 | 
            +
                    new(Oculus.data_store.load_query(id))
         | 
| 58 78 | 
             
                  end
         | 
| 59 79 | 
             
                end
         | 
| 80 | 
            +
             | 
| 81 | 
            +
                private
         | 
| 82 | 
            +
             | 
| 83 | 
            +
                def reload
         | 
| 84 | 
            +
                  Oculus.data_store.load_query(id).each do |attr, value|
         | 
| 85 | 
            +
                    send("#{attr}=", value)
         | 
| 86 | 
            +
                  end
         | 
| 87 | 
            +
                end
         | 
| 88 | 
            +
             | 
| 60 89 | 
             
              end
         | 
| 61 90 | 
             
            end
         | 
| @@ -102,6 +102,18 @@ body { | |
| 102 102 | 
             
              margin-top: 1em;
         | 
| 103 103 | 
             
            }
         | 
| 104 104 |  | 
| 105 | 
            +
            button.star .starred-contents {
         | 
| 106 | 
            +
              display: none;
         | 
| 107 | 
            +
            }
         | 
| 108 | 
            +
             | 
| 109 | 
            +
            button.starred .starred-contents {
         | 
| 110 | 
            +
              display: inline;
         | 
| 111 | 
            +
            }
         | 
| 112 | 
            +
             | 
| 113 | 
            +
            button.starred .unstarred-contents {
         | 
| 114 | 
            +
              display: none;
         | 
| 115 | 
            +
            }
         | 
| 116 | 
            +
             | 
| 105 117 | 
             
            #query-properties-form {
         | 
| 106 118 | 
             
              display: none;
         | 
| 107 119 | 
             
            }
         | 
| @@ -32,6 +32,22 @@ $(function() { | |
| 32 32 | 
             
                });
         | 
| 33 33 | 
             
              });
         | 
| 34 34 |  | 
| 35 | 
            +
              $('#query-actions .star').click(function() {
         | 
| 36 | 
            +
                var btn = $(this),
         | 
| 37 | 
            +
                    newState = !btn.hasClass('starred');
         | 
| 38 | 
            +
             | 
| 39 | 
            +
                $.ajax({
         | 
| 40 | 
            +
                  url: this.href,
         | 
| 41 | 
            +
                  type: 'PUT',
         | 
| 42 | 
            +
                  data: {
         | 
| 43 | 
            +
                    'starred': newState
         | 
| 44 | 
            +
                  },
         | 
| 45 | 
            +
                  success: function() {
         | 
| 46 | 
            +
                    btn.toggleClass('starred', newState);
         | 
| 47 | 
            +
                  }
         | 
| 48 | 
            +
                });
         | 
| 49 | 
            +
              });
         | 
| 50 | 
            +
             | 
| 35 51 | 
             
              $('#query-actions .edit').click(function() {
         | 
| 36 52 | 
             
                $('#query-properties-form').toggle();
         | 
| 37 53 | 
             
              });
         | 
| @@ -13,7 +13,7 @@ | |
| 13 13 | 
             
                <% @queries.each do |query| %>
         | 
| 14 14 | 
             
                  <tr>
         | 
| 15 15 | 
             
                    <td class="status"><span class="indicator indicator-<%= query.status %>">*</span></td>
         | 
| 16 | 
            -
                    <td class="date"><%= query. | 
| 16 | 
            +
                    <td class="date"><%= query.formatted_start_time %></td>
         | 
| 17 17 | 
             
                    <td>
         | 
| 18 18 | 
             
                      <a href="/queries/<%= query.id %>"><%= query.description %></a>
         | 
| 19 19 | 
             
                    </td>
         | 
| @@ -27,9 +27,3 @@ | |
| 27 27 | 
             
                <% end %>
         | 
| 28 28 | 
             
              </tbody>
         | 
| 29 29 | 
             
            </table>
         | 
| 30 | 
            -
             | 
| 31 | 
            -
            <script type="text/javascript">
         | 
| 32 | 
            -
              CodeMirror.fromTextArea(document.getElementById('query-field'), {
         | 
| 33 | 
            -
                tabSize: 2
         | 
| 34 | 
            -
              });
         | 
| 35 | 
            -
            </script>
         | 
| @@ -20,6 +20,9 @@ | |
| 20 20 | 
             
                        <li<% if @current_tab == 'home' %> class="active"<% end %>>
         | 
| 21 21 | 
             
                          <a href="/">Home</a>
         | 
| 22 22 | 
             
                        </li>
         | 
| 23 | 
            +
                        <li<% if @current_tab == 'starred' %> class="active"<% end %>>
         | 
| 24 | 
            +
                          <a href="/starred">Starred</a>
         | 
| 25 | 
            +
                        </li>
         | 
| 23 26 | 
             
                        <li<% if @current_tab == 'history' %> class="active"<% end %>>
         | 
| 24 27 | 
             
                          <a href="/history">History</a>
         | 
| 25 28 | 
             
                        </li>
         | 
| @@ -1,17 +1,35 @@ | |
| 1 1 | 
             
            <h1 class="description"><%= @query.description %></h1>
         | 
| 2 2 |  | 
| 3 3 | 
             
            <div id="query-actions">
         | 
| 4 | 
            +
              <button class="btn btn-success star<% if @query.starred %> starred<% end %>">
         | 
| 5 | 
            +
                <span class="unstarred-contents">
         | 
| 6 | 
            +
                  <i class="icon-star-empty icon-white"></i>
         | 
| 7 | 
            +
                  Star
         | 
| 8 | 
            +
                </span>
         | 
| 9 | 
            +
                <span class="starred-contents">
         | 
| 10 | 
            +
                  <i class="icon-star icon-white"></i>
         | 
| 11 | 
            +
                  Starred
         | 
| 12 | 
            +
                </span>
         | 
| 13 | 
            +
              </button>
         | 
| 4 14 | 
             
              <button class="btn edit<% unless @query.named? %> active<% end %>" data-toggle="button">
         | 
| 5 15 | 
             
                <i class="icon-pencil"></i>
         | 
| 6 16 | 
             
                Edit
         | 
| 7 17 | 
             
              </button>
         | 
| 18 | 
            +
              <a class="btn file<% unless @query.succeeded? && @headers %> disabled<% end %>" href="/queries/<%= @query.id %>/download">
         | 
| 19 | 
            +
                <i class="icon-file"></i>
         | 
| 20 | 
            +
                Download
         | 
| 21 | 
            +
              </a>
         | 
| 8 22 | 
             
            </div>
         | 
| 9 23 |  | 
| 10 24 | 
             
            <dl class="dl-horizontal query-properties">
         | 
| 11 25 | 
             
              <dt>Author</dt>
         | 
| 12 26 | 
             
              <dd class="author"><%= @query.author %> </dd>
         | 
| 13 | 
            -
              <dt> | 
| 14 | 
            -
              <dd><%= @query. | 
| 27 | 
            +
              <dt>Started at</dt>
         | 
| 28 | 
            +
              <dd><%= @query.formatted_start_time %></dd>
         | 
| 29 | 
            +
              <% if @query.finished_at %>
         | 
| 30 | 
            +
              <dt>Finished at</dt>
         | 
| 31 | 
            +
              <dd><%= @query.formatted_finish_time %> (<%= @query.elapsed_time %>)</dd>
         | 
| 32 | 
            +
              <% end %>
         | 
| 15 33 | 
             
            </dl>
         | 
| 16 34 |  | 
| 17 35 | 
             
            <form class="well form-inline" id="query-properties-form">
         | 
| @@ -23,24 +41,31 @@ | |
| 23 41 | 
             
            <pre class="cm-s-default"><%= @query.query %></pre>
         | 
| 24 42 |  | 
| 25 43 | 
             
            <% if @query.succeeded? %>
         | 
| 26 | 
            -
               | 
| 27 | 
            -
                < | 
| 28 | 
            -
                  < | 
| 29 | 
            -
                    <% @headers.each do |label| %>
         | 
| 30 | 
            -
                      <th><%= label %></th>
         | 
| 31 | 
            -
                    <% end %>
         | 
| 32 | 
            -
                  </tr>
         | 
| 33 | 
            -
                </thead>
         | 
| 34 | 
            -
                <tbody class="results">
         | 
| 35 | 
            -
                  <% @results.each do |result| %>
         | 
| 44 | 
            +
              <% if @headers %>
         | 
| 45 | 
            +
                <table class="table table-condensed" id="results-table">
         | 
| 46 | 
            +
                  <thead>
         | 
| 36 47 | 
             
                    <tr>
         | 
| 37 | 
            -
                      <%  | 
| 38 | 
            -
                        < | 
| 48 | 
            +
                      <% @headers.each do |label| %>
         | 
| 49 | 
            +
                        <th><%= label %></th>
         | 
| 39 50 | 
             
                      <% end %>
         | 
| 40 51 | 
             
                    </tr>
         | 
| 41 | 
            -
                   | 
| 42 | 
            -
             | 
| 43 | 
            -
             | 
| 52 | 
            +
                  </thead>
         | 
| 53 | 
            +
                  <tbody class="results">
         | 
| 54 | 
            +
                    <% @results.each do |result| %>
         | 
| 55 | 
            +
                      <tr>
         | 
| 56 | 
            +
                        <% result.each do |value| %>
         | 
| 57 | 
            +
                          <td><%= value %></td>
         | 
| 58 | 
            +
                        <% end %>
         | 
| 59 | 
            +
                      </tr>
         | 
| 60 | 
            +
                    <% end %>
         | 
| 61 | 
            +
                  </tbody>
         | 
| 62 | 
            +
                </table>
         | 
| 63 | 
            +
              <% else %>
         | 
| 64 | 
            +
                <div class="alert alert-success">
         | 
| 65 | 
            +
                  <strong>Heads Up!</strong>
         | 
| 66 | 
            +
                  This query ran successfully, but returned no results.
         | 
| 67 | 
            +
                </div>
         | 
| 68 | 
            +
              <% end %>
         | 
| 44 69 | 
             
            <% elsif @query.error %>
         | 
| 45 70 | 
             
              <div class="alert alert-error">
         | 
| 46 71 | 
             
                <strong>Sorry!</strong>
         | 
| @@ -48,7 +73,7 @@ | |
| 48 73 | 
             
              </div>
         | 
| 49 74 | 
             
            <% else %>
         | 
| 50 75 | 
             
              <div class="alert alert-info">
         | 
| 51 | 
            -
                <strong> | 
| 76 | 
            +
                <strong>Heads Up!</strong>
         | 
| 52 77 | 
             
                This query is in progress, and has not returned any results yet.
         | 
| 53 78 | 
             
              </div>
         | 
| 54 79 | 
             
            <% end %>
         | 
| @@ -56,4 +81,7 @@ | |
| 56 81 | 
             
            <script type="text/javascript">
         | 
| 57 82 | 
             
              var queryNode = $('pre.cm-s-default');
         | 
| 58 83 | 
             
              CodeMirror.runMode(queryNode.text(), 'mysql', queryNode[0]);
         | 
| 84 | 
            +
              $('a.disabled').click(function(e) {
         | 
| 85 | 
            +
                e.preventDefault();
         | 
| 86 | 
            +
              });
         | 
| 59 87 | 
             
            </script>
         | 
| @@ -0,0 +1,29 @@ | |
| 1 | 
            +
            <% @current_tab = 'starred' %>
         | 
| 2 | 
            +
             | 
| 3 | 
            +
            <h2>Starred Queries</h2>
         | 
| 4 | 
            +
            <table class="table">
         | 
| 5 | 
            +
              <thead>
         | 
| 6 | 
            +
                <th></th>
         | 
| 7 | 
            +
                <th>Timestamp</th>
         | 
| 8 | 
            +
                <th>Query</th>
         | 
| 9 | 
            +
                <th>Author</th>
         | 
| 10 | 
            +
                <th></th>
         | 
| 11 | 
            +
              </thead>
         | 
| 12 | 
            +
              <tbody id="history">
         | 
| 13 | 
            +
                <% @queries.each do |query| %>
         | 
| 14 | 
            +
                  <tr>
         | 
| 15 | 
            +
                    <td class="status"><span class="indicator indicator-<%= query.status %>">*</span></td>
         | 
| 16 | 
            +
                    <td class="date"><%= query.formatted_start_time %></td>
         | 
| 17 | 
            +
                    <td>
         | 
| 18 | 
            +
                      <a href="/queries/<%= query.id %>"><%= query.description %></a>
         | 
| 19 | 
            +
                    </td>
         | 
| 20 | 
            +
                    <td class="author">
         | 
| 21 | 
            +
                      <%= query.author %>
         | 
| 22 | 
            +
                    </td>
         | 
| 23 | 
            +
                    <td>
         | 
| 24 | 
            +
                      <a class="delete" href="/queries/<%= query.id %>"><i class="icon-remove"></i></a>
         | 
| 25 | 
            +
                    </td>
         | 
| 26 | 
            +
                  </tr>
         | 
| 27 | 
            +
                <% end %>
         | 
| 28 | 
            +
              </tbody>
         | 
| 29 | 
            +
            </table>
         | 
    
        data/lib/oculus/server.rb
    CHANGED
    
    | @@ -21,6 +21,12 @@ module Oculus | |
| 21 21 | 
             
                  erb :index
         | 
| 22 22 | 
             
                end
         | 
| 23 23 |  | 
| 24 | 
            +
                get '/starred' do
         | 
| 25 | 
            +
                  @queries = Oculus.data_store.starred_queries.map { |q| Oculus::Presenters::QueryPresenter.new(q) }
         | 
| 26 | 
            +
             | 
| 27 | 
            +
                  erb :starred
         | 
| 28 | 
            +
                end
         | 
| 29 | 
            +
             | 
| 24 30 | 
             
                get '/history' do
         | 
| 25 31 | 
             
                  @queries = Oculus.data_store.all_queries.map { |q| Oculus::Presenters::QueryPresenter.new(q) }
         | 
| 26 32 |  | 
| @@ -35,13 +41,12 @@ module Oculus | |
| 35 41 | 
             
                end
         | 
| 36 42 |  | 
| 37 43 | 
             
                post '/queries' do
         | 
| 38 | 
            -
                   | 
| 39 | 
            -
                  query = Oculus::Query.create(:query     => params[:query],
         | 
| 40 | 
            -
                                               :thread_id => connection.thread_id)
         | 
| 44 | 
            +
                  query = Oculus::Query.create(:query => params[:query])
         | 
| 41 45 |  | 
| 42 46 | 
             
                  pid = fork do
         | 
| 47 | 
            +
                    query = Oculus::Query.find(query.id)
         | 
| 48 | 
            +
                    connection = Oculus::Connection::Mysql2.new(Oculus.connection_options)
         | 
| 43 49 | 
             
                    query.execute(connection)
         | 
| 44 | 
            -
                    query.save
         | 
| 45 50 | 
             
                  end
         | 
| 46 51 |  | 
| 47 52 | 
             
                  Process.detach(pid)
         | 
| @@ -67,14 +72,26 @@ module Oculus | |
| 67 72 | 
             
                  erb :show
         | 
| 68 73 | 
             
                end
         | 
| 69 74 |  | 
| 75 | 
            +
                get '/queries/:id/download' do
         | 
| 76 | 
            +
                  query = Oculus::Query.find(params[:id])
         | 
| 77 | 
            +
                  timestamp = query.started_at.strftime('%Y%m%d%H%M')
         | 
| 78 | 
            +
             | 
| 79 | 
            +
                  attachment    "#{timestamp}-query-#{query.id}-results.csv"
         | 
| 80 | 
            +
                  content_type  "text/csv"
         | 
| 81 | 
            +
                  last_modified query.finished_at
         | 
| 82 | 
            +
             | 
| 83 | 
            +
                  query.to_csv
         | 
| 84 | 
            +
                end
         | 
| 85 | 
            +
             | 
| 70 86 | 
             
                get '/queries/:id/status' do
         | 
| 71 87 | 
             
                  Oculus::Presenters::QueryPresenter.new(Oculus::Query.find(params[:id])).status
         | 
| 72 88 | 
             
                end
         | 
| 73 89 |  | 
| 74 90 | 
             
                put '/queries/:id' do
         | 
| 75 91 | 
             
                  @query = Oculus::Query.find(params[:id])
         | 
| 76 | 
            -
                  @query.name | 
| 77 | 
            -
                  @query.author | 
| 92 | 
            +
                  @query.name    = params[:name]              if params[:name]
         | 
| 93 | 
            +
                  @query.author  = params[:author]            if params[:author]
         | 
| 94 | 
            +
                  @query.starred = params[:starred] == "true" if params[:starred]
         | 
| 78 95 | 
             
                  @query.save
         | 
| 79 96 |  | 
| 80 97 | 
             
                  puts "true"
         | 
| @@ -10,7 +10,13 @@ module Oculus | |
| 10 10 |  | 
| 11 11 | 
             
                  def all_queries
         | 
| 12 12 | 
             
                    Dir["#{root}/*.query"].map do |path|
         | 
| 13 | 
            -
                      File.parse(path)
         | 
| 13 | 
            +
                      Query.new(File.parse(path))
         | 
| 14 | 
            +
                    end.sort { |a,b| b.id <=> a.id }
         | 
| 15 | 
            +
                  end
         | 
| 16 | 
            +
             | 
| 17 | 
            +
                  def starred_queries
         | 
| 18 | 
            +
                    Dir["#{root}/starred/*.query"].map do |path|
         | 
| 19 | 
            +
                      Query.new(File.parse(path))
         | 
| 14 20 | 
             
                    end.sort { |a,b| b.id <=> a.id }
         | 
| 15 21 | 
             
                  end
         | 
| 16 22 |  | 
| @@ -19,7 +25,16 @@ module Oculus | |
| 19 25 |  | 
| 20 26 | 
             
                    File.open(filename_for_id(query.id), 'w') do |file|
         | 
| 21 27 | 
             
                      file.write_prelude(query.attributes)
         | 
| 22 | 
            -
                      file.write_results(query.results) if query.results
         | 
| 28 | 
            +
                      file.write_results(query.results) if query.results && query.results.length > 0
         | 
| 29 | 
            +
                    end
         | 
| 30 | 
            +
             | 
| 31 | 
            +
                    FileUtils.mkdir_p(File.join(root, "starred")) unless Dir.exist?(File.join(root, "starred"))
         | 
| 32 | 
            +
                    star_path = starred_filename_for_id(query.id)
         | 
| 33 | 
            +
             | 
| 34 | 
            +
                    if query.starred
         | 
| 35 | 
            +
                      File.symlink(File.expand_path(filename_for_id(query.id)), star_path) unless File.exist?(star_path)
         | 
| 36 | 
            +
                    elsif File.exist?(star_path)
         | 
| 37 | 
            +
                      File.unlink(star_path)
         | 
| 23 38 | 
             
                    end
         | 
| 24 39 | 
             
                  end
         | 
| 25 40 |  | 
| @@ -34,6 +49,9 @@ module Oculus | |
| 34 49 | 
             
                  end
         | 
| 35 50 |  | 
| 36 51 | 
             
                  def delete_query(id)
         | 
| 52 | 
            +
                    star_path = starred_filename_for_id(id)
         | 
| 53 | 
            +
                    File.unlink(star_path) if File.exist?(star_path)
         | 
| 54 | 
            +
             | 
| 37 55 | 
             
                    path = filename_for_id(id)
         | 
| 38 56 |  | 
| 39 57 | 
             
                    if File.exist?(path)
         | 
| @@ -50,9 +68,9 @@ module Oculus | |
| 50 68 | 
             
                      file = File.open(path)
         | 
| 51 69 | 
             
                      attributes = file.attributes
         | 
| 52 70 | 
             
                      attributes[:results] = file.results
         | 
| 53 | 
            -
                       | 
| 54 | 
            -
             | 
| 55 | 
            -
                       | 
| 71 | 
            +
                      attributes[:id] = File.basename(path).split('.').first.to_i
         | 
| 72 | 
            +
                      attributes[:starred] ||= false
         | 
| 73 | 
            +
                      attributes
         | 
| 56 74 | 
             
                    end
         | 
| 57 75 |  | 
| 58 76 | 
             
                    def write_prelude(attributes)
         | 
| @@ -93,6 +111,11 @@ module Oculus | |
| 93 111 | 
             
                    end
         | 
| 94 112 | 
             
                  end
         | 
| 95 113 |  | 
| 114 | 
            +
                  def starred_filename_for_id(id)
         | 
| 115 | 
            +
                    raise ArgumentError unless id.is_a?(Integer) || id =~ /^[0-9]+/
         | 
| 116 | 
            +
                    File.join(root, "starred", "#{id}.query")
         | 
| 117 | 
            +
                  end
         | 
| 118 | 
            +
             | 
| 96 119 | 
             
                  def filename_for_id(id)
         | 
| 97 120 | 
             
                    raise ArgumentError unless id.is_a?(Integer) || id =~ /^[0-9]+/
         | 
| 98 121 | 
             
                    File.join(root, "#{id}.query")
         | 
    
        data/lib/oculus/version.rb
    CHANGED
    
    
    
        data/spec/connection_spec.rb
    CHANGED
    
    | @@ -1,33 +1,14 @@ | |
| 1 1 | 
             
            require 'oculus'
         | 
| 2 2 |  | 
| 3 3 | 
             
            describe Oculus::Connection do
         | 
| 4 | 
            -
               | 
| 5 | 
            -
                client = Mysql2::Client.new(:host => "localhost", :username => "root")
         | 
| 6 | 
            -
                client.query "CREATE DATABASE IF NOT EXISTS test"
         | 
| 7 | 
            -
                client.query "USE test"
         | 
| 8 | 
            -
                client.query %[
         | 
| 9 | 
            -
                  CREATE TABLE IF NOT EXISTS oculus_users (
         | 
| 10 | 
            -
                    id MEDIUMINT NOT NULL AUTO_INCREMENT,
         | 
| 11 | 
            -
                    name VARCHAR(255),
         | 
| 12 | 
            -
                    PRIMARY KEY (id)
         | 
| 13 | 
            -
                  );
         | 
| 14 | 
            -
              ]
         | 
| 15 | 
            -
             | 
| 16 | 
            -
                client.query 'TRUNCATE oculus_users'
         | 
| 17 | 
            -
             | 
| 18 | 
            -
                client.query %[
         | 
| 19 | 
            -
                  INSERT INTO oculus_users (name) VALUES ('Paul'), ('Amy'), ('Peter')
         | 
| 20 | 
            -
                ]
         | 
| 21 | 
            -
              end
         | 
| 22 | 
            -
             | 
| 23 | 
            -
              subject { Oculus::Connection::Mysql2.new(:database => 'test') }
         | 
| 4 | 
            +
              subject { Oculus::Connection::Mysql2.new(:host => 'localhost', :database => 'oculus_test', :username => 'root') }
         | 
| 24 5 |  | 
| 25 6 | 
             
              it "fetches a result set" do
         | 
| 26 7 | 
             
                subject.execute("SELECT * FROM oculus_users").should == [['id', 'name'], [1, 'Paul'], [2, 'Amy'], [3, 'Peter']]
         | 
| 27 8 | 
             
              end
         | 
| 28 9 |  | 
| 29 10 | 
             
              it "returns nil for queries that don't return result sets" do
         | 
| 30 | 
            -
                query_connection = Mysql2::Client.new(:host => "localhost", :database => " | 
| 11 | 
            +
                query_connection = Mysql2::Client.new(:host => "localhost", :database => "oculus_test", :username => "root")
         | 
| 31 12 | 
             
                thread_id = query_connection.thread_id
         | 
| 32 13 | 
             
                Thread.new {
         | 
| 33 14 | 
             
                  query_connection.execute("SELECT * FROM oculus_users WHERE SLEEP(2)")
         | 
    
        data/spec/file_store_spec.rb
    CHANGED
    
    | @@ -38,33 +38,48 @@ describe Oculus::Storage::FileStore do | |
| 38 38 | 
             
              it "round-trips a query with no results to disk" do
         | 
| 39 39 | 
             
                query = Oculus::Query.new(:name => "Unfinished query", :author => "Me")
         | 
| 40 40 | 
             
                subject.save_query(query)
         | 
| 41 | 
            -
                subject.load_query(query.id). | 
| 42 | 
            -
             | 
| 43 | 
            -
             | 
| 44 | 
            -
             | 
| 45 | 
            -
             | 
| 46 | 
            -
             | 
| 41 | 
            +
                subject.load_query(query.id).should == {
         | 
| 42 | 
            +
                  :id => query.id,
         | 
| 43 | 
            +
                  :name => query.name,
         | 
| 44 | 
            +
                  :author => query.author,
         | 
| 45 | 
            +
                  :query => query.query,
         | 
| 46 | 
            +
                  :results => [],
         | 
| 47 | 
            +
                  :thread_id => query.thread_id,
         | 
| 48 | 
            +
                  :starred => false,
         | 
| 49 | 
            +
                  :started_at => query.started_at,
         | 
| 50 | 
            +
                  :finished_at => query.finished_at
         | 
| 51 | 
            +
                }
         | 
| 47 52 | 
             
              end
         | 
| 48 53 |  | 
| 49 54 | 
             
              it "round-trips a query with an error to disk" do
         | 
| 50 55 | 
             
                subject.save_query(broken_query)
         | 
| 51 | 
            -
                subject.load_query(broken_query.id). | 
| 52 | 
            -
             | 
| 53 | 
            -
             | 
| 54 | 
            -
             | 
| 55 | 
            -
             | 
| 56 | 
            -
             | 
| 57 | 
            -
             | 
| 56 | 
            +
                subject.load_query(broken_query.id).should == {
         | 
| 57 | 
            +
                  :id => broken_query.id,
         | 
| 58 | 
            +
                  :name => broken_query.name,
         | 
| 59 | 
            +
                  :error => broken_query.error,
         | 
| 60 | 
            +
                  :author => broken_query.author,
         | 
| 61 | 
            +
                  :query => broken_query.query,
         | 
| 62 | 
            +
                  :results => [],
         | 
| 63 | 
            +
                  :thread_id => broken_query.thread_id,
         | 
| 64 | 
            +
                  :starred => false,
         | 
| 65 | 
            +
                  :started_at => broken_query.started_at,
         | 
| 66 | 
            +
                  :finished_at => broken_query.finished_at
         | 
| 67 | 
            +
                }
         | 
| 58 68 | 
             
              end
         | 
| 59 69 |  | 
| 60 70 | 
             
              it "round-trips a query to disk" do
         | 
| 61 71 | 
             
                subject.save_query(query)
         | 
| 62 | 
            -
                subject.load_query(query.id). | 
| 63 | 
            -
             | 
| 64 | 
            -
             | 
| 65 | 
            -
             | 
| 66 | 
            -
             | 
| 67 | 
            -
             | 
| 72 | 
            +
                subject.load_query(query.id).should == {
         | 
| 73 | 
            +
                  :id => query.id,
         | 
| 74 | 
            +
                  :name => query.name,
         | 
| 75 | 
            +
                  :author => query.author,
         | 
| 76 | 
            +
                  :query => query.query,
         | 
| 77 | 
            +
                  :results => query.results,
         | 
| 78 | 
            +
                  :thread_id => query.thread_id,
         | 
| 79 | 
            +
                  :starred => false,
         | 
| 80 | 
            +
                  :started_at => query.started_at,
         | 
| 81 | 
            +
                  :finished_at => query.finished_at
         | 
| 82 | 
            +
                }
         | 
| 68 83 | 
             
              end
         | 
| 69 84 |  | 
| 70 85 | 
             
              it "doesn't overwrite an existing query id when saving" do
         | 
| @@ -87,9 +102,19 @@ describe Oculus::Storage::FileStore do | |
| 87 102 | 
             
                subject.all_queries.map(&:results).should == [other_query.results, query.results]
         | 
| 88 103 | 
             
              end
         | 
| 89 104 |  | 
| 105 | 
            +
              it "fetches starred queries" do
         | 
| 106 | 
            +
                query.starred = true
         | 
| 107 | 
            +
                subject.save_query(query)
         | 
| 108 | 
            +
                subject.save_query(other_query)
         | 
| 109 | 
            +
             | 
| 110 | 
            +
                results = subject.starred_queries
         | 
| 111 | 
            +
                results.map(&:results).should == [query.results]
         | 
| 112 | 
            +
                results.first.starred.should be true
         | 
| 113 | 
            +
              end
         | 
| 114 | 
            +
             | 
| 90 115 | 
             
              it "deletes queries" do
         | 
| 91 116 | 
             
                subject.save_query(query)
         | 
| 92 | 
            -
                subject.load_query(query.id) | 
| 117 | 
            +
                subject.load_query(query.id)[:name].should == query.name
         | 
| 93 118 | 
             
                subject.delete_query(query.id)
         | 
| 94 119 |  | 
| 95 120 | 
             
                lambda {
         | 
| @@ -10,9 +10,20 @@ describe Oculus::Presenters::QueryPresenter do | |
| 10 10 | 
             
                presenter.description.should == 'foo'
         | 
| 11 11 | 
             
              end
         | 
| 12 12 |  | 
| 13 | 
            -
              it "has a formatted  | 
| 14 | 
            -
                query. | 
| 15 | 
            -
                presenter. | 
| 13 | 
            +
              it "has a formatted start time" do
         | 
| 14 | 
            +
                query.started_at = Time.mktime(2010, 1, 1, 12, 34)
         | 
| 15 | 
            +
                presenter.formatted_start_time.should == '2010-01-01 12:34 PM'
         | 
| 16 | 
            +
              end
         | 
| 17 | 
            +
             | 
| 18 | 
            +
              it "has a formatted finish time" do
         | 
| 19 | 
            +
                query.finished_at = Time.mktime(2010, 1, 1, 12, 34)
         | 
| 20 | 
            +
                presenter.formatted_finish_time.should == '2010-01-01 12:34 PM'
         | 
| 21 | 
            +
              end
         | 
| 22 | 
            +
             | 
| 23 | 
            +
              it "has an elapsed time" do
         | 
| 24 | 
            +
                query.started_at = Time.mktime(2010, 1, 1, 10, 30)
         | 
| 25 | 
            +
                query.finished_at = Time.mktime(2010, 1, 1, 12, 34)
         | 
| 26 | 
            +
                presenter.elapsed_time.should == '2 hours 4 minutes'
         | 
| 16 27 | 
             
              end
         | 
| 17 28 |  | 
| 18 29 | 
             
              it "reports successful queries" do
         | 
    
        data/spec/query_spec.rb
    CHANGED
    
    | @@ -2,25 +2,25 @@ require 'oculus' | |
| 2 2 |  | 
| 3 3 | 
             
            describe Oculus::Query do
         | 
| 4 4 | 
             
              before do
         | 
| 5 | 
            -
                Oculus.data_store = stub
         | 
| 5 | 
            +
                Oculus.data_store = stub(:load_query => {}, :save_query => nil)
         | 
| 6 6 | 
             
              end
         | 
| 7 7 |  | 
| 8 8 | 
             
              it "runs the query against the supplied connection" do
         | 
| 9 | 
            -
                connection = stub
         | 
| 9 | 
            +
                connection = stub(:thread_id => 42)
         | 
| 10 10 | 
             
                query = Oculus::Query.new(:query => 'SELECT * FROM users')
         | 
| 11 11 | 
             
                connection.should_receive(:execute).with('SELECT * FROM users')
         | 
| 12 12 | 
             
                query.execute(connection)
         | 
| 13 13 | 
             
              end
         | 
| 14 14 |  | 
| 15 15 | 
             
              it "stores the results of running the query" do
         | 
| 16 | 
            -
                connection = stub(:execute => [['id', 'name'], [1, 'Paul']])
         | 
| 16 | 
            +
                connection = stub(:execute => [['id', 'name'], [1, 'Paul']], :thread_id => 42)
         | 
| 17 17 | 
             
                query = Oculus::Query.new(:query => 'SELECT * FROM users')
         | 
| 18 18 | 
             
                query.execute(connection)
         | 
| 19 19 | 
             
                query.results.should == [['id', 'name'], [1, 'Paul']]
         | 
| 20 20 | 
             
              end
         | 
| 21 21 |  | 
| 22 22 | 
             
              it "stores errors when queries fail" do
         | 
| 23 | 
            -
                connection = stub
         | 
| 23 | 
            +
                connection = stub(:thread_id => 42)
         | 
| 24 24 | 
             
                query = Oculus::Query.new(:query => 'SELECT * FROM users')
         | 
| 25 25 | 
             
                connection.stub(:execute).and_raise(Oculus::Connection::Error.new('You have an error in your SQL syntax'))
         | 
| 26 26 | 
             
                query.execute(connection)
         | 
| @@ -37,16 +37,39 @@ describe Oculus::Query do | |
| 37 37 | 
             
                query.thread_id.should == 42
         | 
| 38 38 | 
             
              end
         | 
| 39 39 |  | 
| 40 | 
            -
              it "has a  | 
| 40 | 
            +
              it "has a start time" do
         | 
| 41 41 | 
             
                query = Oculus::Query.new
         | 
| 42 | 
            -
                query. | 
| 42 | 
            +
                query.started_at.should be nil
         | 
| 43 43 | 
             
              end
         | 
| 44 44 |  | 
| 45 | 
            -
              it " | 
| 46 | 
            -
                Oculus. | 
| 45 | 
            +
              it "has a finish time" do
         | 
| 46 | 
            +
                query = Oculus::Query.new
         | 
| 47 | 
            +
                query.finished_at.should be nil
         | 
| 48 | 
            +
              end
         | 
| 49 | 
            +
             | 
| 50 | 
            +
              it "updates start time when executing" do
         | 
| 47 51 | 
             
                Time.stub(:now).and_return(now = stub)
         | 
| 48 | 
            -
                 | 
| 49 | 
            -
                query. | 
| 52 | 
            +
                connection = stub(:execute => [['id', 'name'], [1, 'Paul']], :thread_id => 42)
         | 
| 53 | 
            +
                query = Oculus::Query.new(:query => 'SELECT * FROM users')
         | 
| 54 | 
            +
                query.execute(connection)
         | 
| 55 | 
            +
                query.started_at.should == now
         | 
| 56 | 
            +
              end
         | 
| 57 | 
            +
             | 
| 58 | 
            +
              it "updates finish time when executing" do
         | 
| 59 | 
            +
                Time.stub(:now).and_return(now = stub)
         | 
| 60 | 
            +
                connection = stub(:execute => [['id', 'name'], [1, 'Paul']], :thread_id => 42)
         | 
| 61 | 
            +
                query = Oculus::Query.new(:query => 'SELECT * FROM users')
         | 
| 62 | 
            +
                query.execute(connection)
         | 
| 63 | 
            +
                query.finished_at.should == now
         | 
| 64 | 
            +
              end
         | 
| 65 | 
            +
             | 
| 66 | 
            +
              it "updates finish time when execution fails" do
         | 
| 67 | 
            +
                Time.stub(:now).and_return(now = stub)
         | 
| 68 | 
            +
                connection = stub(:thread_id => 42)
         | 
| 69 | 
            +
                connection.stub(:execute).and_raise(Oculus::Connection::Error.new('You have an error in your SQL syntax'))
         | 
| 70 | 
            +
                query = Oculus::Query.new(:query => 'SELECT * FROM users')
         | 
| 71 | 
            +
                query.execute(connection)
         | 
| 72 | 
            +
                query.finished_at.should == now
         | 
| 50 73 | 
             
              end
         | 
| 51 74 |  | 
| 52 75 | 
             
              it "has a name" do
         | 
| @@ -59,6 +82,11 @@ describe Oculus::Query do | |
| 59 82 | 
             
                query.author.should == 'Paul'
         | 
| 60 83 | 
             
              end
         | 
| 61 84 |  | 
| 85 | 
            +
              it "can be starred" do
         | 
| 86 | 
            +
                query = Oculus::Query.new(:starred => true)
         | 
| 87 | 
            +
                query.starred.should == true
         | 
| 88 | 
            +
              end
         | 
| 89 | 
            +
             | 
| 62 90 | 
             
              it "stores new queries in the data store on creation" do
         | 
| 63 91 | 
             
                Oculus.data_store.should_receive(:save_query)
         | 
| 64 92 | 
             
                query = Oculus::Query.create(:results => [['id', 'name'], [1, 'Paul']])
         | 
| @@ -75,18 +103,23 @@ describe Oculus::Query do | |
| 75 103 | 
             
                Oculus::Query.find(1)
         | 
| 76 104 | 
             
              end
         | 
| 77 105 |  | 
| 78 | 
            -
              it "is not complete when  | 
| 106 | 
            +
              it "is not complete when it has not been executed" do
         | 
| 79 107 | 
             
                query = Oculus::Query.new(:query => 'SELECT * FROM users')
         | 
| 80 108 | 
             
                query.should_not be_complete
         | 
| 81 109 | 
             
              end
         | 
| 82 110 |  | 
| 83 | 
            -
              it "is complete when  | 
| 84 | 
            -
                 | 
| 111 | 
            +
              it "is complete when it has been executed" do
         | 
| 112 | 
            +
                connection = stub(:execute => [['id', 'name'], [1, 'Paul']], :thread_id => 42)
         | 
| 113 | 
            +
                query = Oculus::Query.new
         | 
| 114 | 
            +
                query.execute(connection)
         | 
| 85 115 | 
             
                query.should be_complete
         | 
| 86 116 | 
             
              end
         | 
| 87 117 |  | 
| 88 118 | 
             
              it "is complete when there is an error" do
         | 
| 89 | 
            -
                 | 
| 119 | 
            +
                connection = stub(:thread_id => 42)
         | 
| 120 | 
            +
                connection.stub(:execute).and_raise(Oculus::Connection::Error.new('You have an error in your SQL syntax'))
         | 
| 121 | 
            +
                query = Oculus::Query.new
         | 
| 122 | 
            +
                query.execute(connection)
         | 
| 90 123 | 
             
                query.should be_complete
         | 
| 91 124 | 
             
              end
         | 
| 92 125 |  | 
| @@ -96,12 +129,23 @@ describe Oculus::Query do | |
| 96 129 | 
             
              end
         | 
| 97 130 |  | 
| 98 131 | 
             
              it "is successful when results are present" do
         | 
| 99 | 
            -
                 | 
| 132 | 
            +
                connection = stub(:execute => [['id', 'name'], [1, 'Paul']], :thread_id => 42)
         | 
| 133 | 
            +
                query = Oculus::Query.new
         | 
| 134 | 
            +
                query.execute(connection)
         | 
| 100 135 | 
             
                query.succeeded?.should be true
         | 
| 101 136 | 
             
              end
         | 
| 102 137 |  | 
| 103 138 | 
             
              it "is not successful when there is an error" do
         | 
| 104 | 
            -
                 | 
| 139 | 
            +
                connection = stub(:thread_id => 42)
         | 
| 140 | 
            +
                connection.stub(:execute).and_raise(Oculus::Connection::Error.new('You have an error in your SQL syntax'))
         | 
| 141 | 
            +
                query = Oculus::Query.new
         | 
| 142 | 
            +
                query.execute(connection)
         | 
| 105 143 | 
             
                query.succeeded?.should be false
         | 
| 106 144 | 
             
              end
         | 
| 145 | 
            +
             | 
| 146 | 
            +
              it "exports to CSV" do
         | 
| 147 | 
            +
                query = Oculus::Query.new
         | 
| 148 | 
            +
                query.results = [['id', 'name'], [1, 'Paul']]
         | 
| 149 | 
            +
                query.to_csv.should == "id,name\n1,Paul\n"
         | 
| 150 | 
            +
              end
         | 
| 107 151 | 
             
            end
         | 
    
        metadata
    CHANGED
    
    | @@ -1,7 +1,7 @@ | |
| 1 1 | 
             
            --- !ruby/object:Gem::Specification
         | 
| 2 2 | 
             
            name: oculus
         | 
| 3 3 | 
             
            version: !ruby/object:Gem::Version
         | 
| 4 | 
            -
              version: 0. | 
| 4 | 
            +
              version: 0.8.0
         | 
| 5 5 | 
             
              prerelease: 
         | 
| 6 6 | 
             
            platform: ruby
         | 
| 7 7 | 
             
            authors:
         | 
| @@ -9,11 +9,11 @@ authors: | |
| 9 9 | 
             
            autorequire: 
         | 
| 10 10 | 
             
            bindir: bin
         | 
| 11 11 | 
             
            cert_chain: []
         | 
| 12 | 
            -
            date: 2012-05- | 
| 12 | 
            +
            date: 2012-05-06 00:00:00.000000000 Z
         | 
| 13 13 | 
             
            dependencies:
         | 
| 14 14 | 
             
            - !ruby/object:Gem::Dependency
         | 
| 15 15 | 
             
              name: sinatra
         | 
| 16 | 
            -
              requirement: & | 
| 16 | 
            +
              requirement: &70114293249540 !ruby/object:Gem::Requirement
         | 
| 17 17 | 
             
                none: false
         | 
| 18 18 | 
             
                requirements:
         | 
| 19 19 | 
             
                - - ! '>='
         | 
| @@ -21,10 +21,10 @@ dependencies: | |
| 21 21 | 
             
                    version: 1.3.0
         | 
| 22 22 | 
             
              type: :runtime
         | 
| 23 23 | 
             
              prerelease: false
         | 
| 24 | 
            -
              version_requirements: * | 
| 24 | 
            +
              version_requirements: *70114293249540
         | 
| 25 25 | 
             
            - !ruby/object:Gem::Dependency
         | 
| 26 26 | 
             
              name: mysql2
         | 
| 27 | 
            -
              requirement: & | 
| 27 | 
            +
              requirement: &70114293249020 !ruby/object:Gem::Requirement
         | 
| 28 28 | 
             
                none: false
         | 
| 29 29 | 
             
                requirements:
         | 
| 30 30 | 
             
                - - ! '>='
         | 
| @@ -32,10 +32,10 @@ dependencies: | |
| 32 32 | 
             
                    version: 0.3.11
         | 
| 33 33 | 
             
              type: :runtime
         | 
| 34 34 | 
             
              prerelease: false
         | 
| 35 | 
            -
              version_requirements: * | 
| 35 | 
            +
              version_requirements: *70114293249020
         | 
| 36 36 | 
             
            - !ruby/object:Gem::Dependency
         | 
| 37 37 | 
             
              name: vegas
         | 
| 38 | 
            -
              requirement: & | 
| 38 | 
            +
              requirement: &70114293248540 !ruby/object:Gem::Requirement
         | 
| 39 39 | 
             
                none: false
         | 
| 40 40 | 
             
                requirements:
         | 
| 41 41 | 
             
                - - ! '>='
         | 
| @@ -43,10 +43,10 @@ dependencies: | |
| 43 43 | 
             
                    version: 0.1.4
         | 
| 44 44 | 
             
              type: :runtime
         | 
| 45 45 | 
             
              prerelease: false
         | 
| 46 | 
            -
              version_requirements: * | 
| 46 | 
            +
              version_requirements: *70114293248540
         | 
| 47 47 | 
             
            - !ruby/object:Gem::Dependency
         | 
| 48 48 | 
             
              name: rake
         | 
| 49 | 
            -
              requirement: & | 
| 49 | 
            +
              requirement: &70114288904480 !ruby/object:Gem::Requirement
         | 
| 50 50 | 
             
                none: false
         | 
| 51 51 | 
             
                requirements:
         | 
| 52 52 | 
             
                - - ! '>='
         | 
| @@ -54,10 +54,10 @@ dependencies: | |
| 54 54 | 
             
                    version: '0'
         | 
| 55 55 | 
             
              type: :development
         | 
| 56 56 | 
             
              prerelease: false
         | 
| 57 | 
            -
              version_requirements: * | 
| 57 | 
            +
              version_requirements: *70114288904480
         | 
| 58 58 | 
             
            - !ruby/object:Gem::Dependency
         | 
| 59 59 | 
             
              name: cucumber
         | 
| 60 | 
            -
              requirement: & | 
| 60 | 
            +
              requirement: &70114288903580 !ruby/object:Gem::Requirement
         | 
| 61 61 | 
             
                none: false
         | 
| 62 62 | 
             
                requirements:
         | 
| 63 63 | 
             
                - - ! '>='
         | 
| @@ -65,10 +65,10 @@ dependencies: | |
| 65 65 | 
             
                    version: '1'
         | 
| 66 66 | 
             
              type: :development
         | 
| 67 67 | 
             
              prerelease: false
         | 
| 68 | 
            -
              version_requirements: * | 
| 68 | 
            +
              version_requirements: *70114288903580
         | 
| 69 69 | 
             
            - !ruby/object:Gem::Dependency
         | 
| 70 70 | 
             
              name: rspec
         | 
| 71 | 
            -
              requirement: & | 
| 71 | 
            +
              requirement: &70114288902160 !ruby/object:Gem::Requirement
         | 
| 72 72 | 
             
                none: false
         | 
| 73 73 | 
             
                requirements:
         | 
| 74 74 | 
             
                - - ! '>='
         | 
| @@ -76,10 +76,10 @@ dependencies: | |
| 76 76 | 
             
                    version: '2'
         | 
| 77 77 | 
             
              type: :development
         | 
| 78 78 | 
             
              prerelease: false
         | 
| 79 | 
            -
              version_requirements: * | 
| 79 | 
            +
              version_requirements: *70114288902160
         | 
| 80 80 | 
             
            - !ruby/object:Gem::Dependency
         | 
| 81 81 | 
             
              name: capybara
         | 
| 82 | 
            -
              requirement: & | 
| 82 | 
            +
              requirement: &70114288901540 !ruby/object:Gem::Requirement
         | 
| 83 83 | 
             
                none: false
         | 
| 84 84 | 
             
                requirements:
         | 
| 85 85 | 
             
                - - ! '>='
         | 
| @@ -87,7 +87,7 @@ dependencies: | |
| 87 87 | 
             
                    version: '1'
         | 
| 88 88 | 
             
              type: :development
         | 
| 89 89 | 
             
              prerelease: false
         | 
| 90 | 
            -
              version_requirements: * | 
| 90 | 
            +
              version_requirements: *70114288901540
         | 
| 91 91 | 
             
            description: Oculus is a web-based logging SQL client.  It keeps a history of your
         | 
| 92 92 | 
             
              queries and the results they returned, so your research is always at hand, easy
         | 
| 93 93 | 
             
              to share and easy to repeat or reproduce in the future.
         | 
| @@ -131,6 +131,7 @@ files: | |
| 131 131 | 
             
            - lib/oculus/server/views/index.erb
         | 
| 132 132 | 
             
            - lib/oculus/server/views/layout.erb
         | 
| 133 133 | 
             
            - lib/oculus/server/views/show.erb
         | 
| 134 | 
            +
            - lib/oculus/server/views/starred.erb
         | 
| 134 135 | 
             
            - lib/oculus/storage.rb
         | 
| 135 136 | 
             
            - lib/oculus/storage/file_store.rb
         | 
| 136 137 | 
             
            - lib/oculus/version.rb
         |