browse-everything 0.15.1 → 0.16.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 (76) hide show
  1. checksums.yaml +5 -5
  2. data/.rubocop.yml +61 -9
  3. data/.rubocop_todo.yml +2 -15
  4. data/.travis.yml +19 -19
  5. data/CONTRIBUTING.md +6 -6
  6. data/Gemfile +12 -8
  7. data/README.md +30 -0
  8. data/Rakefile +2 -1
  9. data/app/assets/javascripts/browse_everything/behavior.js.coffee +5 -0
  10. data/app/controllers/browse_everything_controller.rb +75 -23
  11. data/app/helpers/browse_everything_helper.rb +2 -8
  12. data/app/helpers/font_awesome_version_helper.rb +9 -8
  13. data/app/services/browse_everything_session.rb +10 -0
  14. data/app/services/browse_everything_session/provider_session.rb +42 -0
  15. data/app/services/browser_factory.rb +25 -0
  16. data/app/views/browse_everything/_files.html.erb +56 -6
  17. data/browse-everything.gemspec +29 -25
  18. data/config/routes.rb +7 -2
  19. data/lib/browse-everything.rb +2 -0
  20. data/lib/browse_everything.rb +45 -12
  21. data/lib/browse_everything/auth/google/credentials.rb +28 -0
  22. data/lib/browse_everything/auth/google/request_parameters.rb +61 -0
  23. data/lib/browse_everything/browser.rb +11 -4
  24. data/lib/browse_everything/driver/authentication_factory.rb +22 -0
  25. data/lib/browse_everything/driver/base.rb +72 -19
  26. data/lib/browse_everything/driver/box.rb +46 -17
  27. data/lib/browse_everything/driver/dropbox.rb +36 -10
  28. data/lib/browse_everything/driver/file_system.rb +14 -26
  29. data/lib/browse_everything/driver/google_drive.rb +187 -54
  30. data/lib/browse_everything/driver/s3.rb +81 -75
  31. data/lib/browse_everything/engine.rb +3 -2
  32. data/lib/browse_everything/file_entry.rb +3 -1
  33. data/lib/browse_everything/retriever.rb +103 -31
  34. data/lib/browse_everything/version.rb +3 -1
  35. data/lib/generators/browse_everything/assets_generator.rb +3 -2
  36. data/lib/generators/browse_everything/config_generator.rb +11 -9
  37. data/lib/generators/browse_everything/install_generator.rb +3 -2
  38. data/lib/generators/browse_everything/templates/browse_everything_providers.yml.example +12 -11
  39. data/spec/controllers/browse_everything_controller_spec.rb +80 -0
  40. data/spec/features/select_files_spec.rb +13 -13
  41. data/spec/features/test_compiling_stylesheets_spec.rb +2 -0
  42. data/spec/fixtures/vcr_cassettes/google_drive.yml +331 -0
  43. data/spec/fixtures/vcr_cassettes/retriever.yml +93 -0
  44. data/spec/helper/browse_everything_controller_helper_spec.rb +21 -7
  45. data/spec/javascripts/jasmine_spec.rb +2 -0
  46. data/spec/javascripts/support/jasmine_helper.rb +1 -0
  47. data/spec/lib/browse_everything/auth/google/credentials_spec.rb +41 -0
  48. data/spec/{unit → lib/browse_everything}/browse_everything_helper_spec.rb +2 -0
  49. data/spec/lib/browse_everything/browser_spec.rb +109 -0
  50. data/spec/{unit → lib/browse_everything/driver}/base_spec.rb +5 -4
  51. data/spec/{unit → lib/browse_everything/driver}/box_spec.rb +20 -5
  52. data/spec/{unit → lib/browse_everything/driver}/dropbox_spec.rb +15 -18
  53. data/spec/{unit → lib/browse_everything/driver}/file_system_spec.rb +32 -26
  54. data/spec/lib/browse_everything/driver/google_drive_spec.rb +171 -0
  55. data/spec/{unit → lib/browse_everything/driver}/s3_spec.rb +38 -21
  56. data/spec/lib/browse_everything/driver_spec.rb +38 -0
  57. data/spec/{unit → lib/browse_everything}/file_entry_spec.rb +4 -1
  58. data/spec/lib/browse_everything/retriever_spec.rb +200 -0
  59. data/spec/lib/browse_everything_spec.rb +67 -0
  60. data/spec/services/browse_everything_session/provider_session_spec.rb +50 -0
  61. data/spec/services/browser_factory_spec.rb +40 -0
  62. data/spec/spec_helper.rb +39 -18
  63. data/spec/support/app/controllers/file_handler_controller.rb +4 -4
  64. data/spec/support/app/views/file_handler/main.html.erb +1 -1
  65. data/spec/support/capybara.rb +17 -0
  66. data/spec/support/rake.rb +3 -1
  67. data/spec/support/wait_for_ajax.rb +14 -0
  68. data/spec/test_app_templates/Gemfile.extra +1 -0
  69. data/spec/test_app_templates/lib/generators/test_app_generator.rb +10 -4
  70. data/spec/views/browse_everything/{_file.html.erb_spec.rb → _files.html.erb_spec.rb} +24 -18
  71. data/tasks/ci.rake +2 -0
  72. metadata +159 -107
  73. data/app/views/browse_everything/_file.html.erb +0 -52
  74. data/app/views/browse_everything/resolve.html.erb +0 -1
  75. data/spec/unit/browser_spec.rb +0 -76
  76. data/spec/unit/retriever_spec.rb +0 -109
@@ -1,12 +1,6 @@
1
- module BrowseEverythingHelper
2
- def array_to_hidden_fields(array, key)
3
- fields = array.to_query(key).split(Rack::Utils::DEFAULT_SEP).collect do |pair|
4
- key, value = pair.split('=', 2).map { |str| Rack::Utils.unescape(str) }
5
- hidden_field_tag(key, value)
6
- end
7
- fields.join("\n").html_safe
8
- end
1
+ # frozen_string_literal: true
9
2
 
3
+ module BrowseEverythingHelper
10
4
  # Extracted from Rack::Mime 1.5.2 for use with earlier versions of Rack/Rails
11
5
  # @param [String] value
12
6
  # @param [String] matcher
@@ -1,16 +1,17 @@
1
+ # frozen_string_literal: true
2
+
1
3
  module FontAwesomeVersionHelper
4
+
5
+ # Determines if the App uses Font Awesome releases 3.x or 4.x
6
+ # @return [TrueClass, FalseClass]
2
7
  def font_awesome_four?
3
8
  Bundler.environment.specs['font-awesome-rails'].first.version >= Gem::Version.new('4')
4
9
  end
5
10
 
6
- def fa3(if3)
7
- fa3or4(if3, '')
8
- end
9
-
10
- def fa4(if4)
11
- fa3or4('', if4)
12
- end
13
-
11
+ # Selects one of two HTML element classes depending upon the release of Font Awesome used by the App
12
+ # @param if3 [String] the Font Awesome 3 HTML class
13
+ # @param if4 [String] the Font Awesome 4 HTML class
14
+ # @return [String] the Font Awesome HTML class
14
15
  def fa3or4(if3, if4)
15
16
  font_awesome_four? ? if4 : if3
16
17
  end
@@ -0,0 +1,10 @@
1
+
2
+ # frozen_string_literal: true
3
+
4
+ class BrowseEverythingSession
5
+ class Base
6
+ def initialize(session:)
7
+ @session = session
8
+ end
9
+ end
10
+ end
@@ -0,0 +1,42 @@
1
+ # frozen_string_literal: true
2
+
3
+ # Object for handling session cookies containing cached values
4
+ class BrowseEverythingSession
5
+ class ProviderSession < Base
6
+ class_attribute :sessions
7
+ self.sessions = {}
8
+
9
+ def self.for(session:, name:)
10
+ ProviderSession.new(session: session, name: name)
11
+ end
12
+
13
+ def initialize(session:, name:)
14
+ @name = name
15
+ super(session: session)
16
+ end
17
+
18
+ def token=(value)
19
+ @session["#{@name}_token"] = value
20
+ end
21
+
22
+ def token
23
+ @session["#{@name}_token"]
24
+ end
25
+
26
+ def code=(value)
27
+ @session["#{@name}_code"] = value
28
+ end
29
+
30
+ def code
31
+ @session["#{@name}_code"]
32
+ end
33
+
34
+ def data=(value)
35
+ @session["#{@name}_data"] = value
36
+ end
37
+
38
+ def data
39
+ @session["#{@name}_data"]
40
+ end
41
+ end
42
+ end
@@ -0,0 +1,25 @@
1
+
2
+ # frozen_string_literal: true
3
+
4
+ class BrowserFactory
5
+ class << self
6
+ def build(session:, url_options:)
7
+ browser = BrowseEverything::Browser.new(url_options)
8
+ browser.providers.each_value do |provider_handler|
9
+ # The authentication token must be set here
10
+ provider_session = BrowseEverythingSession::ProviderSession.for(session: session, name: provider_handler.key.to_sym)
11
+ provider_handler.token = provider_session.token if provider_session.token
12
+ end
13
+ browser
14
+ end
15
+
16
+ def for(name:, url_options: {})
17
+ browser(url_options: url_options).providers[name]
18
+ end
19
+ end
20
+
21
+ def self.browser(url_options: {})
22
+ BrowseEverything::Browser.new(url_options)
23
+ end
24
+ private_class_method :browser
25
+ end
@@ -8,9 +8,59 @@
8
8
  <th role="columnheader">Modified</th>
9
9
  </tr>
10
10
  </thead>
11
- <% provider.contents(browse_path).each_with_index do |file, index| %>
12
- <% next if file.relative_parent_path? or file.size.nil? %>
13
- <%= render :partial => 'file', :locals => { :file => file, :index => index,
14
- :path => browse_everything_engine.contents_path(provider_name, file.id), :parent => params[:parent] } %>
15
- <% end %>
16
- </table>
11
+ <% provider_contents.each_with_index do |file, index| %>
12
+ <% path = browse_everything_engine.contents_path(provider_name, file.id) %>
13
+ <% parent = params[:parent] %>
14
+ <% if file.container? || provider.config[:max_upload_file_size].blank? %>
15
+ <% disabled = false %>
16
+ <% else %>
17
+ <% max_size = provider.config[:max_upload_file_size].to_i %>
18
+ <% disabled = file.size > max_size %>
19
+ <% end %>
20
+
21
+ <tr role="row"
22
+ tabindex="-1"
23
+ data-ev-location="<%= file.location %>"
24
+ data-tt-id="<%= path %>"
25
+ data-tt-parent-id="<%= parent %>"
26
+ data-tt-branch="<%= file.container? ? 'true' : 'false' %>">
27
+
28
+ <td role="gridcell" title="<%= file.name %>" class="<%=file.container? ? 'ev-container' : 'ev-file'%> ev-file-name">
29
+ <% if disabled %>
30
+ <span title="<%= t('browse_everything.size_disabled', max_size: number_to_human_size(max_size)) %>"
31
+ class="<%=file.container? ? 'folder' : 'file'%>" aria-hidden="true">
32
+ <%= file.name %>
33
+ </span>
34
+ <span class="sr-only"><%= file.container? ? ', folder' : ', file' %> </span>
35
+ <% else %>
36
+ <%= link_to browse_everything_engine.contents_path(provider_name, file.id), class: 'ev-link' do %>
37
+ <span class="<%=file.container? ? 'folder' : 'file'%>" aria-hidden="true"/>
38
+ <%= file.name %>
39
+ <span class="sr-only"><%= file.container? ? ', folder' : ', file' %> </span>
40
+ <% end %>
41
+ <% end %>
42
+ </td>
43
+ <% if file.container? %>
44
+ <td role="gridcell" class="ev-directory-select">
45
+ <%= check_box_tag(:select_all, "0", false, class: "ev-select-all") %>
46
+ </td>
47
+ <% else %>
48
+ <td role="gridcell" class="ev-file-select">
49
+ <%= check_box_tag(file.id.to_s.parameterize, "0", false, class: "ev-select-file") %>
50
+ </td>
51
+ <% end %>
52
+
53
+ <td role="gridcell" class="ev-file-size">
54
+ <%= number_to_human_size(file.size).sub(/Bytes/,'bytes') %>
55
+ </td>
56
+
57
+ <td role="gridcell" class="ev-file-kind">
58
+ <%= file.type %>
59
+ </td>
60
+
61
+ <td role="gridcell" class="ev-file-date">
62
+ <%= file.mtime.strftime('%F %R') %>
63
+ </td>
64
+ </tr>
65
+ <% end %>
66
+ </table>
@@ -1,5 +1,6 @@
1
- # coding: utf-8
2
- lib = File.expand_path('../lib', __FILE__)
1
+ # frozen_string_literal: true
2
+
3
+ lib = File.expand_path('lib', __dir__)
3
4
  $LOAD_PATH.unshift(lib) unless $LOAD_PATH.include?(lib)
