Merge branch '4024-pipeline-instances-scroll' of git.curoverse.com:arvados into 4024...
authorradhika <radhika@curoverse.com>
Sun, 16 Nov 2014 21:18:45 +0000 (16:18 -0500)
committerradhika <radhika@curoverse.com>
Sun, 16 Nov 2014 21:18:45 +0000 (16:18 -0500)
14 files changed:
apps/workbench/Gemfile
apps/workbench/Gemfile.lock
apps/workbench/app/assets/javascripts/filterable.js
apps/workbench/app/assets/javascripts/infinite_scroll.js
apps/workbench/app/assets/javascripts/pipeline_instances.js
apps/workbench/app/controllers/application_controller.rb
apps/workbench/app/controllers/pipeline_instances_controller.rb
apps/workbench/app/models/arvados_resource_list.rb
apps/workbench/app/views/application/_choose.html.erb
apps/workbench/app/views/layouts/application.html.erb
apps/workbench/app/views/pipeline_instances/index.html.erb
apps/workbench/test/functional/pipeline_instances_controller_test.rb
apps/workbench/test/integration/filterable_infinite_scroll_test.rb [new file with mode: 0644]
sdk/ruby/arvados.gemspec

index 90532ea9971ae398c3503be7779e0c52a4e31441..5ab6eace2c8de0777e23daa62cc6de1d0863ce94 100644 (file)
@@ -3,7 +3,7 @@ source 'https://rubygems.org'
 gem 'rails', '~> 4.1.0'
 gem 'minitest', '>= 5.0.0'
 
-gem 'arvados', '>= 0.1.20140917180103'
+gem 'arvados', '>= 0.1.20141114230720'
 
 # Bundle edge Rails instead:
 # gem 'rails', :git => 'git://github.com/rails/rails.git'
index 53d42cb856e59903704b39e157bb118de6c4320f..8b9ea947bb0ba9498ab347b673e887e664132414 100644 (file)
@@ -39,11 +39,11 @@ GEM
     addressable (2.3.6)
     andand (1.3.3)
     arel (5.0.1.20140414130214)
-    arvados (0.1.20140917180103)
+    arvados (0.1.20141114230720)
       activesupport (>= 3.2.13)
-      andand
-      google-api-client (~> 0.6.3)
-      json (>= 1.7.7)
+      andand (~> 1.3, >= 1.3.3)
+      google-api-client (~> 0.6.3, >= 0.6.3)
+      json (~> 1.7, >= 1.7.7)
       jwt (>= 0.1.5, < 1.0.0)
     autoparse (0.3.3)
       addressable (>= 2.3.1)
@@ -114,7 +114,7 @@ GEM
     json (1.8.1)
     jwt (0.1.13)
       multi_json (>= 1.5)
-    launchy (2.4.2)
+    launchy (2.4.3)
       addressable (~> 2.3)
     less (2.4.0)
       commonjs (~> 0.2.7)
@@ -242,7 +242,7 @@ PLATFORMS
 DEPENDENCIES
   RedCloth
   andand
-  arvados (>= 0.1.20140917180103)
+  arvados (>= 0.1.20141114230720)
   bootstrap-sass (~> 3.1.0)
   bootstrap-tab-history-rails
   bootstrap-x-editable-rails
index 8ac195383b40027b39ef3062b1cb6e67c90fe41e..cd01f64a74f539945d5fb2f0f7cef7399a574be3 100644 (file)
 // Combining "select" filterable-controls with infinite-scroll is not
 // yet supported.
 
+function updateFilterableQueryNow($target) {
+    var newquery = $target.data('filterable-query-new');
+    var params = $target.data('infinite-content-params-filterable') || {};
+    params.filters = [['any', 'ilike', '%' + newquery + '%']];
+    $target.data('infinite-content-params-filterable', params);
+    $target.data('filterable-query', newquery);
+}
+
 $(document).
-    on('paste keyup input', 'input[type=text].filterable-control', function() {
+    on('ready ajax:success', function() {
+        // Copy any initial input values into
+        // data-filterable-query[-new].
+        $('input[type=text].filterable-control').each(function() {
+            var $this = $(this);
+            var $target = $($this.attr('data-filterable-target'));
+            if ($target.data('filterable-query-new') === undefined) {
+                $target.data('filterable-query', $this.val());
+                $target.data('filterable-query-new', $this.val());
+                updateFilterableQueryNow($target);
+            }
+        });
+        $('[data-infinite-scroller]').on('refresh-content', '[data-filterable-query]', function(e) {
+            // If some other event causes a refresh-content event while there
+            // is a new query waiting to cooloff, we should use the new query
+            // right away -- otherwise we'd launch an extra ajax request that
+            // would have to be reloaded as soon as the cooloff period ends.
+            if (this != e.target)
+                return;
+            if ($(this).data('filterable-query') == $(this).data('filterable-query-new'))
+                return;
+            updateFilterableQueryNow($(this));
+        });
+    }).
+    on('paste keyup input', 'input[type=text].filterable-control', function(e) {
+        if (this != e.target) return;
         var $target = $($(this).attr('data-filterable-target'));
         var currentquery = $target.data('filterable-query');
         if (currentquery === undefined) currentquery = '';
@@ -74,11 +107,7 @@ $(document).
                 // in the next 1/4 second (like type or erase
                 // characters in the search box), hide the stale
                 // content and ask the server for new results.
-                var newquery = $target.data('filterable-query-new');
-                var params = $target.data('infinite-content-params-filterable') || {};
-                params.filters = [['any', 'ilike', '%' + newquery + '%']];
-                $target.data('infinite-content-params-filterable', params);
-                $target.data('filterable-query', newquery);
+                updateFilterableQueryNow($target);
                 $target.trigger('refresh-content');
             }, 250));
         } else {
index 047257644f0b804469c47ab3a17429c363aa0188..7bdf574ed9964802d6dab2b011ad3d8ca8b7fd1d 100644 (file)
@@ -1,7 +1,7 @@
 function maybe_load_more_content(event) {
-    var scroller = this;        // element with scroll bars
-    var $container;             // element that receives new content
-    var src;                    // url for retrieving content
+    var scroller = this;
+    var $container = $(event.data.container);
+    var src;                     // url for retrieving content
     var scrollHeight;
     var spinner, colspan;
     var serial = Date.now();
@@ -11,7 +11,6 @@ function maybe_load_more_content(event) {
         >
         scrollHeight - 50)
     {
-        $container = $(event.data.container);
         if (!$container.attr('data-infinite-content-href0')) {
             // Remember the first page source url, so we can refresh
             // from page 1 later.
index fa9d6ae5a4d4ada1200d033b54727370675957f7..bd87b28028ac2586b90bdc375e4a7563184fd145 100644 (file)
@@ -1,5 +1,5 @@
 function run_pipeline_button_state() {
-    var a = $('a.editable.required.editable-empty,input.form-control.required[value=]');
+    var a = $('a.editable.required.editable-empty,input.form-control.required[value=""]');
     if (a.length > 0) {
         $(".run-pipeline-button").addClass("disabled");
     }
index 88ea4b0a6141b484a7f4a60404f17fa54b1cf979..e88d38e2adcd1120f54bf4c49caca07791904c9b 100644 (file)
@@ -164,6 +164,7 @@ class ApplicationController < ActionController::Base
   def find_objects_for_index
     @objects ||= model_class
     @objects = @objects.filter(@filters).limit(@limit).offset(@offset)
+    @objects.fetch_multiple_pages(false)
   end
 
   def render_index
@@ -172,9 +173,9 @@ class ApplicationController < ActionController::Base
         if params[:partial]
           @next_page_href = next_page_href(partial: params[:partial], filters: @filters.to_json)
           render json: {
-            content: render_to_string(partial: "show_#{params[:partial]}", formats: [:html]),
-                                      next_page_href: @next_page_href
-
+            content: render_to_string(partial: "show_#{params[:partial]}",
+                                      formats: [:html]),
+            next_page_href: @next_page_href
           }
         else
           render json: @objects
@@ -218,6 +219,8 @@ class ApplicationController < ActionController::Base
     if !objects
       objects = @objects
     end
+    # result_limit and result_offset won't work until we call #results
+    objects.results
     if objects.respond_to?(:result_offset) and
         objects.respond_to?(:result_limit) and
         objects.respond_to?(:items_available)
@@ -268,7 +271,6 @@ class ApplicationController < ActionController::Base
       if params[:partial]
         f.json {
           find_objects_for_index if !@objects
-          @objects.fetch_multiple_pages(false)
           render json: {
             content: render_to_string(partial: "choose_rows.html",
                                       formats: [:html]),
index 82a9b348bc1943f393855390e11441224778e034..3326527cc8912e9ea45dc72d1a5277d548e91550 100644 (file)
@@ -293,18 +293,6 @@ class PipelineInstancesController < ApplicationController
     %w(Compare Graph)
   end
 
-  def index
-    if params[:search].andand.length.andand > 0
-      @select ||= PipelineInstance.columns.map(&:name)
-      base_search = PipelineInstance.select(@select)
-      @objects = base_search.where(any: ['contains', params[:search]]).
-                              uniq { |pi| pi.uuid }
-    end
-
-    @limit = 20
-    super
-  end
-
   protected
   def for_comparison v
     if v.is_a? Hash or v.is_a? Array
@@ -314,8 +302,12 @@ class PipelineInstancesController < ApplicationController
     end
   end
 
+  def load_filters_and_paging_params
+    params[:limit] = 20
+    super
+  end
+
   def find_objects_by_uuid
     @objects = model_class.where(uuid: params[:uuids])
   end
-
 end
index 3000aa8ac264a1e18523e810a594689a8ec607f5..6890b874513be7178cb19d848bc2820834dee922 100644 (file)
@@ -2,6 +2,8 @@ class ArvadosResourceList
   include ArvadosApiClientHelper
   include Enumerable
 
+  attr_reader :resource_class
+
   def initialize resource_class=nil
     @resource_class = resource_class
     @fetch_multiple_pages = true
index d36f5f95d2ccff0aede8bfc878ae475a60a86f30..4e1503bde59738b90eb99b8b42f2d430a8987657 100644 (file)
@@ -3,7 +3,7 @@
     <div class="modal-content">
       <div class="modal-header">
         <button type="button" class="close" data-dismiss="modal" aria-hidden="true">&times;</button>
-        <h4 class="modal-title"><%= params[:title] || "Choose #{@objects.first.andand.class_for_display}" %></h4>
+        <h4 class="modal-title"><%= params[:title] || "Choose #{@objects.resource_class.andand.class_for_display}" %></h4>
       </div>
 
       <div class="modal-body">
index 0a309db5b559bc6da69b5a30c75e76432ed131ca..324714e5346efe574fa6e74465c7fdeb2827b9ec 100644 (file)
@@ -41,7 +41,7 @@
     }
   </style>
   <link href="//netdna.bootstrapcdn.com/font-awesome/4.1.0/css/font-awesome.css" rel="stylesheet">
-<%= piwik_tracking_tag %>
+  <%= piwik_tracking_tag if (PiwikAnalytics.configuration.url != 'localhost' rescue false) %>
 </head>
 <body>
 <%= render template: 'layouts/body' %>
index 2308fc1ca6a99a315cd5bc2dea2e740744812721..e538815b228adb84c6264cc2153c0b25eacd0722 100644 (file)
@@ -2,7 +2,10 @@
   <div class="input-group">
     <input type="text" class="form-control filterable-control recent-pipeline-instances-filterable-control"
            placeholder="Search pipeline instances"
-           data-filterable-target="#recent-pipeline-instances"/>
+           data-filterable-target="#recent-pipeline-instances"
+           <%# Just for the double-load test in FilterableInfiniteScrollTest: %>
+           value="<%= params[:search] %>"
+           />
   </div>
 
   <%= form_tag({action: 'compare', controller: params[:controller], method: 'get'}, {method: 'get', id: 'compare', class: 'pull-right small-form-margin'}) do |f| %>
index 90bccc0eb18da5a11be0c5c23d1583c9947e3200..a14d419e128525e829a25fa1471ff9f1d1be005a 100644 (file)
@@ -69,7 +69,7 @@ class PipelineInstancesControllerTest < ActionController::TestCase
       end
   end
 
-  test "component rendering copes with unexpeceted components format" do
+  test "component rendering copes with unexpected components format" do
     get(:show,
         {id: api_fixture("pipeline_instances")["components_is_jobspec"]["uuid"]},
         session_for(:active))
diff --git a/apps/workbench/test/integration/filterable_infinite_scroll_test.rb b/apps/workbench/test/integration/filterable_infinite_scroll_test.rb
new file mode 100644 (file)
index 0000000..4434f9a
--- /dev/null
@@ -0,0 +1,29 @@
+require 'integration_helper'
+
+class FilterableInfiniteScrollTest < ActionDispatch::IntegrationTest
+  setup do
+    headless = Headless.new
+    headless.start
+    Capybara.current_driver = :selenium
+  end
+
+  # Chrome remembers what you had in the text field when you hit
+  # "back". Here, we simulate the same effect by sending an otherwise
+  # unused ?search=foo param to pre-populate the search field.
+  test 'no double-load if text input has a value at page load time' do
+    visit page_with_token('admin', '/pipeline_instances')
+    assert_text 'pipeline_2'
+    visit page_with_token('admin', '/pipeline_instances?search=pipeline_1')
+    # Horrible hack to ensure the search results can't load correctly
+    # on the second attempt.
+    assert_selector '#recent-pipeline-instances'
+    assert page.evaluate_script('$("#recent-pipeline-instances[data-infinite-content-href0]").attr("data-infinite-content-href0","/give-me-an-error").length == 1')
+    # Wait for the first page of results to appear.
+    assert_text 'pipeline_1'
+    # Make sure the results are filtered.
+    assert_no_text 'pipeline_2'
+    # Make sure pipeline_2 didn't disappear merely because the results
+    # were replaced with an error message.
+    assert_text 'pipeline_1'
+  end
+end
index bec698a12ddffd13181c57d10dc147a505e25c8d..5361e03a2bea967f00b441932d0efeef0f0a7054 100644 (file)
@@ -18,7 +18,7 @@ Gem::Specification.new do |s|
   s.files       = ["lib/arvados.rb", "lib/arvados/keep.rb"]
   s.required_ruby_version = '>= 2.1.0'
   s.add_dependency('google-api-client', '~> 0.6.3', '>= 0.6.3')
-  s.add_dependency('activesupport', '~> 3.2', '>= 3.2.13')
+  s.add_dependency('activesupport', '>= 3.2.13')
   s.add_dependency('json', '~> 1.7', '>= 1.7.7')
   s.add_dependency('andand', '~> 1.3', '>= 1.3.3')
   s.add_runtime_dependency('jwt', '>= 0.1.5', '< 1.0.0')