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.
@@ -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
@@ -0,0 +1,5 @@
1
+ module Fog
2
+ module Local
3
+ VERSION = '0.1.0'
4
+ end
5
+ 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