miasma-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,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: