boar 1.0.1

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 (50) hide show
  1. checksums.yaml +7 -0
  2. data/.gitignore +7 -0
  3. data/.travis-gemfile +13 -0
  4. data/.travis.yml +10 -0
  5. data/.yardopts +1 -0
  6. data/Gemfile +20 -0
  7. data/README.md +19 -0
  8. data/Rakefile +11 -0
  9. data/app/controllers/boar/application_controller.rb +16 -0
  10. data/app/controllers/boar/downloads_controller.rb +17 -0
  11. data/app/controllers/boar/pages_controller.rb +13 -0
  12. data/app/models/boar/handlers/authentication.rb +38 -0
  13. data/app/models/boar/handlers/downloads/base.rb +56 -0
  14. data/app/models/boar/handlers/downloads/box_net.rb +67 -0
  15. data/app/models/boar/handlers/downloads/dropbox.rb +59 -0
  16. data/app/models/boar/handlers/downloads/google_drive.rb +48 -0
  17. data/app/models/boar/handlers/downloads/local.rb +34 -0
  18. data/app/models/boar/handlers/downloads/remote.rb +24 -0
  19. data/app/models/boar/handlers/downloads/s3.rb +54 -0
  20. data/app/models/boar/handlers/downloads/sky_drive.rb +45 -0
  21. data/app/models/boar/handlers/generic.rb +25 -0
  22. data/app/models/boar/handlers/hosts.rb +28 -0
  23. data/app/models/boar/handlers/locale.rb +25 -0
  24. data/app/models/boar/handlers/path_mapper.rb +15 -0
  25. data/app/models/boar/handlers/root.rb +25 -0
  26. data/app/models/boar/handlers/views.rb +25 -0
  27. data/app/models/boar/services/downloads.rb +118 -0
  28. data/app/models/boar/services/generic.rb +78 -0
  29. data/app/models/boar/services/pages.rb +62 -0
  30. data/app/models/boar/utils/basic.rb +27 -0
  31. data/boar.gemspec +41 -0
  32. data/lib/boar.rb +31 -0
  33. data/lib/boar/configuration.rb +77 -0
  34. data/lib/boar/engine.rb +16 -0
  35. data/lib/boar/exceptions.rb +46 -0
  36. data/lib/boar/providers/base.rb +28 -0
  37. data/lib/boar/providers/box_net.rb +27 -0
  38. data/lib/boar/providers/dropbox.rb +26 -0
  39. data/lib/boar/providers/google_drive.rb +87 -0
  40. data/lib/boar/providers/sky_drive.rb +80 -0
  41. data/lib/boar/router.rb +68 -0
  42. data/lib/boar/version.rb +25 -0
  43. data/lib/tasks/boar.rake +102 -0
  44. data/spec/boar/controllers/application_controller_spec.rb +11 -0
  45. data/spec/boar/controllers/pages_controller_spec.rb +11 -0
  46. data/spec/boar/routes_spec.rb +11 -0
  47. data/spec/boar_spec.rb +7 -0
  48. data/spec/coverage_helper.rb +17 -0
  49. data/spec/spec_helper.rb +15 -0
  50. metadata +264 -0
