fog-local 0.1.0

Sign up to get free protection for your applications and to get access to all the features.
@@ -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