miasma-local 0.1.0

Sign up to get free protection for your applications and to get access to all the features.
@@ -0,0 +1,7 @@
1
+ ---
2
+ SHA1:
3
+ metadata.gz: ceebf096e1b2813c60f3601a5b54a2ed0e230644
4
+ data.tar.gz: 1962f4cdef1ec1017613928760beb706333e24d4
5
+ SHA512:
6
+ metadata.gz: 8d0ed1893acc392ab162b25a9e6976e68f50c7b5d9cf767c8bc18385b5b3ba208e5bf7ce97e3cd652dd660e770c5e614d74fbdb62992c68c2f0365023ddba5de
7
+ data.tar.gz: abc9904bc3562e61db8400aaeb6336066ff69253887ea45032c1b8c240d7638646ee592e14df8dad61ba7822d562f044ba6d7fdd6a86516cfb52a2d32f9fbded
@@ -0,0 +1,2 @@
1
+ ## v0.1.0
2
+ * Initial release
data/LICENSE ADDED
@@ -0,0 +1,13 @@
1
+ Copyright 2014 Chris Roberts
2
+
3
+ Licensed under the Apache License, Version 2.0 (the "License");
4
+ you may not use this file except in compliance with the License.
5
+ You may obtain a copy of the License at
6
+
7
+ http://www.apache.org/licenses/LICENSE-2.0
8
+
9
+ Unless required by applicable law or agreed to in writing, software
10
+ distributed under the License is distributed on an "AS IS" BASIS,
11
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12
+ See the License for the specific language governing permissions and
13
+ limitations under the License.
@@ -0,0 +1,6 @@
1
+ # Miasma Local
2
+
3
+ Local API plugin for the miasma cloud library
4
+
5
+ ## Info
6
+ * Repository: https://github.com/miasma-rb/miasma-local
@@ -0,0 +1,2 @@
1
+ require 'miasma'
2
+ require 'miasma-local/version'
@@ -0,0 +1,4 @@
1
+ module MiasmaLocal
2
+ # Current library version
3
+ VERSION = Gem::Version.new('0.1.0')
4
+ end
@@ -0,0 +1,27 @@
1
+ require 'miasma'
2
+
3
+ module Miasma
4
+ module Contrib
5
+
6
+ # Local API core helper
7
+ class LocalApiCore
8
+
9
+ # Common API methods
10
+ module ApiCommon
11
+
12
+ # Set attributes into model
13
+ #
14
+ # @param klass [Class]
15
+ def self.included(klass)
16
+ klass.class_eval do
17
+ attribute :object_store_root, String
18
+ end
19
+ end
20
+
21
+ end
22
+
23
+ end
24
+ end
25
+
26
+ Models::Storage.autoload :Local, 'miasma/contrib/local/storage'
27
+ end
@@ -0,0 +1,252 @@
1
+ require 'miasma'
2
+ require 'fileutils'
3
+ require 'tempfile'
4
+
5
+ module Miasma
6
+ module Models
7
+ class Storage
8
+ class Local < Storage
9
+
10
+ include Contrib::LocalApiCore::ApiCommon
11
+
12
+ # Create new instance
13
+ #
14
+ # @param args [Hash]
15
+ # @return [self]
16
+ def initialize(args={})
17
+ super
18
+ unless(::File.directory?(object_store_root))
19
+ FileUtils.mkdir_p(object_store_root)
20
+ end
21
+ end
22
+
23
+ # Save bucket
24
+ #
25
+ # @param bucket [Models::Storage::Bucket]
26
+ # @return [Models::Storage::Bucket]
27
+ def bucket_save(bucket)
28
+ unless(bucket.persisted?)
29
+ FileUtils.mkdir_p(full_path(bucket))
30
+ bucket.id = bucket.name
31
+ bucket.valid_state
32
+ end
33
+ bucket
34
+ end
35
+
36
+ # Destroy bucket
37
+ #
38
+ # @param bucket [Models::Storage::Bucket]
39
+ # @return [TrueClass, FalseClass]
40
+ def bucket_destroy(bucket)
41
+ if(bucket.persisted?)
42
+ FileUtils.rmdir(full_path(bucket))
43
+ true
44
+ else
45
+ false
46
+ end
47
+ end
48
+
49
+ # Reload the bucket
50
+ #
51
+ # @param bucket [Models::Storage::Bucket]
52
+ # @return [Models::Storage::Bucket]
53
+ def bucket_reload(bucket)
54
+ if(bucket.persisted?)
55
+ unless(::File.directory?(full_path(bucket)))
56
+ bucket.data.clear
57
+ bucket.dirty.clear
58
+ else
59
+ bucket.valid_state
60
+ end
61
+ end
62
+ bucket
63
+ end
64
+
65
+ # Return all buckets
66
+ #
67
+ # @return [Array<Models::Storage::Bucket>]
68
+ def bucket_all
69
+ Dir.new(object_store_root).map do |item|
70
+ if(::File.directory?(item) && !item.start_with?('.'))
71
+ Bucket.new(
72
+ self,
73
+ :id => ::File.basename(uri_unescape(item)),
74
+ :name => ::File.basename(uri_unescape(item))
75
+ ).valid_state
76
+ end
77
+ end.compact
78
+ end
79
+
80
+ # Return filtered files
81
+ #
82
+ # @param args [Hash] filter options
83
+ # @return [Array<Models::Storage::File>]
84
+ def file_filter(bucket, args)
85
+ Dir.glob(::File.join(full_path(bucket), uri_escape(args[:prefix]), '*')).map do |item|
86
+ if(::File.file?(item) && !item.start_with?('.'))
87
+ item_name = item.sub("#{full_path(bucket)}/", '')
88
+ item_name = uri_unescape(item_name)
89
+ File.new(
90
+ bucket,
91
+ :id => ::File.join(bucket.name, item_name),
92
+ :name => item_name,
93
+ :updated => File.mtime(item),
94
+ :size => File.size(item)
95
+ ).valid_state
96
+ end
97
+ end.compact
98
+ end
99
+
100
+ # Return all files within bucket
101
+ #
102
+ # @param bucket [Bucket]
103
+ # @return [Array<File>]
104
+ # @todo pagination auto-follow
105
+ def file_all(bucket)
106
+ Dir.glob(::File.join(full_path(bucket), '*')).map do |item|
107
+ if(::File.file?(item) && !item.start_with?('.'))
108
+ item_name = item.sub("#{full_path(bucket)}/", '')
109
+ item_name = uri_unescape(item_name)
110
+ File.new(
111
+ bucket,
112
+ :id => ::File.join(bucket.name, item_name),
113
+ :name => item_name,
114
+ :updated => File.mtime(item),
115
+ :size => File.size(item)
116
+ ).valid_state
117
+ end
118
+ end.compact
119
+ end
120
+
121
+ # Save file
122
+ #
123
+ # @param file [Models::Storage::File]
124
+ # @return [Models::Storage::File]
125
+ def file_save(file)
126
+ if(file.dirty?)
127
+ file.load_data(file.attributes)
128
+ if(file.attributes[:body].is_a?(IO))
129
+ file.body.rewind
130
+ tmp_file = Tempfile.new('miasma')
131
+ while(content = file.body.read(Storage::READ_BODY_CHUNK_SIZE))
132
+ tmp_file.write content
133
+ end
134
+ tmp_file.close
135
+ FileUtils.mv(tmp_file.path, full_path(file))
136
+ end
137
+ file.id = ::File.join(file.bucket.name, file.name)
138
+ file.reload
139
+ end
140
+ file
141
+ end
142
+
143
+ # Destroy file
144
+ #
145
+ # @param file [Models::Storage::File]
146
+ # @return [TrueClass, FalseClass]
147
+ def file_destroy(file)
148
+ if(file.persisted?)
149
+ FileUtils.rm(full_path(file))
150
+ true
151
+ else
152
+ false
153
+ end
154
+ end
155
+
156
+ # Reload the file
157
+ #
158
+ # @param file [Models::Storage::File]
159
+ # @return [Models::Storage::File]
160
+ def file_reload(file)
161
+ if(file.persisted?)
162
+ new_info = Smash.new.tap do |data|
163
+ data[:updated] = File.mtime(full_path(file))
164
+ data[:size] = File.size(file_path(file))
165
+ data[:etag] = Digest::MD5.hexdigest(content)
166
+ data[:content_type] = MIME::Types.of(full_path(file))
167
+ end
168
+ file.load_data(file.attributes.deep_merge(new_info))
169
+ file.valid_state
170
+ end
171
+ file
172
+ end
173
+
174
+ # Create publicly accessible URL
175
+ #
176
+ # @param timeout_secs [Integer] seconds available
177
+ # @return [String] URL
178
+ # @todo where is this in swift?
179
+ def file_url(file, timeout_secs)
180
+ if(file.persisted?)
181
+ raise NotImplementedError
182
+ else
183
+ raise Error::ModelPersistError.new "#{file} has not been saved!"
184
+ end
185
+ end
186
+
187
+ # Fetch the contents of the file
188
+ #
189
+ # @param file [Models::Storage::File]
190
+ # @return [IO, HTTP::Response::Body]
191
+ def file_body(file)
192
+ if(file.persisted?)
193
+ tmp_file = Tempfile.new('miasma')
194
+ tmp_file.delete
195
+ FileUtils.cp(full_path(file), tmp_file.path)
196
+ tmp_file.open
197
+ tmp_file
198
+ else
199
+ StringIO.new('')
200
+ end
201
+ end
202
+
203
+ # @return [String] escaped bucket name
204
+ def bucket_path(bucket)
205
+ ::File.join(object_store_root, uri_escape(bucket.name))
206
+ end
207
+
208
+ # @return [String] escaped file path
209
+ def file_path(file)
210
+ file.name.split('/').map do |part|
211
+ uri_escape(part)
212
+ end.join('/')
213
+ end
214
+
215
+ # Provide full path for object
216
+ #
217
+ # @param file_or_bucket [File, Bucket]
218
+ # @return [String]
219
+ def full_path(file_or_bucket)
220
+ path = ''
221
+ if(file_or_bucket.respond_to?(:bucket))
222
+ path << '/' << bucket_path(file_or_bucket.bucket)
223
+ end
224
+ path << '/' << file_path(file_or_bucket)
225
+ path
226
+ end
227
+
228
+ # URL string escape
229
+ #
230
+ # @param string [String] string to escape
231
+ # @return [String] escaped string
232
+ # @todo move this to common module
233
+ def uri_escape(string)
234
+ string.to_s.gsub(/([^a-zA-Z0-9_.\-~])/) do
235
+ '%' << $1.unpack('H2' * $1.bytesize).join('%').upcase
236
+ end
237
+ end
238
+
239
+ # Un-escape URL escaped string
240
+ #
241
+ # @param string [String]
242
+ # @return [String]
243
+ def uri_unescape(string)
244
+ string.to_s.gsub(/%([^%]{2})/) do
245
+ [$1].pack('H2')
246
+ end
247
+ end
248
+
249
+ end
250
+ end
251
+ end
252
+ end
@@ -0,0 +1,16 @@
1
+ $LOAD_PATH.unshift File.expand_path(File.dirname(__FILE__)) + '/lib/'
2
+ require 'miasma-local/version'
3
+ Gem::Specification.new do |s|
4
+ s.name = 'miasma-local'
5
+ s.version = MiasmaLocal::VERSION.version
6
+ s.summary = 'Smoggy local API'
7
+ s.author = 'Chris Roberts'
8
+ s.email = 'code@chrisroberts.org'
9
+ s.homepage = 'https://github.com/miasma-rb/miasma-local'
10
+ s.description = 'Smoggy local API'
11
+ s.license = 'Apache 2.0'
12
+ s.require_path = 'lib'
13
+ s.add_dependency 'miasma'
14
+ s.add_dependency 'mime-types'
15
+ s.files = Dir['lib/**/*'] + %w(miasma-local.gemspec README.md CHANGELOG.md LICENSE)
16
+ end
metadata ADDED
@@ -0,0 +1,80 @@
1
+ --- !ruby/object:Gem::Specification
2
+ name: miasma-local
3
+ version: !ruby/object:Gem::Version
4
+ version: 0.1.0
5
+ platform: ruby
6
+ authors:
7
+ - Chris Roberts
8
+ autorequire:
9
+ bindir: bin
10
+ cert_chain: []
11
+ date: 2015-01-05 00:00:00.000000000 Z
12
+ dependencies:
13
+ - !ruby/object:Gem::Dependency
14
+ name: miasma
15
+ requirement: !ruby/object:Gem::Requirement
16
+ requirements:
17
+ - - ">="
18
+ - !ruby/object:Gem::Version
19
+ version: '0'
20
+ type: :runtime
21
+ prerelease: false
22
+ version_requirements: !ruby/object:Gem::Requirement
23
+ requirements:
24
+ - - ">="
25
+ - !ruby/object:Gem::Version
26
+ version: '0'
27
+ - !ruby/object:Gem::Dependency
28
+ name: mime-types
29
+ requirement: !ruby/object:Gem::Requirement
30
+ requirements:
31
+ - - ">="
32
+ - !ruby/object:Gem::Version
33
+ version: '0'
34
+ type: :runtime
35
+ prerelease: false
36
+ version_requirements: !ruby/object:Gem::Requirement
37
+ requirements:
38
+ - - ">="
39
+ - !ruby/object:Gem::Version
40
+ version: '0'
41
+ description: Smoggy local API
42
+ email: code@chrisroberts.org
43
+ executables: []
44
+ extensions: []
45
+ extra_rdoc_files: []
46
+ files:
47
+ - CHANGELOG.md
48
+ - LICENSE
49
+ - README.md
50
+ - lib/miasma-local.rb
51
+ - lib/miasma-local/version.rb
52
+ - lib/miasma/contrib/local.rb
53
+ - lib/miasma/contrib/local/storage.rb
54
+ - miasma-local.gemspec
55
+ homepage: https://github.com/miasma-rb/miasma-local
56
+ licenses:
57
+ - Apache 2.0
58
+ metadata: {}
59
+ post_install_message:
60
+ rdoc_options: []
61
+ require_paths:
62
+ - lib
63
+ required_ruby_version: !ruby/object:Gem::Requirement
64
+ requirements:
65
+ - - ">="
66
+ - !ruby/object:Gem::Version
67
+ version: '0'
68
+ required_rubygems_version: !ruby/object:Gem::Requirement
69
+ requirements:
70
+ - - ">="
71
+ - !ruby/object:Gem::Version
72
+ version: '0'
73
+ requirements: []
74
+ rubyforge_project:
75
+ rubygems_version: 2.2.2
76
+ signing_key:
77
+ specification_version: 4
78
+ summary: Smoggy local API
79
+ test_files: []
80
+ has_rdoc: