fog-local 0.1.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 +7 -0
- data/.gitignore +15 -0
- data/.travis.yml +9 -0
- data/CONTRIBUTING.md +18 -0
- data/CONTRIBUTORS.md +18 -0
- data/Gemfile +4 -0
- data/LICENSE.md +20 -0
- data/README.md +49 -0
- data/Rakefile +8 -0
- data/fog-local.gemspec +27 -0
- data/lib/fog-local.rb +1 -0
- data/lib/fog/bin/local.rb +29 -0
- data/lib/fog/local.rb +1 -0
- data/lib/fog/local/core.rb +9 -0
- data/lib/fog/local/models/storage/directories.rb +33 -0
- data/lib/fog/local/models/storage/directory.rb +53 -0
- data/lib/fog/local/models/storage/file.rb +132 -0
- data/lib/fog/local/models/storage/files.rb +84 -0
- data/lib/fog/local/storage.rb +96 -0
- data/lib/fog/local/version.rb +5 -0
- data/tests/helper.rb +28 -0
- data/tests/helpers/collection_helper.rb +97 -0
- data/tests/helpers/formats_helper.rb +98 -0
- data/tests/helpers/formats_helper_tests.rb +110 -0
- data/tests/helpers/mock_helper.rb +7 -0
- data/tests/helpers/model_helper.rb +31 -0
- data/tests/helpers/schema_validator_tests.rb +107 -0
- data/tests/helpers/succeeds_helper.rb +9 -0
- data/tests/local/models/directories_tests.rb +17 -0
- data/tests/local/models/directory_tests.rb +16 -0
- data/tests/local/models/file_tests.rb +43 -0
- data/tests/local/storage_tests.rb +40 -0
- data/tests/watchr.rb +22 -0
- metadata +136 -0
@@ -0,0 +1,84 @@
|
|
1
|
+
require 'fog/core/collection'
|
2
|
+
require 'fog/local/models/storage/file'
|
3
|
+
|
4
|
+
module Fog
|
5
|
+
module Storage
|
6
|
+
class Local
|
7
|
+
class Files < Fog::Collection
|
8
|
+
attribute :directory
|
9
|
+
|
10
|
+
model Fog::Storage::Local::File
|
11
|
+
|
12
|
+
def all
|
13
|
+
requires :directory
|
14
|
+
if directory.collection.get(directory.key)
|
15
|
+
data = []
|
16
|
+
Dir.chdir(service.path_to(directory.key)) {
|
17
|
+
data = Dir.glob('**/*').reject do |file|
|
18
|
+
::File.directory?(file)
|
19
|
+
end.map do |key|
|
20
|
+
path = file_path(key)
|
21
|
+
{
|
22
|
+
:content_length => ::File.size(path),
|
23
|
+
:key => key,
|
24
|
+
:last_modified => ::File.mtime(path)
|
25
|
+
}
|
26
|
+
end
|
27
|
+
}
|
28
|
+
load(data)
|
29
|
+
else
|
30
|
+
nil
|
31
|
+
end
|
32
|
+
end
|
33
|
+
|
34
|
+
def get(key, &block)
|
35
|
+
requires :directory
|
36
|
+
path = file_path(key)
|
37
|
+
if ::File.exist?(path)
|
38
|
+
data = {
|
39
|
+
:content_length => ::File.size(path),
|
40
|
+
:key => key,
|
41
|
+
:last_modified => ::File.mtime(path)
|
42
|
+
}
|
43
|
+
if block_given?
|
44
|
+
file = ::File.open(path)
|
45
|
+
while (chunk = file.read(Excon::CHUNK_SIZE)) && yield(chunk); end
|
46
|
+
file.close
|
47
|
+
new(data)
|
48
|
+
else
|
49
|
+
body = ::File.read(path)
|
50
|
+
new(data.merge!(:body => body))
|
51
|
+
end
|
52
|
+
else
|
53
|
+
nil
|
54
|
+
end
|
55
|
+
end
|
56
|
+
|
57
|
+
def head(key)
|
58
|
+
requires :directory
|
59
|
+
path = file_path(key)
|
60
|
+
if ::File.exist?(path)
|
61
|
+
new({
|
62
|
+
:content_length => ::File.size(path),
|
63
|
+
:key => key,
|
64
|
+
:last_modified => ::File.mtime(path)
|
65
|
+
})
|
66
|
+
else
|
67
|
+
nil
|
68
|
+
end
|
69
|
+
end
|
70
|
+
|
71
|
+
def new(attributes = {})
|
72
|
+
requires :directory
|
73
|
+
super({ :directory => directory }.merge!(attributes))
|
74
|
+
end
|
75
|
+
|
76
|
+
private
|
77
|
+
|
78
|
+
def file_path(key)
|
79
|
+
service.path_to(::File.join(directory.key, key))
|
80
|
+
end
|
81
|
+
end
|
82
|
+
end
|
83
|
+
end
|
84
|
+
end
|
@@ -0,0 +1,96 @@
|
|
1
|
+
require 'fog/local/core'
|
2
|
+
|
3
|
+
module Fog
|
4
|
+
module Storage
|
5
|
+
class Local < Fog::Service
|
6
|
+
requires :local_root
|
7
|
+
recognizes :endpoint, :scheme, :host, :port, :path
|
8
|
+
|
9
|
+
model_path 'fog/local/models/storage'
|
10
|
+
collection :directories
|
11
|
+
model :directory
|
12
|
+
model :file
|
13
|
+
collection :files
|
14
|
+
|
15
|
+
require 'uri'
|
16
|
+
|
17
|
+
class Mock
|
18
|
+
attr_reader :endpoint
|
19
|
+
|
20
|
+
def self.data
|
21
|
+
@data ||= Hash.new do |hash, key|
|
22
|
+
hash[key] = {}
|
23
|
+
end
|
24
|
+
end
|
25
|
+
|
26
|
+
def self.reset
|
27
|
+
@data = nil
|
28
|
+
end
|
29
|
+
|
30
|
+
def initialize(options={})
|
31
|
+
Fog::Mock.not_implemented
|
32
|
+
|
33
|
+
@local_root = ::File.expand_path(options[:local_root])
|
34
|
+
|
35
|
+
@endpoint = options[:endpoint] || build_endpoint_from_options(options)
|
36
|
+
end
|
37
|
+
|
38
|
+
def data
|
39
|
+
self.class.data[@local_root]
|
40
|
+
end
|
41
|
+
|
42
|
+
def local_root
|
43
|
+
@local_root
|
44
|
+
end
|
45
|
+
|
46
|
+
def path_to(partial)
|
47
|
+
::File.join(@local_root, partial)
|
48
|
+
end
|
49
|
+
|
50
|
+
def reset_data
|
51
|
+
self.class.data.delete(@local_root)
|
52
|
+
end
|
53
|
+
|
54
|
+
private
|
55
|
+
def build_endpoint_from_options(options)
|
56
|
+
return unless options[:host]
|
57
|
+
|
58
|
+
URI::Generic.build(options).to_s
|
59
|
+
end
|
60
|
+
end
|
61
|
+
|
62
|
+
class Real
|
63
|
+
attr_reader :endpoint
|
64
|
+
|
65
|
+
def initialize(options={})
|
66
|
+
@local_root = ::File.expand_path(options[:local_root])
|
67
|
+
|
68
|
+
@endpoint = options[:endpoint] || build_endpoint_from_options(options)
|
69
|
+
end
|
70
|
+
|
71
|
+
def local_root
|
72
|
+
@local_root
|
73
|
+
end
|
74
|
+
|
75
|
+
def path_to(partial)
|
76
|
+
::File.join(@local_root, partial)
|
77
|
+
end
|
78
|
+
|
79
|
+
def copy_object(source_directory_name, source_object_name, target_directory_name, target_object_name, options={})
|
80
|
+
require 'fileutils'
|
81
|
+
source_path = path_to(::File.join(source_directory_name, source_object_name))
|
82
|
+
target_path = path_to(::File.join(target_directory_name, target_object_name))
|
83
|
+
::FileUtils.mkdir_p(::File.dirname(target_path))
|
84
|
+
::FileUtils.copy_file(source_path, target_path)
|
85
|
+
end
|
86
|
+
|
87
|
+
private
|
88
|
+
def build_endpoint_from_options(options)
|
89
|
+
return unless options[:host]
|
90
|
+
|
91
|
+
URI::Generic.build(options).to_s
|
92
|
+
end
|
93
|
+
end
|
94
|
+
end
|
95
|
+
end
|
96
|
+
end
|
data/tests/helper.rb
ADDED
@@ -0,0 +1,28 @@
|
|
1
|
+
begin
|
2
|
+
require "codeclimate-test-reporter"
|
3
|
+
CodeClimate::TestReporter.start
|
4
|
+
rescue LoadError => e
|
5
|
+
$stderr.puts "not recording test coverage: #{e.inspect}"
|
6
|
+
end
|
7
|
+
|
8
|
+
require File.expand_path('../../lib/fog/local', __FILE__)
|
9
|
+
|
10
|
+
Bundler.require(:test)
|
11
|
+
|
12
|
+
Excon.defaults.merge!(:debug_request => true, :debug_response => true)
|
13
|
+
|
14
|
+
require File.expand_path(File.join(File.dirname(__FILE__), 'helpers', 'mock_helper'))
|
15
|
+
|
16
|
+
# This overrides the default 600 seconds timeout during live test runs
|
17
|
+
if Fog.mocking?
|
18
|
+
Fog.timeout = ENV['FOG_TEST_TIMEOUT'] || 2000
|
19
|
+
Fog::Logger.warning "Setting default fog timeout to #{Fog.timeout} seconds"
|
20
|
+
end
|
21
|
+
|
22
|
+
def lorem_file
|
23
|
+
File.open(File.dirname(__FILE__) + '/lorem.txt', 'r')
|
24
|
+
end
|
25
|
+
|
26
|
+
def array_differences(array_a, array_b)
|
27
|
+
(array_a - array_b) | (array_b - array_a)
|
28
|
+
end
|
@@ -0,0 +1,97 @@
|
|
1
|
+
def collection_tests(collection, params = {}, mocks_implemented = true)
|
2
|
+
tests('success') do
|
3
|
+
|
4
|
+
tests("#new(#{params.inspect})").succeeds do
|
5
|
+
pending if Fog.mocking? && !mocks_implemented
|
6
|
+
collection.new(params)
|
7
|
+
end
|
8
|
+
|
9
|
+
tests("#create(#{params.inspect})").succeeds do
|
10
|
+
pending if Fog.mocking? && !mocks_implemented
|
11
|
+
@instance = collection.create(params)
|
12
|
+
end
|
13
|
+
# FIXME: work around for timing issue on AWS describe_instances mocks
|
14
|
+
|
15
|
+
if Fog.mocking? && @instance.respond_to?(:ready?)
|
16
|
+
@instance.wait_for { ready? }
|
17
|
+
end
|
18
|
+
|
19
|
+
tests("#all").succeeds do
|
20
|
+
pending if Fog.mocking? && !mocks_implemented
|
21
|
+
collection.all
|
22
|
+
end
|
23
|
+
|
24
|
+
if !Fog.mocking? || mocks_implemented
|
25
|
+
@identity = @instance.identity
|
26
|
+
end
|
27
|
+
|
28
|
+
tests("#get(#{@identity})").succeeds do
|
29
|
+
pending if Fog.mocking? && !mocks_implemented
|
30
|
+
collection.get(@identity)
|
31
|
+
end
|
32
|
+
|
33
|
+
tests('Enumerable') do
|
34
|
+
pending if Fog.mocking? && !mocks_implemented
|
35
|
+
|
36
|
+
methods = [
|
37
|
+
'all?', 'any?', 'find', 'detect', 'collect', 'map',
|
38
|
+
'find_index', 'flat_map', 'collect_concat', 'group_by',
|
39
|
+
'none?', 'one?'
|
40
|
+
]
|
41
|
+
|
42
|
+
# JRuby 1.7.5+ issue causes a SystemStackError: stack level too deep
|
43
|
+
# https://github.com/jruby/jruby/issues/1265
|
44
|
+
if RUBY_PLATFORM == "java" and JRUBY_VERSION =~ /1\.7\.[5-8]/
|
45
|
+
methods.delete('all?')
|
46
|
+
end
|
47
|
+
|
48
|
+
methods.each do |enum_method|
|
49
|
+
if collection.respond_to?(enum_method)
|
50
|
+
tests("##{enum_method}").succeeds do
|
51
|
+
block_called = false
|
52
|
+
collection.send(enum_method) {|x| block_called = true }
|
53
|
+
block_called
|
54
|
+
end
|
55
|
+
end
|
56
|
+
end
|
57
|
+
|
58
|
+
[
|
59
|
+
'max_by','min_by'
|
60
|
+
].each do |enum_method|
|
61
|
+
if collection.respond_to?(enum_method)
|
62
|
+
tests("##{enum_method}").succeeds do
|
63
|
+
block_called = false
|
64
|
+
collection.send(enum_method) {|x| block_called = true; 0 }
|
65
|
+
block_called
|
66
|
+
end
|
67
|
+
end
|
68
|
+
|
69
|
+
end
|
70
|
+
|
71
|
+
end
|
72
|
+
|
73
|
+
if block_given?
|
74
|
+
yield(@instance)
|
75
|
+
end
|
76
|
+
|
77
|
+
if !Fog.mocking? || mocks_implemented
|
78
|
+
@instance.destroy
|
79
|
+
end
|
80
|
+
end
|
81
|
+
|
82
|
+
tests('failure') do
|
83
|
+
|
84
|
+
if !Fog.mocking? || mocks_implemented
|
85
|
+
@identity = @identity.to_s
|
86
|
+
@identity = @identity.gsub(/[a-zA-Z]/) { Fog::Mock.random_letters(1) }
|
87
|
+
@identity = @identity.gsub(/\d/) { Fog::Mock.random_numbers(1) }
|
88
|
+
@identity
|
89
|
+
end
|
90
|
+
|
91
|
+
tests("#get('#{@identity}')").returns(nil) do
|
92
|
+
pending if Fog.mocking? && !mocks_implemented
|
93
|
+
collection.get(@identity)
|
94
|
+
end
|
95
|
+
|
96
|
+
end
|
97
|
+
end
|
@@ -0,0 +1,98 @@
|
|
1
|
+
require "fog/schema/data_validator"
|
2
|
+
|
3
|
+
# format related hackery
|
4
|
+
# allows both true.is_a?(Fog::Boolean) and false.is_a?(Fog::Boolean)
|
5
|
+
# allows both nil.is_a?(Fog::Nullable::String) and ''.is_a?(Fog::Nullable::String)
|
6
|
+
module Fog
|
7
|
+
module Boolean; end
|
8
|
+
module Nullable
|
9
|
+
module Boolean; end
|
10
|
+
module Integer; end
|
11
|
+
module String; end
|
12
|
+
module Time; end
|
13
|
+
module Float; end
|
14
|
+
module Hash; end
|
15
|
+
module Array; end
|
16
|
+
end
|
17
|
+
end
|
18
|
+
[FalseClass, TrueClass].each {|klass| klass.send(:include, Fog::Boolean)}
|
19
|
+
[FalseClass, TrueClass, NilClass, Fog::Boolean].each {|klass| klass.send(:include, Fog::Nullable::Boolean)}
|
20
|
+
[NilClass, String].each {|klass| klass.send(:include, Fog::Nullable::String)}
|
21
|
+
[NilClass, Time].each {|klass| klass.send(:include, Fog::Nullable::Time)}
|
22
|
+
[Integer, NilClass].each {|klass| klass.send(:include, Fog::Nullable::Integer)}
|
23
|
+
[Float, NilClass].each {|klass| klass.send(:include, Fog::Nullable::Float)}
|
24
|
+
[Hash, NilClass].each {|klass| klass.send(:include, Fog::Nullable::Hash)}
|
25
|
+
[Array, NilClass].each {|klass| klass.send(:include, Fog::Nullable::Array)}
|
26
|
+
|
27
|
+
module Shindo
|
28
|
+
class Tests
|
29
|
+
# Generates a Shindo test that compares a hash schema to the result
|
30
|
+
# of the passed in block returning true if they match.
|
31
|
+
#
|
32
|
+
# The schema that is passed in is a Hash or Array of hashes that
|
33
|
+
# have Classes in place of values. When checking the schema the
|
34
|
+
# value should match the Class.
|
35
|
+
#
|
36
|
+
# Strict mode will fail if the data has additional keys. Setting
|
37
|
+
# +strict+ to +false+ will allow additional keys to appear.
|
38
|
+
#
|
39
|
+
# @param [Hash] schema A Hash schema
|
40
|
+
# @param [Hash] options Options to change validation rules
|
41
|
+
# @option options [Boolean] :allow_extra_keys
|
42
|
+
# If +true+ does not fail when keys are in the data that are
|
43
|
+
# not specified in the schema. This allows new values to
|
44
|
+
# appear in API output without breaking the check.
|
45
|
+
# @option options [Boolean] :allow_optional_rules
|
46
|
+
# If +true+ does not fail if extra keys are in the schema
|
47
|
+
# that do not match the data. Not recommended!
|
48
|
+
# @yield Data to check with schema
|
49
|
+
#
|
50
|
+
# @example Using in a test
|
51
|
+
# Shindo.tests("comparing welcome data against schema") do
|
52
|
+
# data = {:welcome => "Hello" }
|
53
|
+
# data_matches_schema(:welcome => String) { data }
|
54
|
+
# end
|
55
|
+
#
|
56
|
+
# comparing welcome data against schema
|
57
|
+
# + data matches schema
|
58
|
+
#
|
59
|
+
# @example Example schema
|
60
|
+
# {
|
61
|
+
# "id" => String,
|
62
|
+
# "ram" => Integer,
|
63
|
+
# "disks" => [
|
64
|
+
# {
|
65
|
+
# "size" => Float
|
66
|
+
# }
|
67
|
+
# ],
|
68
|
+
# "dns_name" => Fog::Nullable::String,
|
69
|
+
# "active" => Fog::Boolean,
|
70
|
+
# "created" => DateTime
|
71
|
+
# }
|
72
|
+
#
|
73
|
+
# @return [Boolean]
|
74
|
+
def data_matches_schema(schema, options = {})
|
75
|
+
test('data matches schema') do
|
76
|
+
validator = Fog::Schema::DataValidator.new
|
77
|
+
valid = validator.validate(yield, schema, options)
|
78
|
+
@message = validator.message unless valid
|
79
|
+
valid
|
80
|
+
end
|
81
|
+
end
|
82
|
+
|
83
|
+
# @deprecated #formats is deprecated. Use #data_matches_schema instead
|
84
|
+
def formats(format, strict = true)
|
85
|
+
test('has proper format') do
|
86
|
+
if strict
|
87
|
+
options = {:allow_extra_keys => false, :allow_optional_rules => true}
|
88
|
+
else
|
89
|
+
options = {:allow_extra_keys => true, :allow_optional_rules => true}
|
90
|
+
end
|
91
|
+
validator = Fog::Schema::DataValidator.new
|
92
|
+
valid = validator.validate(yield, format, options)
|
93
|
+
@message = validator.message unless valid
|
94
|
+
valid
|
95
|
+
end
|
96
|
+
end
|
97
|
+
end
|
98
|
+
end
|
@@ -0,0 +1,110 @@
|
|
1
|
+
Shindo.tests('test_helper', 'meta') do
|
2
|
+
|
3
|
+
tests('comparing welcome data against schema') do
|
4
|
+
data = {:welcome => "Hello" }
|
5
|
+
data_matches_schema(:welcome => String) { data }
|
6
|
+
end
|
7
|
+
|
8
|
+
tests('#data_matches_schema') do
|
9
|
+
tests('when value matches schema expectation') do
|
10
|
+
data_matches_schema({"key" => String}) { {"key" => "Value"} }
|
11
|
+
end
|
12
|
+
|
13
|
+
tests('when values within an array all match schema expectation') do
|
14
|
+
data_matches_schema({"key" => [Integer]}) { {"key" => [1, 2]} }
|
15
|
+
end
|
16
|
+
|
17
|
+
tests('when nested values match schema expectation') do
|
18
|
+
data_matches_schema({"key" => {:nested_key => String}}) { {"key" => {:nested_key => "Value"}} }
|
19
|
+
end
|
20
|
+
|
21
|
+
tests('when collection of values all match schema expectation') do
|
22
|
+
data_matches_schema([{"key" => String}]) { [{"key" => "Value"}, {"key" => "Value"}] }
|
23
|
+
end
|
24
|
+
|
25
|
+
tests('when collection is empty although schema covers optional members') do
|
26
|
+
data_matches_schema([{"key" => String}], {:allow_optional_rules => true}) { [] }
|
27
|
+
end
|
28
|
+
|
29
|
+
tests('when additional keys are passed and not strict') do
|
30
|
+
data_matches_schema({"key" => String}, {:allow_extra_keys => true}) { {"key" => "Value", :extra => "Bonus"} }
|
31
|
+
end
|
32
|
+
|
33
|
+
tests('when value is nil and schema expects NilClass') do
|
34
|
+
data_matches_schema({"key" => NilClass}) { {"key" => nil} }
|
35
|
+
end
|
36
|
+
|
37
|
+
tests('when value and schema match as hashes') do
|
38
|
+
data_matches_schema({}) { {} }
|
39
|
+
end
|
40
|
+
|
41
|
+
tests('when value and schema match as arrays') do
|
42
|
+
data_matches_schema([]) { [] }
|
43
|
+
end
|
44
|
+
|
45
|
+
tests('when value is a Time') do
|
46
|
+
data_matches_schema({"time" => Time}) { {"time" => Time.now} }
|
47
|
+
end
|
48
|
+
|
49
|
+
tests('when key is missing but value should be NilClass (#1477)') do
|
50
|
+
data_matches_schema({"key" => NilClass}, {:allow_optional_rules => true}) { {} }
|
51
|
+
end
|
52
|
+
|
53
|
+
tests('when key is missing but value is nullable (#1477)') do
|
54
|
+
data_matches_schema({"key" => Fog::Nullable::String}, {:allow_optional_rules => true}) { {} }
|
55
|
+
end
|
56
|
+
end
|
57
|
+
|
58
|
+
tests('#formats backwards compatible changes') do
|
59
|
+
|
60
|
+
tests('when value matches schema expectation') do
|
61
|
+
formats({"key" => String}) { {"key" => "Value"} }
|
62
|
+
end
|
63
|
+
|
64
|
+
tests('when values within an array all match schema expectation') do
|
65
|
+
formats({"key" => [Integer]}) { {"key" => [1, 2]} }
|
66
|
+
end
|
67
|
+
|
68
|
+
tests('when nested values match schema expectation') do
|
69
|
+
formats({"key" => {:nested_key => String}}) { {"key" => {:nested_key => "Value"}} }
|
70
|
+
end
|
71
|
+
|
72
|
+
tests('when collection of values all match schema expectation') do
|
73
|
+
formats([{"key" => String}]) { [{"key" => "Value"}, {"key" => "Value"}] }
|
74
|
+
end
|
75
|
+
|
76
|
+
tests('when collection is empty although schema covers optional members') do
|
77
|
+
formats([{"key" => String}]) { [] }
|
78
|
+
end
|
79
|
+
|
80
|
+
tests('when additional keys are passed and not strict') do
|
81
|
+
formats({"key" => String}, false) { {"key" => "Value", :extra => "Bonus"} }
|
82
|
+
end
|
83
|
+
|
84
|
+
tests('when value is nil and schema expects NilClass') do
|
85
|
+
formats({"key" => NilClass}) { {"key" => nil} }
|
86
|
+
end
|
87
|
+
|
88
|
+
tests('when value and schema match as hashes') do
|
89
|
+
formats({}) { {} }
|
90
|
+
end
|
91
|
+
|
92
|
+
tests('when value and schema match as arrays') do
|
93
|
+
formats([]) { [] }
|
94
|
+
end
|
95
|
+
|
96
|
+
tests('when value is a Time') do
|
97
|
+
formats({"time" => Time}) { {"time" => Time.now} }
|
98
|
+
end
|
99
|
+
|
100
|
+
tests('when key is missing but value should be NilClass (#1477)') do
|
101
|
+
formats({"key" => NilClass}) { {} }
|
102
|
+
end
|
103
|
+
|
104
|
+
tests('when key is missing but value is nullable (#1477)') do
|
105
|
+
formats({"key" => Fog::Nullable::String}) { {} }
|
106
|
+
end
|
107
|
+
|
108
|
+
end
|
109
|
+
|
110
|
+
end
|