sixarm_ruby_blob 1.0.0 → 1.0.1

Sign up to get free protection for your applications and to get access to all the features.
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA1:
3
- metadata.gz: a2fbb759d31bb2f54e52e52edd9db4a43f833ebc
4
- data.tar.gz: 3e6d068636b9f1dc652ef6284e7295e31600809e
3
+ metadata.gz: b2498c0f1bc1c1f18d4608c27b90469e5b68218c
4
+ data.tar.gz: 2db4616404f8bd1df0fa333a7dbfd2342549fd23
5
5
  SHA512:
6
- metadata.gz: cf0a87ce08854d34b0e9ff3fa6e43c9d4f9d54d8ec5f0204f56b525e8dc8c680b3b7f3e310b2db84dc24724e2804f064831d2bff2c3e4047de8ffb7a787e52d2
7
- data.tar.gz: e086df08b5fbbe61526a802d04447fb6cfaf3a19d2c32c1133b7bb5d8361e09fc7a01ac2b5eb7e5160d4a7269d9b1f700e54b3311414f16fec155cd2e3d57937
6
+ metadata.gz: 33161d97849da4819f3b996362ec2de113c3f7a60d6a1521d43470aa821c5435a5f69b6abbe261da07e91257b123097ff753d0aa86a16b888c7e426a4f3f5ad3
7
+ data.tar.gz: 9dc6a2e226a9eb1a3d0f738cf8d95d0fb882ffb31f9a360be4d55aeadb4422a95973b30e7fc72cc14e5a7076e49560805fd90ee167b8ab52284e2d246782b8aa
checksums.yaml.gz.sig CHANGED
@@ -1,2 +1 @@
1
-
2
- b@���^��&Y{=��56�<������)�Pl�T�D�8?\�FW�I�ۑd�^ޓ1� ��H$4E���� �.�$Ws+B�"�:"��ި�[�E%�[VG%�yPF~�Ɵ����=Կ��䬎9�#S
1
+ -� ��*X/T�ބ��;�Ӟ#U'3���
data/VERSION CHANGED
@@ -1 +1 @@
1
- 1.0.0
1
+ 1.0.1
@@ -0,0 +1,31 @@
1
+ # -*- coding: utf-8 -*-
2
+
3
+ ###
4
+ #
5
+ # Blob abstract model suitable for our
6
+ # typical kinds of data files in our app.
7
+ #
8
+ # We can store the blob on the local file system (see BlobFile),
9
+ # We can make the blob accessible via URI (see BlobURI).
10
+ # We can export the blob to an external system (see BlobExport).
11
+ #
12
+ ###
13
+
14
+ class Blob
15
+
16
+ def name; @name; end
17
+ def name=x; @name=x; end
18
+
19
+ def initialize(options = {})
20
+ @name ||= options[:name]
21
+ end
22
+
23
+ def ==(other)
24
+ self.name == other.name
25
+ end
26
+
27
+ def eql?(other)
28
+ self.name.eql?(other.name)
29
+ end
30
+
31
+ end
@@ -0,0 +1,28 @@
1
+ # Blob methods for accessing a blob via a dir name and base name.
2
+ # Exclusively for the Blob class.
3
+
4
+ class Blob
5
+
6
+ #attr_accessor :dir # Dir name of this blob, e.g. "/my/photos"
7
+ #attr_accessor :base # Base name of this blob, e.g. "photo.jpg"
8
+
9
+ def dir; @dir; end
10
+ def dir=(x); @dir=x; end
11
+
12
+ def base; @base; end
13
+ def base=(x); @base=x; end
14
+
15
+ def initialize(options = {})
16
+ @dir ||= options[:dir]
17
+ @base ||= options[:base]
18
+ end
19
+
20
+ def name
21
+ @name ||= "#{dir}/#{base}"
22
+ end
23
+
24
+ def ext
25
+ base =~ /\.(\w+)$/ ? $1 : nil
26
+ end
27
+
28
+ end
@@ -0,0 +1,50 @@
1
+ # Blob methods for exporting.
2
+ # Exclusively for the Blob class.
3
+
4
+ class Blob
5
+
6
+ # Get the blob export's directory name, e.g. "/my/photos"
7
+ #
8
+ # The implementation simply calls #file_dir.
9
+ # Override this when an export needs a custom directory base.
10
+ #
11
+ # @return [String] the blob export's directory name
12
+ #
13
+ def export_dir
14
+ file_dir
15
+ end
16
+
17
+ # Get the blob export's base name, e.g. "photo.jpg"
18
+ #
19
+ # The implementation simply calls #file_base.
20
+ # Override this when an export needs a custom base name.
21
+ #
22
+ # @return [String] the blob export's base name
23
+ #
24
+ def export_base
25
+ file_base
26
+ end
27
+
28
+ # Get the blob export's path, e.g. "/my/photos/photo.jpg"
29
+ #
30
+ # The implementation simply calls #file_path.
31
+ # Override this when an export needs a custom path.
32
+ #
33
+ # @return [String] the blob export's path
34
+ #
35
+ def export_path
36
+ export_pathname.to_s
37
+ end
38
+
39
+ # Get the blob export's pathname, e.g. Pathname("/my/photos/photo.jpg")
40
+ #
41
+ # The implementation simply calls #file_pathname.
42
+ # Override this when an export needs a custom path.
43
+ #
44
+ # @return [String] the file's path suitable for export
45
+ #
46
+ def export_pathname
47
+ Pathname(export_dir) + export_base
48
+ end
49
+
50
+ end
@@ -0,0 +1,73 @@
1
+ # Blob file methods for accessing a blob via the filesystem.
2
+ # Exclusively for the Blob class.
3
+
4
+ class Blob
5
+
6
+ # Get the blob file's dir name e.g. "/my/photos".
7
+ #
8
+ # This impl calls #dir which is typically fine.
9
+ #
10
+ # Override this if the local storage file dir
11
+ # name is different than the generic base name.
12
+ #
13
+ # @return [String] the blob file's dir name
14
+ #
15
+ def file_dir
16
+ dir
17
+ end
18
+
19
+ # Get the blob file's base name e.g. "photo.jpg".
20
+ #
21
+ # This impl calls #base which is typically fine.
22
+ #
23
+ # Override this when the local storage file base
24
+ # name is different than the generic base name.
25
+ #
26
+ # @return [String] the blob file's base name
27
+ #
28
+ def file_base
29
+ base
30
+ end
31
+
32
+ # Get the blob file's path e.g. "/my/photos/photo.jpg"
33
+ #
34
+ # This impl calls #file_dir and #file_base.
35
+ # Subclasses can likely use this as-is.
36
+ #
37
+ # @return [Pathname] the blob file's path
38
+ #
39
+ def file_path
40
+ file_pathname.to_s
41
+ end
42
+
43
+ # Get the blob file's pathname e.g. Pathname("/my/photos/photo.jpg")
44
+ #
45
+ # This impl calls #file_dir and #file_base.
46
+ # Subclasses can likely use this as-is.
47
+ #
48
+ # @return [Pathname] the blob file's pathname
49
+ #
50
+ def file_pathname
51
+ Pathname(file_dir) + file_base
52
+ end
53
+
54
+ # Does the file exist on the local filesystem?
55
+ #
56
+ # @return [boolean] iff the file exists
57
+ #
58
+ def file_exist?
59
+ FileTest.exists? file_path
60
+ end
61
+
62
+ # Return true iff the image exists the way we expect.
63
+ # Currently, this is simply calling file_exist.
64
+ #
65
+ # @deprecated
66
+ # @return [boolean] iff the file exists
67
+ #
68
+ def exist?
69
+ Rails.logger.warn "DEPRECATED"
70
+ FileTest.exists? file_path
71
+ end
72
+
73
+ end
@@ -0,0 +1,51 @@
1
+ # Blob method to upload from a web page.
2
+ # Exclusively for the Blob class.
3
+
4
+ class Blob
5
+
6
+ # Upload to this blob's file_path from a web form file_field.
7
+ #
8
+ # TODO optimize this to move the temp file into place
9
+ #
10
+ # @return [Boolean] true iff the upload succeeds
11
+ #
12
+ def upload(file_field)
13
+ self.class.upload(file_path, file_field)
14
+ end
15
+
16
+ # Upload to a file_path from a web form file_field.
17
+ #
18
+ # TODO optimize this to move the temp file into place
19
+ #
20
+ # @return [Boolean] true iff the upload succeeds
21
+ #
22
+ def self.upload(file_path, file_field)
23
+ if vet_file_field?(file_field)
24
+ file_field.tempfile.binmode
25
+ File.open(file_path, "wb") { |f| f.write(file_field.read) }
26
+ return true
27
+ end
28
+ return false
29
+ end
30
+
31
+ # Vet the file field for all the methods that we expect
32
+ # from a web browser upload; we call this before we upload.
33
+ #
34
+ def self.vet_file_field?(file_field)
35
+ !!(
36
+ file_field \
37
+ && file_field.respond_to?(:tempfile) \
38
+ && file_field.tempfile \
39
+ && file_field.tempfile.respond_to?(:path) \
40
+ && file_field.tempfile.respond_to?(:binmode) \
41
+ && file_field.tempfile.path
42
+ )
43
+ end
44
+
45
+ # Deprecated
46
+ #
47
+ def save(file_field)
48
+ raise "Deprecated: replace with #upload"
49
+ end
50
+
51
+ end
@@ -0,0 +1,64 @@
1
+ # Blob URI methods for accessing a blob via the web.
2
+ # Exclusively for the Blob class.
3
+
4
+ class Blob
5
+
6
+ # Get the blob URI's dir name e.g. "/my/photos".
7
+ #
8
+ # This impl calls #dir which is typically fine.
9
+ #
10
+ # Override this if the local storage file dir
11
+ # name is different than the generic base name.
12
+ #
13
+ # @return [String] the blob URI's dir name
14
+ #
15
+ def uri_dir
16
+ dir
17
+ end
18
+
19
+ # Get the blob's URI base name e.g. "photo.jpg".
20
+ #
21
+ # This impl calls #base which is typically fine.
22
+ #
23
+ # Override this when the URI base name
24
+ # is different than the generic base name.
25
+ #
26
+ # @return [String] the blob URI's base name
27
+ #
28
+ def uri_base
29
+ base
30
+ end
31
+
32
+ # Get the blob's URI to access this blob from the web.
33
+ #
34
+ # This impl calls #uri_dir and #uri_base.
35
+ # Override this e.g. for assets, CDNs, etc.
36
+ #
37
+ # @return [String] the blob URI's path
38
+ #
39
+ def uri
40
+ return "/#{uri_dir}/#{uri_base}"
41
+ end
42
+
43
+ # Get the blob's URI to access this blob from the web,
44
+ # with a random chaff query appended as a cache buster.
45
+ #
46
+ # @return [String] the blob URI's path?uuid=chaff
47
+ #
48
+ def uri_cacheless
49
+ return "#{uri}?cacheless=#{SecureRandom.uuid}"
50
+ end
51
+
52
+ # Deprecated
53
+ #
54
+ def url
55
+ raise "Deprecated: replace with #uri"
56
+ end
57
+
58
+ # Deprecated
59
+ #
60
+ def url_cacheless
61
+ raise "Deprecated: replace with #uri_cacheless"
62
+ end
63
+
64
+ end
@@ -0,0 +1,56 @@
1
+ # -*- coding: utf-8 -*-
2
+ require 'minitest/autorun'
3
+ require 'simplecov'
4
+ SimpleCov.start
5
+
6
+ describe Blob do
7
+
8
+ let(:blob){ Factory.build(:blob, name: FAB.name) }
9
+
10
+ describe "equality" do
11
+
12
+ before do
13
+ @a = Blob.new
14
+ @b = Blob.new
15
+ end
16
+
17
+ describe "#==(other)" do
18
+
19
+ it "is true iff names are equal (using ==)" do
20
+ @a.name = "foo"
21
+ @b.name = "foo"
22
+ (@a==@b).must_be_true
23
+ end
24
+
25
+ it "is false when names are inequal (using ==)" do
26
+ @a.name = "foo"
27
+ @b.name = "bar"
28
+ (@a==@b).must_be_false
29
+ end
30
+
31
+ end
32
+
33
+ describe "#eql?(other)" do
34
+
35
+ before do
36
+ @a = Blob.new
37
+ @b = Blob.new
38
+ end
39
+
40
+ it "is true iff names are equal (using #eql?)" do
41
+ @a.name = "foo"
42
+ @b.name = "foo"
43
+ (@a.eql?(@b)).must_be_true
44
+ end
45
+
46
+ it "is false when names are inequal (using #eql?)" do
47
+ @a.name = "foo"
48
+ @b.name = "bar"
49
+ (@a.eql?(@b)).must_be_false
50
+ end
51
+
52
+ end
53
+
54
+ end
55
+
56
+ end
@@ -0,0 +1,52 @@
1
+ # -*- coding: utf-8 -*-
2
+ require 'minitest/autorun'
3
+ require 'simplecov'
4
+ SimpleCov.start
5
+
6
+ describe Blob do
7
+
8
+ let(:dir){ FAB.dirname }
9
+ let(:base){ FAB.basename }
10
+ let(:blob){ Blob.new(dir: dir, base: base) }
11
+
12
+ # We choose to use Unix-friendly file names: lowercase a-z and underscores,
13
+ # always with "/" as a directory separator, typically with a dot extension,
14
+ # never with non-ASCII characters.
15
+ #
16
+ let(:base_match){ /^[0-1a-z\_\.]+$/ }
17
+ let(:dir_match){ /^[0-1a-z\_\/]+$/ }
18
+ let(:path_match){ /^[0-1a-z\_\/\.]+$/ }
19
+
20
+ describe "#base" do
21
+ it "uses a good unix name" do
22
+ blob.base.must_match base_match
23
+ end
24
+ end
25
+
26
+ describe "#dir" do
27
+ it "uses a good unix name" do
28
+ blob.dir.must_match dir_match
29
+ end
30
+ end
31
+
32
+ describe "#name" do
33
+ it "concats the name" do
34
+ blob.name.must_equal "#{dir}/#{base}"
35
+ end
36
+ end
37
+
38
+ describe "#ext" do
39
+
40
+ it "returns the file base name extension, if it exists" do
41
+ blob.base = "foo.goo.hoo"
42
+ blob.ext.must_equal "hoo"
43
+ end
44
+
45
+ it "returns nil, if there is no extension" do
46
+ blob.base = "foogoohoo"
47
+ blob.ext.must_be_nil
48
+ end
49
+
50
+ end
51
+
52
+ end
@@ -0,0 +1,38 @@
1
+ # -*- coding: utf-8 -*-
2
+ require 'minitest/autorun'
3
+ require 'simplecov'
4
+ SimpleCov.start
5
+
6
+ describe Blob do
7
+
8
+ let(:dir){ "abc" }
9
+ let(:base){ "def" }
10
+ let(:blob){ Blob.new(dir: dir, base: base) }
11
+
12
+ describe "#export_dir" do
13
+
14
+ it "defaults to the file dir" do
15
+ blob.export_dir.must_equal blob.file_dir
16
+ blob.export_dir.must_equal "#{dir}"
17
+ end
18
+
19
+ end
20
+
21
+ describe "#export_base" do
22
+
23
+ it "it defaults to the file base" do
24
+ blob.export_base.must_equal blob.file_base
25
+ blob.export_base.must_match "#{base}"
26
+ end
27
+
28
+ end
29
+
30
+ describe "#export_path" do
31
+
32
+ it "combines the dir and base" do
33
+ blob.export_path.must_equal "#{dir}/#{base}"
34
+ end
35
+
36
+ end
37
+
38
+ end
@@ -0,0 +1,64 @@
1
+ # -*- coding: utf-8 -*-
2
+ require 'minitest/autorun'
3
+ require 'simplecov'
4
+ SimpleCov.start
5
+
6
+ describe Blob do
7
+
8
+ let(:dir){ "abc" }
9
+ let(:base){ "def" }
10
+ let(:blob){ Blob.new(dir: dir, base: base) }
11
+
12
+ # We choose to use Unix-friendly file names: lowercase a-z and underscores,
13
+ # always with "/" as a directory separator, typically with a dot extension,
14
+ # never with non-ASCII characters.
15
+ #
16
+ let(:base_match){ /^[0-1a-z\_\.]+$/ }
17
+ let(:dir_match){ /^[0-1a-z\_\/]+$/ }
18
+ let(:path_match){ /^[0-1a-z\_\/\.]+$/ }
19
+
20
+ describe "#file_dir" do
21
+
22
+ it "is the dir" do
23
+ blob.file_dir.must_equal dir
24
+ end
25
+
26
+ it "uses a good unix name" do
27
+ blob.file_dir.must_match dir_match
28
+ end
29
+
30
+ end
31
+
32
+ describe "#file_base" do
33
+
34
+ it "is the base" do
35
+ blob.file_base.must_equal base
36
+ end
37
+
38
+ it "uses a good unix name" do
39
+ blob.file_base.must_match base_match
40
+ end
41
+
42
+ end
43
+
44
+ describe "#file_path" do
45
+
46
+ it "combines the dir and base" do
47
+ blob.file_path.must_equal "#{dir}/#{base}"
48
+ end
49
+
50
+ it "uses a good unix name" do
51
+ blob.file_path.must_match path_match
52
+ end
53
+
54
+ end
55
+
56
+ describe "#file_exist?" do
57
+
58
+ it "returns true iff the file exists" do
59
+ blob.file_exist? #TODO
60
+ end
61
+
62
+ end
63
+
64
+ end
@@ -0,0 +1,74 @@
1
+ # -*- coding: utf-8 -*-
2
+ require 'minitest/autorun'
3
+ require 'simplecov'
4
+ SimpleCov.start
5
+
6
+ describe Blob do
7
+
8
+ DIR = Pathname(__FILE__).dirname + "samples"
9
+ BASE = "upload_output.bin"
10
+ TEMPFILE_PATH = DIR + "upload_input.bin"
11
+ TEMPFILE_BINMODE = true
12
+
13
+ let(:blob){ Blob.new(dir: DIR, base: BASE) }
14
+ let(:original_filename){ "hello" }
15
+ let(:content_type){ "abc/def" }
16
+
17
+ # Mocks
18
+ let(:tempfile){ OpenStruct.new(path: TEMPFILE_PATH, binmode: TEMPFILE_BINMODE) }
19
+ let(:file_field){ OpenStruct.new(content_type: content_type, original_filename: original_filename, tempfile: tempfile) }
20
+
21
+ describe "upload" do
22
+
23
+ before do
24
+ @file_path = blob.file_path
25
+
26
+ # Delete a previously-uploaded file if it exists
27
+ File.exists?(@file_path) and File.delete(@file_path)
28
+
29
+ # Sanity check
30
+ File.exists?(tempfile.path).must_be_true "Temp file must exist: #{tempfile.path}"
31
+
32
+ end
33
+
34
+ describe ".upload" do
35
+
36
+ it "uploads" do
37
+ File.exists?(@file_path).must_be_false "Blob file must not exist: #{@file_path}"
38
+ blob.upload(file_field).must_be_true
39
+ File.exists?(@file_path).must_be_true "Blob file must exist: #{@file_path}"
40
+ end
41
+
42
+ end
43
+
44
+ describe "#upload" do
45
+
46
+ it "uploads" do
47
+ File.exists?(@file_path).must_be_false "Blob file must not exist: #{@file_path}"
48
+ Blob.upload(@file_path, file_field).must_be_true
49
+ File.exists?(@file_path).must_be_true "Blob file must exist: #{@file_path}"
50
+ end
51
+
52
+ end
53
+
54
+ describe "#vet_file_field" do
55
+
56
+ before do
57
+ # Sanity check
58
+ file_field.must_exist
59
+ file_field.must_respond_to :tempfile
60
+ file_field.tempfile.must_be_exist
61
+ file_field.tempfile.must_respond_to :path
62
+ file_field.tempfile.must_respond_to :binmode
63
+ file_field.tempfile.path.must_exist
64
+ end
65
+
66
+ it "vets" do
67
+ Blob.vet_file_field?(file_field).must_be_true
68
+ end
69
+
70
+ end
71
+
72
+ end
73
+
74
+ end
@@ -0,0 +1,54 @@
1
+ # -*- coding: utf-8 -*-
2
+ require 'minitest/autorun'
3
+ require 'simplecov'
4
+ SimpleCov.start
5
+
6
+ describe Blob do
7
+
8
+ let(:dir){ "foo" }
9
+ let(:base){ "bar" }
10
+ let(:blob){ Blob.new(dir: dir, base: base) }
11
+
12
+ describe "#uri_dir" do
13
+
14
+ it "is the dir" do
15
+ blob.uri_dir.must_equal dir
16
+ end
17
+
18
+ end
19
+
20
+ describe "#uri_base" do
21
+
22
+ it "is the base" do
23
+ blob.uri_base.must_equal base
24
+ end
25
+
26
+ end
27
+
28
+ describe "#uri" do
29
+
30
+ it "returns a URI that we can parse successfully" do
31
+ URI.parse(blob.uri)
32
+ end
33
+
34
+ end
35
+
36
+ describe "#uri_cacheless" do
37
+
38
+ it "gets a URI that we can parse successfully" do
39
+ URI.parse(blob.uri_cacheless)
40
+ end
41
+
42
+ it "gets a URI that has a unique id appended" do
43
+ blob.uri_cacheless.must_match /\?cacheless=[-0-9abcdef]{36}$/
44
+ end
45
+
46
+ it "returns a URI that is different each time" do
47
+ x = blob.uri_cacheless
48
+ y = blob.uri_cacheless
49
+ x.wont_equal y
50
+ end
51
+
52
+ end
53
+
54
+ end
@@ -10,8 +10,6 @@ require 'sixarm_ruby_blob'
10
10
  require 'securerandom'
11
11
  require 'uri'
12
12
 
13
-
14
-
15
13
  FAB = Fab.new
16
14
 
17
15
  ['base','dir','file','export','upload','uri'].map{|x|
data.tar.gz.sig CHANGED
Binary file
metadata CHANGED
@@ -1,7 +1,7 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: sixarm_ruby_blob
3
3
  version: !ruby/object:Gem::Version
4
- version: 1.0.0
4
+ version: 1.0.1
5
5
  platform: ruby
6
6
  authors:
7
7
  - SixArm
@@ -70,7 +70,20 @@ files:
70
70
  - README.md
71
71
  - VERSION
72
72
  - lib/sixarm_ruby_blob.rb
73
+ - lib/sixarm_ruby_blob/base.rb
74
+ - lib/sixarm_ruby_blob/dir.rb
75
+ - lib/sixarm_ruby_blob/file.rb
76
+ - lib/sixarm_ruby_blob/export.rb
77
+ - lib/sixarm_ruby_blob/upload.rb
78
+ - lib/sixarm_ruby_blob/uri.rb
73
79
  - test/sixarm_ruby_blob_test.rb
80
+ - test/sixarm_ruby_blob_test/base_test.rb
81
+ - test/sixarm_ruby_blob_test/dir_test.rb
82
+ - test/sixarm_ruby_blob_test/file_test.rb
83
+ - test/sixarm_ruby_blob_test/export_test.rb
84
+ - test/sixarm_ruby_blob_test/upload_test.rb
85
+ - test/sixarm_ruby_blob_test/uri_test.rb
86
+ - test/sixarm_ruby_blob_test/samples/upload_input.bin
74
87
  homepage: http://sixarm.com/
75
88
  licenses:
76
89
  - BSD
@@ -101,4 +114,11 @@ specification_version: 4
101
114
  summary: SixArm.com » Ruby » Blob of data abstract base class
102
115
  test_files:
103
116
  - test/sixarm_ruby_blob_test.rb
117
+ - test/sixarm_ruby_blob_test/base_test.rb
118
+ - test/sixarm_ruby_blob_test/dir_test.rb
119
+ - test/sixarm_ruby_blob_test/file_test.rb
120
+ - test/sixarm_ruby_blob_test/export_test.rb
121
+ - test/sixarm_ruby_blob_test/upload_test.rb
122
+ - test/sixarm_ruby_blob_test/uri_test.rb
123
+ - test/sixarm_ruby_blob_test/samples/upload_input.bin
104
124
  has_rdoc: true
metadata.gz.sig CHANGED
Binary file