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.
- checksums.yaml +5 -5
- data/.rubocop.yml +61 -9
- data/.rubocop_todo.yml +2 -15
- data/.travis.yml +19 -19
- data/CONTRIBUTING.md +6 -6
- data/Gemfile +12 -8
- data/README.md +30 -0
- data/Rakefile +2 -1
- data/app/assets/javascripts/browse_everything/behavior.js.coffee +5 -0
- data/app/controllers/browse_everything_controller.rb +75 -23
- data/app/helpers/browse_everything_helper.rb +2 -8
- data/app/helpers/font_awesome_version_helper.rb +9 -8
- data/app/services/browse_everything_session.rb +10 -0
- data/app/services/browse_everything_session/provider_session.rb +42 -0
- data/app/services/browser_factory.rb +25 -0
- data/app/views/browse_everything/_files.html.erb +56 -6
- data/browse-everything.gemspec +29 -25
- data/config/routes.rb +7 -2
- data/lib/browse-everything.rb +2 -0
- data/lib/browse_everything.rb +45 -12
- data/lib/browse_everything/auth/google/credentials.rb +28 -0
- data/lib/browse_everything/auth/google/request_parameters.rb +61 -0
- data/lib/browse_everything/browser.rb +11 -4
- data/lib/browse_everything/driver/authentication_factory.rb +22 -0
- data/lib/browse_everything/driver/base.rb +72 -19
- data/lib/browse_everything/driver/box.rb +46 -17
- data/lib/browse_everything/driver/dropbox.rb +36 -10
- data/lib/browse_everything/driver/file_system.rb +14 -26
- data/lib/browse_everything/driver/google_drive.rb +187 -54
- data/lib/browse_everything/driver/s3.rb +81 -75
- data/lib/browse_everything/engine.rb +3 -2
- data/lib/browse_everything/file_entry.rb +3 -1
- data/lib/browse_everything/retriever.rb +103 -31
- data/lib/browse_everything/version.rb +3 -1
- data/lib/generators/browse_everything/assets_generator.rb +3 -2
- data/lib/generators/browse_everything/config_generator.rb +11 -9
- data/lib/generators/browse_everything/install_generator.rb +3 -2
- data/lib/generators/browse_everything/templates/browse_everything_providers.yml.example +12 -11
- data/spec/controllers/browse_everything_controller_spec.rb +80 -0
- data/spec/features/select_files_spec.rb +13 -13
- data/spec/features/test_compiling_stylesheets_spec.rb +2 -0
- data/spec/fixtures/vcr_cassettes/google_drive.yml +331 -0
- data/spec/fixtures/vcr_cassettes/retriever.yml +93 -0
- data/spec/helper/browse_everything_controller_helper_spec.rb +21 -7
- data/spec/javascripts/jasmine_spec.rb +2 -0
- data/spec/javascripts/support/jasmine_helper.rb +1 -0
- data/spec/lib/browse_everything/auth/google/credentials_spec.rb +41 -0
- data/spec/{unit → lib/browse_everything}/browse_everything_helper_spec.rb +2 -0
- data/spec/lib/browse_everything/browser_spec.rb +109 -0
- data/spec/{unit → lib/browse_everything/driver}/base_spec.rb +5 -4
- data/spec/{unit → lib/browse_everything/driver}/box_spec.rb +20 -5
- data/spec/{unit → lib/browse_everything/driver}/dropbox_spec.rb +15 -18
- data/spec/{unit → lib/browse_everything/driver}/file_system_spec.rb +32 -26
- data/spec/lib/browse_everything/driver/google_drive_spec.rb +171 -0
- data/spec/{unit → lib/browse_everything/driver}/s3_spec.rb +38 -21
- data/spec/lib/browse_everything/driver_spec.rb +38 -0
- data/spec/{unit → lib/browse_everything}/file_entry_spec.rb +4 -1
- data/spec/lib/browse_everything/retriever_spec.rb +200 -0
- data/spec/lib/browse_everything_spec.rb +67 -0
- data/spec/services/browse_everything_session/provider_session_spec.rb +50 -0
- data/spec/services/browser_factory_spec.rb +40 -0
- data/spec/spec_helper.rb +39 -18
- data/spec/support/app/controllers/file_handler_controller.rb +4 -4
- data/spec/support/app/views/file_handler/main.html.erb +1 -1
- data/spec/support/capybara.rb +17 -0
- data/spec/support/rake.rb +3 -1
- data/spec/support/wait_for_ajax.rb +14 -0
- data/spec/test_app_templates/Gemfile.extra +1 -0
- data/spec/test_app_templates/lib/generators/test_app_generator.rb +10 -4
- data/spec/views/browse_everything/{_file.html.erb_spec.rb → _files.html.erb_spec.rb} +24 -18
- data/tasks/ci.rake +2 -0
- metadata +159 -107
- data/app/views/browse_everything/_file.html.erb +0 -52
- data/app/views/browse_everything/resolve.html.erb +0 -1
- data/spec/unit/browser_spec.rb +0 -76
- data/spec/unit/retriever_spec.rb +0 -109
@@ -1,3 +1,5 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
1
3
|
module BrowseEverything
|
2
4
|
class Browser
|
3
5
|
attr_reader :providers
|
@@ -11,15 +13,20 @@ module BrowseEverything
|
|
11
13
|
opts = BrowseEverything.config
|
12
14
|
end
|
13
15
|
|
14
|
-
@providers =
|
15
|
-
opts.each_pair do |
|
16
|
+
@providers = ActiveSupport::HashWithIndifferentAccess.new
|
17
|
+
opts.each_pair do |driver_key, config|
|
16
18
|
begin
|
17
|
-
|
18
|
-
|
19
|
+
driver = driver_key.to_s
|
20
|
+
driver_klass = BrowseEverything::Driver.const_get((config[:driver] || driver).camelize.to_sym)
|
21
|
+
@providers[driver_key] = driver_klass.new(config.merge(url_options: url_options))
|
19
22
|
rescue NameError
|
20
23
|
Rails.logger.warn "Unknown provider: #{driver}"
|
21
24
|
end
|
22
25
|
end
|
23
26
|
end
|
27
|
+
|
28
|
+
def first_provider
|
29
|
+
@providers.to_hash.each_value.to_a.first
|
30
|
+
end
|
24
31
|
end
|
25
32
|
end
|
@@ -0,0 +1,22 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module BrowseEverything
|
4
|
+
module Driver
|
5
|
+
# Class for instantiating authentication API Objects
|
6
|
+
class AuthenticationFactory
|
7
|
+
# Constructor
|
8
|
+
# @param klass [Class] the authentication object class
|
9
|
+
# @param params [Array, Hash] the parameters for the authentication constructor
|
10
|
+
def initialize(klass, *params)
|
11
|
+
@klass = klass
|
12
|
+
@params = params
|
13
|
+
end
|
14
|
+
|
15
|
+
# Constructs an authentication Object
|
16
|
+
# @return [Object]
|
17
|
+
def authenticate
|
18
|
+
@klass.new(*@params)
|
19
|
+
end
|
20
|
+
end
|
21
|
+
end
|
22
|
+
end
|
@@ -1,66 +1,119 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
1
3
|
module BrowseEverything
|
2
4
|
module Driver
|
5
|
+
# Abstract class for provider classes
|
3
6
|
class Base
|
4
7
|
include BrowseEverything::Engine.routes.url_helpers
|
5
8
|
|
6
|
-
|
7
|
-
attr_accessor :token
|
9
|
+
# Provide accessor and mutator methods for @token and @code
|
10
|
+
attr_accessor :token, :code
|
11
|
+
|
12
|
+
# Integrate sorting lambdas for configuration using initializers
|
13
|
+
class << self
|
14
|
+
attr_accessor :sorter
|
15
|
+
|
16
|
+
# Provide a default sorting lambda
|
17
|
+
# @return [Proc]
|
18
|
+
def default_sorter
|
19
|
+
lambda { |files|
|
20
|
+
files.sort do |a, b|
|
21
|
+
if b.container?
|
22
|
+
a.container? ? a.name.downcase <=> b.name.downcase : 1
|
23
|
+
else
|
24
|
+
a.container? ? -1 : a.name.downcase <=> b.name.downcase
|
25
|
+
end
|
26
|
+
end
|
27
|
+
}
|
28
|
+
end
|
29
|
+
|
30
|
+
# Set the sorter lambda (or proc) for all subclasses
|
31
|
+
# (see Class.inherited)
|
32
|
+
# @param subclass [Class] the class inheriting from BrowseEverything::Driver::Base
|
33
|
+
def inherited(subclass)
|
34
|
+
subclass.sorter = sorter
|
35
|
+
end
|
36
|
+
end
|
8
37
|
|
9
|
-
|
10
|
-
|
38
|
+
# Constructor
|
39
|
+
# @param config_values [Hash] configuration for the driver
|
40
|
+
def initialize(config_values)
|
41
|
+
@config = config_values
|
42
|
+
@sorter = self.class.sorter || self.class.default_sorter
|
11
43
|
validate_config
|
12
44
|
end
|
13
45
|
|
46
|
+
# Ensure that the configuration Hash has indifferent access
|
47
|
+
# @return [ActiveSupport::HashWithIndifferentAccess]
|
48
|
+
def config
|
49
|
+
@config = ActiveSupport::HashWithIndifferentAccess.new(@config) if @config.is_a? Hash
|
50
|
+
@config
|
51
|
+
end
|
52
|
+
|
53
|
+
# Abstract method
|
54
|
+
def validate_config; end
|
55
|
+
|
56
|
+
# Generate the key for the driver
|
57
|
+
# @return [String]
|
14
58
|
def key
|
15
59
|
self.class.name.split(/::/).last.underscore
|
16
60
|
end
|
17
61
|
|
62
|
+
# Generate the icon markup for the driver
|
63
|
+
# @return [String]
|
18
64
|
def icon
|
19
65
|
'unchecked'
|
20
66
|
end
|
21
67
|
|
68
|
+
# Generate the name for the driver
|
69
|
+
# @return [String]
|
22
70
|
def name
|
23
71
|
@name ||= (@config[:name] || self.class.name.split(/::/).last.titleize)
|
24
72
|
end
|
25
73
|
|
26
|
-
|
27
|
-
|
28
|
-
|
29
|
-
def contents(_path)
|
74
|
+
# Abstract method
|
75
|
+
def contents(*_args)
|
30
76
|
[]
|
31
77
|
end
|
32
78
|
|
33
|
-
|
34
|
-
|
35
|
-
|
36
|
-
|
79
|
+
# Generate the link for a resource at a given path
|
80
|
+
# @param path [String] the path to the resource
|
81
|
+
# @return [Array<String, Hash>]
|
37
82
|
def link_for(path)
|
38
83
|
[path, { file_name: File.basename(path) }]
|
39
84
|
end
|
40
85
|
|
86
|
+
# Abstract method
|
41
87
|
def authorized?
|
42
88
|
false
|
43
89
|
end
|
44
90
|
|
45
|
-
#
|
91
|
+
# Abstract method
|
46
92
|
def auth_link
|
47
93
|
[]
|
48
94
|
end
|
49
95
|
|
50
|
-
|
96
|
+
# Abstract method
|
97
|
+
def connect(*_args)
|
51
98
|
nil
|
52
99
|
end
|
53
100
|
|
54
101
|
private
|
55
102
|
|
56
|
-
|
57
|
-
connector_response_url(callback_options)
|
58
|
-
end
|
59
|
-
|
103
|
+
# Generate the options for the Rails URL generation for API callbacks
|
60
104
|
# remove the script_name parameter from the url_options since that is causing issues
|
61
105
|
# with the route not containing the engine path in rails 4.2.0
|
106
|
+
# @return [Hash]
|
62
107
|
def callback_options
|
63
|
-
config
|
108
|
+
options = config.to_hash
|
109
|
+
options.deep_symbolize_keys!
|
110
|
+
options[:url_options].reject { |k, _v| k == :script_name }
|
111
|
+
end
|
112
|
+
|
113
|
+
# Generate the URL for the API callback
|
114
|
+
# @return [String]
|
115
|
+
def callback
|
116
|
+
connector_response_url(callback_options)
|
64
117
|
end
|
65
118
|
end
|
66
119
|
end
|
@@ -1,18 +1,36 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
require 'ruby-box'
|
4
|
+
require_relative 'authentication_factory'
|
5
|
+
|
1
6
|
module BrowseEverything
|
2
7
|
module Driver
|
3
8
|
# Driver for accessing the Box API (https://www.box.com/home)
|
4
9
|
class Box < Base
|
5
|
-
require 'ruby-box'
|
6
|
-
|
7
10
|
ITEM_LIMIT = 99999
|
8
11
|
|
12
|
+
class << self
|
13
|
+
attr_accessor :authentication_klass
|
14
|
+
|
15
|
+
def default_authentication_klass
|
16
|
+
RubyBox::Session
|
17
|
+
end
|
18
|
+
end
|
19
|
+
|
20
|
+
# Constructor
|
21
|
+
# @param config_values [Hash] configuration for the driver
|
22
|
+
def initialize(config_values)
|
23
|
+
self.class.authentication_klass ||= self.class.default_authentication_klass
|
24
|
+
super(config_values)
|
25
|
+
end
|
26
|
+
|
9
27
|
def icon
|
10
28
|
'cloud'
|
11
29
|
end
|
12
30
|
|
13
31
|
def validate_config
|
14
32
|
return if config[:client_id] && config[:client_secret]
|
15
|
-
raise
|
33
|
+
raise InitializationError, 'Box driver requires both :client_id and :client_secret argument'
|
16
34
|
end
|
17
35
|
|
18
36
|
# @param [String] id of the file or folder in Box
|
@@ -20,16 +38,16 @@ module BrowseEverything
|
|
20
38
|
def contents(id = '')
|
21
39
|
if id.empty?
|
22
40
|
folder = box_client.root_folder
|
23
|
-
|
41
|
+
@entries = []
|
24
42
|
else
|
25
43
|
folder = box_client.folder_by_id(id)
|
26
|
-
|
44
|
+
@entries = [parent_directory(folder)]
|
27
45
|
end
|
28
46
|
|
29
|
-
folder.items(ITEM_LIMIT, 0, %w
|
30
|
-
|
47
|
+
folder.items(ITEM_LIMIT, 0, %w[name size created_at]).collect do |f|
|
48
|
+
@entries << directory_entry(f)
|
31
49
|
end
|
32
|
-
|
50
|
+
@sorter.call(@entries)
|
33
51
|
end
|
34
52
|
|
35
53
|
# @param [String] id of the file in Box
|
@@ -68,17 +86,28 @@ module BrowseEverything
|
|
68
86
|
|
69
87
|
def box_client
|
70
88
|
if token_expired?
|
71
|
-
session = box_session
|
89
|
+
session = box_session
|
72
90
|
register_access_token(session.refresh_token(box_refresh_token))
|
73
91
|
end
|
74
|
-
RubyBox::Client.new(box_session
|
92
|
+
RubyBox::Client.new(box_session)
|
93
|
+
end
|
94
|
+
|
95
|
+
def session
|
96
|
+
AuthenticationFactory.new(
|
97
|
+
self.class.authentication_klass,
|
98
|
+
client_id: config[:client_id],
|
99
|
+
client_secret: config[:client_secret],
|
100
|
+
access_token: box_token,
|
101
|
+
refresh_token: box_refresh_token
|
102
|
+
)
|
103
|
+
end
|
104
|
+
|
105
|
+
def authenticate
|
106
|
+
session.authenticate
|
75
107
|
end
|
76
108
|
|
77
|
-
def box_session
|
78
|
-
|
79
|
-
client_secret: config[:client_secret],
|
80
|
-
access_token: token,
|
81
|
-
refresh_token: refresh_token)
|
109
|
+
def box_session
|
110
|
+
authenticate
|
82
111
|
end
|
83
112
|
|
84
113
|
# If there is an active session, {@token} will be set by {BrowseEverythingController} using data stored in the
|
@@ -114,8 +143,8 @@ module BrowseEverything
|
|
114
143
|
BrowseEverything::FileEntry.new(Pathname(folder.name).join('..'), '', '..', 0, Time.current, true)
|
115
144
|
end
|
116
145
|
|
117
|
-
def directory_entry(
|
118
|
-
BrowseEverything::FileEntry.new(
|
146
|
+
def directory_entry(file)
|
147
|
+
BrowseEverything::FileEntry.new(file.id, "#{key}:#{file.id}", file.name, file.size, file.created_at, file.type == 'folder')
|
119
148
|
end
|
120
149
|
end
|
121
150
|
end
|
@@ -1,4 +1,7 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
1
3
|
require 'dropbox_api'
|
4
|
+
require_relative 'authentication_factory'
|
2
5
|
|
3
6
|
module BrowseEverything
|
4
7
|
module Driver
|
@@ -49,23 +52,34 @@ module BrowseEverything
|
|
49
52
|
end
|
50
53
|
end
|
51
54
|
|
55
|
+
class << self
|
56
|
+
attr_accessor :authentication_klass
|
57
|
+
|
58
|
+
def default_authentication_klass
|
59
|
+
DropboxApi::Authenticator
|
60
|
+
end
|
61
|
+
end
|
62
|
+
|
63
|
+
# Constructor
|
64
|
+
# @param config_values [Hash] configuration for the driver
|
65
|
+
def initialize(config_values)
|
66
|
+
self.class.authentication_klass ||= self.class.default_authentication_klass
|
67
|
+
super(config_values)
|
68
|
+
end
|
69
|
+
|
52
70
|
def icon
|
53
71
|
'dropbox'
|
54
72
|
end
|
55
73
|
|
56
74
|
def validate_config
|
57
|
-
raise
|
58
|
-
raise
|
75
|
+
raise InitializationError, 'Dropbox driver requires a :client_id argument' unless config[:client_id]
|
76
|
+
raise InitializationError, 'Dropbox driver requires a :client_secret argument' unless config[:client_secret]
|
59
77
|
end
|
60
78
|
|
61
79
|
def contents(path = '')
|
62
|
-
|
63
|
-
|
64
|
-
|
65
|
-
|
66
|
-
def details(path)
|
67
|
-
metadata = client.get_metadata(path)
|
68
|
-
FileEntryFactory.build(metadata: metadata, key: key)
|
80
|
+
response = client.list_folder(path)
|
81
|
+
@entries = response.entries.map { |entry| FileEntryFactory.build(metadata: entry, key: key) }
|
82
|
+
@sorter.call(@entries)
|
69
83
|
end
|
70
84
|
|
71
85
|
def download(path)
|
@@ -102,8 +116,20 @@ module BrowseEverything
|
|
102
116
|
|
103
117
|
private
|
104
118
|
|
119
|
+
def session
|
120
|
+
AuthenticationFactory.new(
|
121
|
+
self.class.authentication_klass,
|
122
|
+
config[:client_id],
|
123
|
+
config[:client_secret]
|
124
|
+
)
|
125
|
+
end
|
126
|
+
|
127
|
+
def authenticate
|
128
|
+
session.authenticate
|
129
|
+
end
|
130
|
+
|
105
131
|
def authenticator
|
106
|
-
@authenticator ||=
|
132
|
+
@authenticator ||= authenticate
|
107
133
|
end
|
108
134
|
|
109
135
|
def client
|
@@ -1,3 +1,5 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
1
3
|
module BrowseEverything
|
2
4
|
module Driver
|
3
5
|
class FileSystem < Base
|
@@ -6,20 +8,18 @@ module BrowseEverything
|
|
6
8
|
end
|
7
9
|
|
8
10
|
def validate_config
|
9
|
-
|
10
|
-
raise BrowseEverything::InitializationError, 'FileSystem driver requires a :home argument'
|
11
|
+
raise BrowseEverything::InitializationError, 'FileSystem driver requires a :home argument' if config[:home].blank?
|
11
12
|
end
|
12
13
|
|
13
14
|
def contents(path = '')
|
14
|
-
|
15
|
-
|
16
|
-
|
17
|
-
|
18
|
-
|
19
|
-
|
20
|
-
end
|
15
|
+
real_path = File.join(config[:home], path)
|
16
|
+
@entries = if File.directory?(real_path)
|
17
|
+
make_directory_entry real_path
|
18
|
+
else
|
19
|
+
[details(real_path)]
|
20
|
+
end
|
21
21
|
|
22
|
-
|
22
|
+
@sorter.call(@entries)
|
23
23
|
end
|
24
24
|
|
25
25
|
def link_for(path)
|
@@ -33,7 +33,7 @@ module BrowseEverything
|
|
33
33
|
end
|
34
34
|
|
35
35
|
def details(path, display = File.basename(path))
|
36
|
-
return nil unless File.exist?
|
36
|
+
return nil unless File.exist? path
|
37
37
|
info = File::Stat.new(path)
|
38
38
|
BrowseEverything::FileEntry.new(
|
39
39
|
make_pathname(path),
|
@@ -47,31 +47,19 @@ module BrowseEverything
|
|
47
47
|
|
48
48
|
private
|
49
49
|
|
50
|
-
def make_directory_entry(
|
50
|
+
def make_directory_entry(real_path)
|
51
51
|
entries = []
|
52
|
-
if relative_path.present?
|
53
|
-
entries << details(File.expand_path('..', real_path), '..')
|
54
|
-
end
|
55
52
|
entries + Dir[File.join(real_path, '*')].collect { |f| details(f) }
|
56
53
|
end
|
57
54
|
|
58
|
-
def sort_entries(entries)
|
59
|
-
entries.sort do |a, b|
|
60
|
-
if b.container?
|
61
|
-
a.container? ? a.name.downcase <=> b.name.downcase : 1
|
62
|
-
else
|
63
|
-
a.container? ? -1 : a.name.downcase <=> b.name.downcase
|
64
|
-
end
|
65
|
-
end
|
66
|
-
end
|
67
|
-
|
68
55
|
def make_pathname(path)
|
69
56
|
Pathname.new(File.expand_path(path)).relative_path_from(Pathname.new(config[:home]))
|
70
57
|
end
|
71
58
|
|
72
59
|
def file_size(path)
|
73
60
|
File.size(path).to_i
|
74
|
-
rescue
|
61
|
+
rescue StandardError => error
|
62
|
+
Rails.logger.error "Failed to find the file size for #{path}: #{error}"
|
75
63
|
0
|
76
64
|
end
|
77
65
|
end
|