4
5
  require 'browse_everything/version'
5
6
 
@@ -13,39 +14,42 @@ Gem::Specification.new do |spec|
13
14
  spec.homepage = 'https://github.com/projecthydra/browse-everything'
14
15
  spec.license = 'Apache 2'
15
16
 
16
- spec.files = `git ls-files`.split($/)
17
+ spec.files = `git ls-files -z`.split(/\000/)
17
18
  spec.executables = spec.files.grep(%r{^bin/}) { |f| File.basename(f) }
18
19
  spec.test_files = spec.files.grep(%r{^(test|spec|features)/})
19
20
  spec.require_paths = ['lib']
20
21
 
21
- spec.add_dependency 'rails', '>= 3.1'
22
22
  spec.add_dependency 'addressable', '~> 2.5'
23
- spec.add_dependency 'google_drive'
23
+ spec.add_dependency 'aws-sdk-s3'
24
+ spec.add_dependency 'bootstrap-sass', '~> 3.2'
24
25
  spec.add_dependency 'dropbox_api', '>= 0.1.10'
26
+ spec.add_dependency 'font-awesome-rails'
27
+ spec.add_dependency 'google-api-client', '~> 0.21'
28
+ spec.add_dependency 'google_drive', '~> 2.1'
29
+ spec.add_dependency 'typhoeus'
30
+ spec.add_dependency 'rails', '>= 4.2'
25
31
  spec.add_dependency 'ruby-box'
26
32
  spec.add_dependency 'sass-rails'
27
- spec.add_dependency 'bootstrap-sass', '~> 3.2'
28
- spec.add_dependency 'font-awesome-rails'
29
- spec.add_dependency 'google-api-client', '~> 0.9'
30
- spec.add_dependency 'signet'
31
- spec.add_dependency 'httparty'
32
- spec.add_dependency 'aws-sdk-s3'
33
+ spec.add_dependency 'signet', '~> 0.8'
34
+ spec.add_dependency 'thor', '~> 0.20'
35
+
36
+ spec.add_development_dependency 'bundler', '~> 1.3'
37
+ spec.add_development_dependency 'capybara'
38
+ spec.add_development_dependency 'chromedriver-helper'
39
+ spec.add_development_dependency 'coveralls'
40
+ spec.add_development_dependency 'engine_cart', '~> 2.0'
41
+ spec.add_development_dependency 'factory_bot_rails'
42
+ spec.add_development_dependency 'jasmine', '~> 2.3'
43
+ spec.add_development_dependency 'rails-controller-testing'
44
+ spec.add_development_dependency 'rake'
33
45
  spec.add_development_dependency 'rspec', '~> 3.0'
34
- spec.add_development_dependency 'rspec-rails'
35
46
  spec.add_development_dependency 'rspec-its'
36
- spec.add_development_dependency 'rubocop', '~> 0.42.0'
37
- spec.add_development_dependency 'rubocop-rspec', '~> 1.8.0'
47
+ spec.add_development_dependency 'rspec-rails'
48
+ spec.add_development_dependency 'rubocop', '~> 0.53'
49
+ spec.add_development_dependency 'rubocop-rspec', '~> 1.23'
50
+ spec.add_development_dependency 'selenium-webdriver'
38
51
  spec.add_development_dependency 'simplecov'
39
- spec.add_development_dependency 'bundler', '~> 1.3'
40
- spec.add_development_dependency 'pry'
41
- spec.add_development_dependency 'rake'
42
- spec.add_development_dependency 'webmock'
43
- spec.add_development_dependency 'vcr'
44
52
  spec.add_development_dependency 'sqlite3'