@@ -0,0 +1,62 @@
1
+ # encoding: utf-8
2
+ #
3
+ # This file is part of the boar gem. Copyright (C) 2013 and above Shogun <shogun_panda@me.com>.
4
+ # Licensed under the MIT license, which can be found at http://www.opensource.org/licenses/mit-license.php.
5
+ #
6
+
7
+ module Boar
8
+ module Services
9
+ class Pages < Boar::Services::Generic
10
+ def main
11
+ # Get the path
12
+ path = @controller.params[:path]
13
+
14
+ # Map the path
15
+ path = self.handler_for(:pages_mapper, options).call(self, path)
16
+
17
+ # Handle ACL for the path
18
+ self.handle_authentication(path, @options)
19
+
20
+ @controller.render(file: search_page(path)) if !@controller.performed?
21
+ end
22
+
23
+ private
24
+ def search_page(path, partial = false, options = nil)
25
+ # Setup path and options
26
+ path = "index" if path.blank?
27
+ options ||= @options
28
+
29
+ # Get the template file
30
+ template = (self.handler_for(:root, options).call + path).to_s
31
+
32
+ begin
33
+ perform_search(template, partial, options)
34
+
35
+ # We return the template if search didn't fail, which means that the #render method will success.
36
+ template
37
+ rescue ActionView::MissingTemplate
38
+ raise Boar::Exceptions::NotFound.new(template)
39
+ end
40
+ end
41
+
42
+ def perform_search(template, partial, options)
43
+ # Get the context
44
+ context = @controller.view_renderer.lookup_context
45
+
46
+ # Setup the locale
47
+ context.locale = options[:add_locale] ? current_locale(options) : nil
48
+
49
+ # Perform the search
50
+ context.with_fallbacks { context.find_template(template, nil, partial, [], {}) }
51
+ end
52
+
53
+ def current_root(options = nil)
54
+ self.handler_for(:root, options).call
55
+ end
56
+
57
+ def current_locale(options = nil)
58
+ self.handler_for(:locale, options).call
59
+ end
60
+ end
61
+ end
62
+ end
@@ -0,0 +1,27 @@
1
+ # encoding: utf-8
2
+ #
3
+ # This file is part of the boar gem. Copyright (C) 2013 and above Shogun <shogun_panda@me.com>.
4
+ # Licensed under the MIT license, which can be found at http://www.opensource.org/licenses/mit-license.php.
5
+ #
6
+
7
+ module Boar
8
+ module Utils
9
+ module Basic
10
+ def ensure_hash(obj, default = {})
11
+ HashWithIndifferentAccess.new(obj.is_a?(Hash) ? obj : default)
12
+ end
13
+
14
+ def get_option(options, key, default = nil)
15
+ begin
16
+ options.symbolize_keys.fetch(key.to_sym)
17
+ rescue Exception => _
18
+ block_given? ? yield(key) : default
19
+ end
20
+ end
21
+
22
+ def interpolate(template, args)
23
+ Mustache.render(template, args)
24
+ end
25
+ end
26
+ end
27
+ end
@@ -0,0 +1,41 @@
1
+ # encoding: utf-8
2
+ #
3
+ # This file is part of the boar gem. Copyright (C) 2013 and above Shogun <shogun_panda@me.com>.
4
+ # Licensed under the MIT license, which can be found at http://www.opensource.org/licenses/mit-license.php.
5
+ #
6
+
7
+ require File.expand_path('../lib/boar/version', __FILE__)
8
+
9
+ Gem::Specification.new do |gem|
10
+ gem.name = "boar"
11
+ gem.version = Boar::Version::STRING
12
+ gem.homepage = "http://github.com/ShogunPanda/boar"
13
+ gem.summary = "A Rails engine to handle local static pages and downloads on the cloud."
14
+ gem.description = "A Rails engine to handle local static pages and downloads on the cloud."
15
+ gem.rubyforge_project = "boar"
16
+
17
+ gem.authors = ["Shogun"]
18
+ gem.email = ["shogun_panda@me.com"]
19
+
20
+ gem.files = `git ls-files`.split($\)
21
+ gem.executables = gem.files.grep(%r{^bin/}).map{ |f| File.basename(f) }
22
+ gem.test_files = gem.files.grep(%r{^(test|spec|features)/})
23
+ gem.require_paths = ["lib"]
24
+
25
+ gem.required_ruby_version = ">= 1.9.3"
26
+
27
+ gem.add_dependency("rails", ">= 3.2.12")
28
+ gem.add_dependency("mustache", "~> 0.99.4")
29
+ gem.add_dependency("mbrao", "~> 1.1.1")
30
+ gem.add_dependency("redis", "~> 3.0.3")
31
+ gem.add_dependency("oj", "~> 2.0.10")
32
+ gem.add_dependency("elephas", "~> 3.0.0")
33
+ gem.add_dependency("clavem", "~> 1.2.2")
34
+
35
+ # Downloads gem
36
+ gem.add_dependency("dropbox-sdk", "~> 1.5.1")
37
+ gem.add_dependency("google-api-client", "~> 0.6.3")
38
+ gem.add_dependency("aws-sdk", "~> 1.9.5")
39
+ gem.add_dependency("ruby-box", "~> 1.2.0")
40
+ gem.add_dependency("skydrive", "~> 0.1.0")
41
+ end
@@ -0,0 +1,31 @@
1
+ # encoding: utf-8
2
+ #
3
+ # This file is part of the boar gem. Copyright (C) 2013 and above Shogun <shogun_panda@me.com>.
4
+ # Licensed under the MIT license, which can be found at http://www.opensource.org/licenses/mit-license.php.
5
+ #
6
+
7
+ # TODO: Simplify code in ALL classes.
8
+ # TODO: Rename #call to #perform everywhere.
9
+
10
+ require "mbrao"
11
+ require "mustache"
12
+ require "mime-types"
13
+ require "redis"
14
+ require "oj"
15
+ require "clavem"
16
+ require "dropbox_sdk"
17
+ require "google/api_client"
18
+ require "aws-sdk"
19
+ require "ruby-box"
20
+ require "skydrive"
21
+
22
+ require "boar/version" if !defined?(Boar::Version)
23
+ require "boar/configuration"
24
+ require "boar/router"
25
+ require "boar/engine"
26
+ require "boar/exceptions"
27
+ require "boar/providers/base"
28
+ require "boar/providers/dropbox"
29
+ require "boar/providers/google_drive"
30
+ require "boar/providers/box_net"
31
+ require "boar/providers/sky_drive"
@@ -0,0 +1,77 @@
1
+ # encoding: utf-8
2
+ #
3
+ # This file is part of the boar gem. Copyright (C) 2013 and above Shogun <shogun_panda@me.com>.
4
+ # Licensed under the MIT license, which can be found at http://www.opensource.org/licenses/mit-license.php.
5
+ #
6
+
7
+ # TODO: Embed the specific configuration here, and then use a get("key.subkey") interface
8
+ # TODO: Reorganize all options
9
+
10
+ module Boar
11
+ class Configuration
12
+ def initialize(app)
13
+ @data = {
14
+ app: app,
15
+ backend: Redis.new,
16
+ skip_cache_param: "sc",
17
+ handlers: {
18
+ root: Boar::Handlers::Root,
19
+ authentication: Boar::Handlers::Authentication,
20
+ locale: Boar::Handlers::Locale,
21
+ views: Boar::Handlers::Views,
22
+ hosts: Boar::Handlers::Hosts
23
+ }
24
+ }
25
+
26
+ initialize_pages()
27
+ initialize_downloads()
28
+ end
29
+
30
+ def backend_key(key, _, request)
31
+ host = self.handlers[:hosts].new.call(request)
32
+ "boar[#{host}]:#{key}"
33
+ end
34
+
35
+ def method_missing(method, *args, &block)
36
+ key = method.to_sym
37
+
38
+ if @data.has_key?(key) then
39
+ rv = @data[key]
40
+ rv = HashWithIndifferentAccess.new(rv) if rv.is_a?(Hash)
41
+ rv
42
+ else
43
+ super
44
+ end
45
+ end
46
+
47
+ private
48
+ def initialize_pages
49
+ @data.merge!({
50
+ pages_root: "",
51
+ pages_directory: "{{root}}/app/pages",
52
+ locale_param: :locale,
53
+ add_locale: true,
54
+ default_locale: @data[:app].config.i18n.default_locale || "en",
55
+ locale_regexp: /[a-z]{2}((_([a-zA-Z]{2}))?)/,
56
+ views: {
57
+ authentication: "authentication",
58
+ error: "errors/{{code}}"
59
+ }
60
+ })
61
+
62
+ @data[:handlers].merge!({pages_mapper: Boar::Handlers::PathMapper})
63
+ end
64
+
65
+ def initialize_downloads
66
+ @data.merge!({
67
+ downloads_root: "downloads",
68
+ downloads_directory: "{{root}}/files",
69
+ config_file: "{{root}}/config/downloads.yml",
70
+ credentials_file: "{{root}}/config/credentials.yml",
71
+ default_provider: :local
72
+ })
73
+
74
+ @data[:handlers].merge!({downloads_mapper: Boar::Handlers::PathMapper})
75
+ end
76
+ end
77
+ end
@@ -0,0 +1,16 @@
1
+ # encoding: utf-8
2
+ #
3
+ # This file is part of the boar gem. Copyright (C) 2013 and above Shogun <shogun_panda@me.com>.
4
+ # Licensed under the MIT license, which can be found at http://www.opensource.org/licenses/mit-license.php.
5
+ #
6
+
7
+ module Boar
8
+ class Engine < ::Rails::Engine
9
+ isolate_namespace Boar
10
+ mattr_accessor :backend
11
+
12
+ initializer "boar.configuration" do |app|
13
+ app.config.boar = Boar::Configuration.new(app)
14
+ end
15
+ end
16
+ end
@@ -0,0 +1,46 @@
1
+ # encoding: utf-8
2
+ #
3
+ # This file is part of the boar gem. Copyright (C) 2013 and above Shogun <shogun_panda@me.com>.
4
+ # Licensed under the MIT license, which can be found at http://www.opensource.org/licenses/mit-license.php.
5
+ #
6
+
7
+ module Boar
8
+ module Exceptions
9
+ class Error < ::Exception
10
+ attr_reader :code
11
+
12
+ def initialize(code, message = nil)
13
+ super(message)
14
+ @code = code
15
+ end
16
+
17
+ def self.must_raise?(exception)
18
+ exception.is_a?(Lazier::Exceptions::Dump) && Rails.env.development?
19
+ end
20
+ end
21
+
22
+ class ServerError < Error
23
+ def initialize(message)
24
+ super(500, message)
25
+ end
26
+ end
27
+
28
+ class UnImplemented < Error
29
+ def initialize(message = nil)
30
+ super(501, message)
31
+ end
32
+ end
33
+
34
+ class AuthorizationFailed < Error
35
+ def initialize(message = nil)
36
+ super(403, message)
37
+ end
38
+ end
39
+
40
+ class NotFound < Error
41
+ def initialize(message = nil)
42
+ super(404, message)
43
+ end
44
+ end
45
+ end
46
+ end
@@ -0,0 +1,28 @@
1
+ # encoding: utf-8
2
+ #
3
+ # This file is part of the boar gem. Copyright (C) 2013 and above Shogun <shogun_panda@me.com>.
4
+ # Licensed under the MIT license, which can be found at http://www.opensource.org/licenses/mit-license.php.
5
+ #
6
+
7
+ module Boar
8
+ module Providers
9
+ class Base
10
+ def redirect_for_authentication(_, _)
11
+ raise Boar::Exceptions::UnImplemented
12
+ end
13
+
14
+ def get_credentials(_, _, _)
15
+ raise Boar::Exceptions::UnImplemented
16
+ end
17
+
18
+ def update_credentials(credentials, params)
19
+ params[:all][params[:host]][params[:name]] = params[:single].merge(credentials).to_hash
20
+ open(params[:path], "w") {|f| f.write(params[:all].to_yaml) }
21
+ end
22
+
23
+ def search_file(_, _)
24
+ false
25
+ end
26
+ end
27
+ end
28
+ end
@@ -0,0 +1,27 @@
1
+ # encoding: utf-8
2
+ #
3
+ # This file is part of the boar gem. Copyright (C) 2013 and above Shogun <shogun_panda@me.com>.
4
+ # Licensed under the MIT license, which can be found at http://www.opensource.org/licenses/mit-license.php.
5
+ #
6
+
7
+ module Boar
8
+ module Providers
9
+ class BoxNet < Base
10
+ def redirect_for_authentication(authorizer, configuration)
11
+ @session = RubyBox::Session.new({client_id: configuration[:client_id], client_secret: configuration[:client_secret]})
12
+ @session.authorize_url(authorizer.callback_url)
13
+ end
14
+
15
+ def get_credentials(authorizer, request, response)
16
+ begin
17
+ raise Clavem::Exceptions::AuthorizationDenied if request.query["error"].present?
18
+
19
+ token = @session.get_access_token(request.query["code"])
20
+ {access_token: token.token, refresh_token: token.refresh_token}
21
+ rescue RuntimeError
22
+ nil
23
+ end
24
+ end
25
+ end
26
+ end
27
+ end
@@ -0,0 +1,26 @@
1
+ # encoding: utf-8
2
+ #
3
+ # This file is part of the boar gem. Copyright (C) 2013 and above Shogun <shogun_panda@me.com>.
4
+ # Licensed under the MIT license, which can be found at http://www.opensource.org/licenses/mit-license.php.
5
+ #
6
+
7
+ module Boar
8
+ module Providers
9
+ class Dropbox < Base
10
+ def redirect_for_authentication(authorizer, configuration)
11
+ @session = DropboxSession.new(configuration[:app_key], configuration[:app_secret])
12
+ @session.get_request_token
13
+ @session.get_authorize_url + "&oauth_callback=#{authorizer.callback_url}"
14
+ end
15
+
16
+ def get_credentials(authorizer, request, response)
17
+ begin
18
+ @session.get_access_token
19
+ {session: @session.serialize}
20
+ rescue DropboxAuthError
21
+ nil
22
+ end
23
+ end
24
+ end
25
+ end
26
+ end
@@ -0,0 +1,87 @@
1
+ # encoding: utf-8
2
+ #
3
+ # This file is part of the boar gem. Copyright (C) 2013 and above Shogun <shogun_panda@me.com>.
4
+ # Licensed under the MIT license, which can be found at http://www.opensource.org/licenses/mit-license.php.
5
+ #
6
+
7
+ module Boar
8
+ module Providers
9
+ class GoogleDrive < Base
10
+ NAME = "boar"
11
+ VERSION = "1.0.0"
12
+ SCOPES = ["https://www.googleapis.com/auth/drive"]
13
+
14
+ def self.client(configuration)
15
+ rv = Google::APIClient.new(authorization: :oauth_2, application_name: Boar::Providers::GoogleDrive::NAME, application_version: Boar::Providers::GoogleDrive::VERSION)
16
+ rv.authorization.client_id = configuration[:client_id]
17
+ rv.authorization.client_secret = configuration[:client_secret]
18
+ rv.authorization.scope = Boar::Providers::GoogleDrive::SCOPES
19
+ rv
20
+ end
21
+
22
+ def self.authorized_client(configuration)
23
+ authorizer = Clavem::Authorizer.new(configuration.fetch(:authorizer_host, "localhost"), configuration.fetch(:authorizer_port, "2501"))
24
+
25
+ client = Boar::Providers::GoogleDrive.client(configuration)
26
+ client.authorization.redirect_uri = authorizer.callback_url
27
+ client.authorization.refresh_token = configuration["token"]
28
+ client.authorization.fetch_access_token!
29
+
30
+ client
31
+ end
32
+
33
+ def redirect_for_authentication(authorizer, configuration)
34
+ @client = Boar::Providers::GoogleDrive.client(configuration)
35
+ @client.authorization.redirect_uri = authorizer.callback_url
36
+ @client.authorization.authorization_uri
37
+ end
38
+
39
+ def get_credentials(authorizer, request, response)
40
+ begin
41
+ raise Clavem::Exceptions::AuthorizationDenied if request.query["error"].present? || request.query["code"].blank?
42
+
43
+ @client.authorization.code = request.query["code"].to_s
44
+ @client.authorization.fetch_access_token!
45
+ {token: @client.authorization.refresh_token}
46
+ rescue RuntimeError
47
+ nil
48
+ end
49
+ end
50
+
51
+ def search_file(path, params)
52
+ rv = nil
53
+
54
+ # Split path
55
+ Lazier.load_pathname
56
+ filename = File.basename(path)
57
+ tree = Pathname.new(File.dirname(path)).components
58
+
59
+ # Initialize client
60
+ client = Boar::Providers::GoogleDrive.authorized_client(params[:single])
61
+ api = client.discovered_api('drive', 'v2')
62
+
63
+ # Find the last folder
64
+ parent = "root"
65
+ tree.each do |folder|
66
+ list = client.execute(api_method: api.children.list, parameters: {"folderId" => parent, "q" => "title='#{folder}'", "maxResults" => 1})
67
+ parent = validate_file_entry(list)
68
+ break if !parent
69
+ end
70
+
71
+ # We have the container, query for the file
72
+ if parent then
73
+ list = client.execute(api_method: api.files.list, parameters: {"folderId" => parent, "q" => "title='#{filename}'", "maxResults" => 1})
74
+ rv = validate_file_entry(list)
75
+ end
76
+
77
+ rv ? {"id" => rv} : nil
78
+ end
79
+
80
+ private
81
+ def validate_file_entry(results)
82
+ items = results.data.items
83
+ items.present? ? items[0]["id"] : nil
84
+ end
85
+ end
86
+ end
87
+ end