45
- spec.add_development_dependency 'factory_girl_rails'
46
- spec.add_development_dependency 'engine_cart', '~> 1.0'
47
- spec.add_development_dependency 'capybara'
48
- spec.add_development_dependency 'jasmine', '~> 2.3'
49
- spec.add_development_dependency 'coveralls'
50
- spec.add_development_dependency 'poltergeist', '~> 1.10'
53
+ spec.add_development_dependency 'vcr'
54
+ spec.add_development_dependency 'webmock'
51
55
  end
data/config/routes.rb CHANGED
@@ -1,6 +1,11 @@
1
+ # frozen_string_literal: true
2
+
1
3
  BrowseEverything::Engine.routes.draw do
2
4
  get 'connect', to: 'browse_everything#auth', as: 'connector_response'
3
- match 'resolve', to: 'browse_everything#resolve', as: 'resolver', via: [:get, :post]
4
- match ':provider(/*path)', to: 'browse_everything#show', as: 'contents', via: [:get, :post]
5
+ match 'resolve', to: 'browse_everything#resolve', as: 'resolver', via: %i[get post]
6
+ # The "format: false" argument ensures that directory paths containing period characters can be parsed
7
+ # By default, ""/dir1/dir.somedirectory.2" will be parsed as "/dir1/dir" with the format requested as "somedirectory.2"
8
+ # Please see http://guides.rubyonrails.org/routing.html#route-globbing-and-wildcard-segments
9
+ match ':provider(/*path)', to: 'browse_everything#show', as: 'contents', via: %i[get post], format: false
5
10
  root to: 'browse_everything#index'
6
11
  end
@@ -1,3 +1,5 @@
1
+ # frozen_string_literal: true
2
+
1
3
  require 'bootstrap-sass'
2
4
  require 'font-awesome-rails'
3
5
  require 'browse_everything'
@@ -1,13 +1,14 @@
1
+ # frozen_string_literal: true
2
+
1
3
  require 'rails'
2
4
  require 'browse_everything/version'
3
5
  require 'browse_everything/engine'
4
6
  require 'browse_everything/retriever'
5
7
 
6
8
  module BrowseEverything
7
- class InitializationError < RuntimeError; end
8
-
9
9
  autoload :Browser, 'browse_everything/browser'
10
10
  autoload :FileEntry, 'browse_everything/file_entry'
11
+
11
12
  module Driver
12
13
  autoload :Base, 'browse_everything/driver/base'
13
14
  autoload :FileSystem, 'browse_everything/driver/file_system'
@@ -15,28 +16,60 @@ module BrowseEverything
15
16
  autoload :Box, 'browse_everything/driver/box'
16
17
  autoload :GoogleDrive, 'browse_everything/driver/google_drive'
17
18
  autoload :S3, 'browse_everything/driver/s3'
19
+
20
+ # Access the sorter set for the base driver class
21
+ # @return [Proc]
22
+ def sorter
23
+ BrowseEverything::Driver::Base.sorter
24
+ end
25
+
26
+ # Provide a custom sorter for all driver classes
27
+ # @param [Proc] the sorting lambda (or proc)
28
+ def sorter=(sorting_proc)
29
+ BrowseEverything::Driver::Base.sorter = sorting_proc
30
+ end
31
+
32
+ module_function :sorter, :sorter=
18
33
  end
19
34
 
35
+ module Auth
36
+ module Google
37
+ autoload :Credentials, 'browse_everything/auth/google/credentials'
38
+ autoload :RequestParameters, 'browse_everything/auth/google/request_parameters'
39
+ end
40
+ end
41
+
42
+ class InitializationError < RuntimeError; end
43
+ class NotImplementedError < StandardError; end
44
+ class NotAuthorizedError < StandardError; end
45
+
20
46
  class << self
47
+ attr_writer :config
48
+
21
49
  def configure(value)
22
- if value.nil? || value.is_a?(Hash)
23
- @config = value
50
+ return if value.nil?
51
+ if value.is_a?(Hash)
52
+ @config = ActiveSupport::HashWithIndifferentAccess.new value
24
53
  elsif value.is_a?(String)
25
- @config = YAML.load(ERB.new(File.read(value)).result)
26
-
27
- if @config.include? 'drop_box'
28
- warn '[DEPRECATION] `drop_box` is deprecated. Please use `dropbox` instead.'
29
- @config['dropbox'] = @config.delete('drop_box')
30
- end
31
-
54
+ config_file_content = File.read(value)
55
+ config_file_template = ERB.new(config_file_content)
56
+ config_values = YAML.safe_load(config_file_template.result)
57
+ @config = ActiveSupport::HashWithIndifferentAccess.new config_values
58
+ @config.deep_symbolize_keys
32
59
  else
33
60
  raise InitializationError, "Unrecognized configuration: #{value.inspect}"
34
61
  end
62
+
63
+ if @config.include? 'drop_box'
64
+ warn '[DEPRECATION] `drop_box` is deprecated. Please use `dropbox` instead.'
65
+ @config['dropbox'] = @config.delete('drop_box')
66
+ end
35
67
  end
36
68
 
37
69
  def config
38
70
  if @config.nil?
39
- configure(File.join(Rails.root.to_s, 'config', 'browse_everything_providers.yml'))
71
+ config_path = Rails.root.join 'config', 'browse_everything_providers.yml'
72
+ configure config_path.to_s
40
73
  end
41
74
  @config
42
75
  end
@@ -0,0 +1,28 @@
1
+ # frozen_string_literal: true
2
+
3
+ require 'googleauth'
4
+
5
+ # Object structuring the credentials retrieved for the Google API's
6
+ module BrowseEverything
7
+ module Auth
8
+ module Google
9
+ class Credentials < ::Google::Auth::UserRefreshCredentials
10
+ # Ensures that every call to retrieve an access token does *not* require an HTTP request
11
+ # @see Google::Auth::UserRefreshCredentials#fetch_access_token
12
+ # @param options [Hash] the access token values
13
+ def fetch_access_token(options = {})
14
+ return build_token_hash if access_token
15
+ super(options)
16
+ end
17
+
18
+ private
19
+
20
+ # Structure a hash from existing access token values (usually cached within a Cookie)
21
+ # @return [Hash]
22
+ def build_token_hash
23
+ { 'access_token' => access_token }
24
+ end
25
+ end
26
+ end
27
+ end
28
+ end
@@ -0,0 +1,61 @@
1
+ # frozen_string_literal: true
2
+
3
+ # Manages request parameters for the request to the Google Drive API
4
+ module BrowseEverything
5
+ module Auth
6
+ module Google
7
+ class RequestParameters < OpenStruct
8
+ # Overrides the constructor for an OpenStruct instance
9
+ # Provides default parameters
10
+ def initialize(params = {})
11
+ @params = default_params.merge(params)
12
+ super(@params)
13
+ end
14
+
15
+ private
16
+
17
+ # The default query parameters for the Google Drive API
18
+ # @return [Hash]
19
+ def default_params
20
+ {
21
+ q: default_query,
22
+ order_by: 'modifiedTime desc,folder,name',
23
+ fields: 'nextPageToken,files(name,id,mimeType,size,modifiedTime,parents,web_content_link)',
24
+ supports_team_drives: true,
25
+ include_team_drive_items: true,
26
+ corpora: 'user,allTeamDrives',
27
+ page_size: 1000
28
+ }
29
+ end
30
+
31
+ def default_query
32
+ field_queries = []
33
+ contraints.each_pair do |field, constraints|
34
+ field_constraint = constraints.join(" and #{field} ")
35
+ field_queries << "#{field} #{field_constraint}"
36
+ end
37
+ field_queries.join(' ')
38
+ end
39
+
40
+ def contraints
41
+ {
42
+ 'mimeType' => [
43
+ '!= \'application/vnd.google-apps.audio\'',
44
+ '!= \'application/vnd.google-apps.document\'',
45
+ '!= \'application/vnd.google-apps.drawing\'',
46
+ '!= \'application/vnd.google-apps.form\'',
47
+ '!= \'application/vnd.google-apps.fusiontable\'',
48
+ '!= \'application/vnd.google-apps.map\'',
49
+ '!= \'application/vnd.google-apps.photo\'',
50
+ '!= \'application/vnd.google-apps.presentation\'',
51
+ '!= \'application/vnd.google-apps.script\'',
52
+ '!= \'application/vnd.google-apps.site\'',
53
+ '!= \'application/vnd.google-apps.spreadsheet\'',
54
+ '!= \'application/vnd.google-apps.video\''
55
+ ]
56
+ }
57
+ end
58
+ end
59
+ end
60
+ end
61
+ end