joint 0.1
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.
- data/README.rdoc +36 -0
- data/Rakefile +36 -0
- data/lib/joint.rb +102 -0
- data/lib/joint/version.rb +3 -0
- data/test/fixtures/mr_t.jpg +0 -0
- data/test/fixtures/unixref.pdf +0 -0
- data/test/helper.rb +23 -0
- data/test/test_joint.rb +108 -0
- metadata +120 -0
data/README.rdoc
ADDED
@@ -0,0 +1,36 @@
|
|
1
|
+
= Joint
|
2
|
+
|
3
|
+
MongoMapper and GridFS united in file upload love.
|
4
|
+
|
5
|
+
== Usage
|
6
|
+
|
7
|
+
Declare the plugin and use the attachment method to make attachments.
|
8
|
+
|
9
|
+
class Foo
|
10
|
+
include MongoMapper::Document
|
11
|
+
plugin Joint
|
12
|
+
|
13
|
+
attachment :image
|
14
|
+
attachment :pdf
|
15
|
+
end
|
16
|
+
|
17
|
+
This gives you #image, #image=, #pdf, and #pdf=. The = methods take any IO that responds to read (File, Tempfile, etc). The image and pdf methods return a GridIO instance (can be found in the ruby driver).
|
18
|
+
|
19
|
+
== Dependencies
|
20
|
+
|
21
|
+
* MongoMapper 0.7.2 - gem install mongo_mapper
|
22
|
+
* Wand >= 0.2.1 - gem install wand
|
23
|
+
|
24
|
+
== Note on Patches/Pull Requests
|
25
|
+
|
26
|
+
* Fork the project.
|
27
|
+
* Make your feature addition or bug fix.
|
28
|
+
* Add tests for it. This is important so I don't break it in a
|
29
|
+
future version unintentionally.
|
30
|
+
* Commit, do not mess with rakefile, version, or history.
|
31
|
+
(if you want to have your own version, that is fine but bump version in a commit by itself I can ignore when I pull)
|
32
|
+
* Send me a pull request. Bonus points for topic branches.
|
33
|
+
|
34
|
+
== Copyright
|
35
|
+
|
36
|
+
Copyright (c) 2010 John Nunemaker. See LICENSE for details.
|
data/Rakefile
ADDED
@@ -0,0 +1,36 @@
|
|
1
|
+
require 'rubygems'
|
2
|
+
require 'rake'
|
3
|
+
require 'yard'
|
4
|
+
require 'jeweler'
|
5
|
+
require 'rake/testtask'
|
6
|
+
|
7
|
+
require File.join(File.dirname(__FILE__), 'lib', 'joint', 'version')
|
8
|
+
|
9
|
+
Jeweler::Tasks.new do |gem|
|
10
|
+
gem.name = "joint"
|
11
|
+
gem.summary = %Q{MongoMapper and GridFS united in file upload love.}
|
12
|
+
gem.description = %Q{MongoMapper and GridFS united in file upload love.}
|
13
|
+
gem.email = "nunemaker@gmail.com"
|
14
|
+
gem.homepage = "http://github.com/jnunemaker/joint"
|
15
|
+
gem.authors = ["John Nunemaker"]
|
16
|
+
gem.version = Joint::Version
|
17
|
+
gem.files = FileList['lib/**/*.rb', 'bin/*', '[A-Z]*', 'test/**/*'].to_a
|
18
|
+
gem.test_files = FileList['test/**/*'].to_a
|
19
|
+
|
20
|
+
gem.add_dependency 'wand', '>= 0.2.1'
|
21
|
+
gem.add_dependency 'mime-types'
|
22
|
+
|
23
|
+
gem.add_development_dependency 'jeweler'
|
24
|
+
gem.add_development_dependency 'mongo_mapper'
|
25
|
+
end
|
26
|
+
Jeweler::GemcutterTasks.new
|
27
|
+
|
28
|
+
Rake::TestTask.new(:test) do |test|
|
29
|
+
test.libs << 'lib' << 'test'
|
30
|
+
test.ruby_opts << '-rubygems'
|
31
|
+
test.pattern = 'test/**/test_*.rb'
|
32
|
+
test.verbose = true
|
33
|
+
end
|
34
|
+
|
35
|
+
task :test => :check_dependencies
|
36
|
+
task :default => :test
|
data/lib/joint.rb
ADDED
@@ -0,0 +1,102 @@
|
|
1
|
+
require 'mime/types'
|
2
|
+
require 'wand'
|
3
|
+
|
4
|
+
module Joint
|
5
|
+
autoload :Version, 'joint/version'
|
6
|
+
|
7
|
+
def self.file_name(file)
|
8
|
+
file.respond_to?(:original_filename) ? file.original_filename : File.basename(file.path)
|
9
|
+
end
|
10
|
+
|
11
|
+
module ClassMethods
|
12
|
+
def attachment(name)
|
13
|
+
self.class.class_inheritable_accessor :attachment_names
|
14
|
+
self.class.attachment_names ||= []
|
15
|
+
self.class.attachment_names << name
|
16
|
+
|
17
|
+
after_save :save_attachments
|
18
|
+
before_destroy :destroy_attached_files
|
19
|
+
|
20
|
+
key "#{name}_id".to_sym, ObjectId
|
21
|
+
key "#{name}_name".to_sym, String
|
22
|
+
key "#{name}_size".to_sym, Integer
|
23
|
+
key "#{name}_type".to_sym, String
|
24
|
+
|
25
|
+
define_method(name) do
|
26
|
+
AttachmentProxy.new(self, name)
|
27
|
+
end
|
28
|
+
|
29
|
+
define_method("#{name}=") do |file|
|
30
|
+
self["#{name}_id"] = Mongo::ObjectID.new
|
31
|
+
self["#{name}_size"] = File.size(file)
|
32
|
+
self["#{name}_type"] = Wand.wave(file.path)
|
33
|
+
self["#{name}_name"] = Joint.file_name(file)
|
34
|
+
attachment_assignments[name] = file
|
35
|
+
end
|
36
|
+
end
|
37
|
+
|
38
|
+
def grid
|
39
|
+
@grid ||= Mongo::Grid.new(database)
|
40
|
+
end
|
41
|
+
end
|
42
|
+
|
43
|
+
module InstanceMethods
|
44
|
+
def attachment_assignments
|
45
|
+
@attachment_assignments ||= {}
|
46
|
+
end
|
47
|
+
|
48
|
+
def grid
|
49
|
+
self.class.grid
|
50
|
+
end
|
51
|
+
|
52
|
+
private
|
53
|
+
def save_attachments
|
54
|
+
attachment_assignments.each do |attachment|
|
55
|
+
name, file = attachment
|
56
|
+
content_type = self["#{name}_type"]
|
57
|
+
|
58
|
+
if file.respond_to?(:read)
|
59
|
+
grid.put(file.read, self["#{name}_name"], :content_type => content_type, :_id => self["#{name}_id"])
|
60
|
+
end
|
61
|
+
end
|
62
|
+
|
63
|
+
@attachment_assignments.clear
|
64
|
+
end
|
65
|
+
|
66
|
+
def destroy_attached_files
|
67
|
+
self.class.attachment_names.each do |name|
|
68
|
+
grid.delete(self["#{name}_id"])
|
69
|
+
end
|
70
|
+
end
|
71
|
+
end
|
72
|
+
|
73
|
+
class AttachmentProxy
|
74
|
+
def initialize(instance, name)
|
75
|
+
@instance, @name = instance, name
|
76
|
+
end
|
77
|
+
|
78
|
+
def id
|
79
|
+
@instance.send("#{@name}_id")
|
80
|
+
end
|
81
|
+
|
82
|
+
def name
|
83
|
+
@instance.send("#{@name}_name")
|
84
|
+
end
|
85
|
+
|
86
|
+
def size
|
87
|
+
@instance.send("#{@name}_size")
|
88
|
+
end
|
89
|
+
|
90
|
+
def type
|
91
|
+
@instance.send("#{@name}_type")
|
92
|
+
end
|
93
|
+
|
94
|
+
def grid_io
|
95
|
+
@grid_io ||= @instance.grid.get(id)
|
96
|
+
end
|
97
|
+
|
98
|
+
def method_missing(method, *args, &block)
|
99
|
+
grid_io.send(method, *args, &block)
|
100
|
+
end
|
101
|
+
end
|
102
|
+
end
|
Binary file
|
Binary file
|
data/test/helper.rb
ADDED
@@ -0,0 +1,23 @@
|
|
1
|
+
require 'test/unit'
|
2
|
+
require 'tempfile'
|
3
|
+
require 'pp'
|
4
|
+
require 'mongo_mapper'
|
5
|
+
|
6
|
+
require File.expand_path(File.dirname(__FILE__) + '/../lib/joint')
|
7
|
+
|
8
|
+
MongoMapper.database = "testing"
|
9
|
+
|
10
|
+
class Test::Unit::TestCase
|
11
|
+
def self.test(name, &block)
|
12
|
+
test_name = "test_#{name.gsub(/\s+/,'_')}".to_sym
|
13
|
+
defined = instance_method(test_name) rescue false
|
14
|
+
raise "#{test_name} is already defined in #{self}" if defined
|
15
|
+
if block_given?
|
16
|
+
define_method(test_name, &block)
|
17
|
+
else
|
18
|
+
define_method(test_name) do
|
19
|
+
flunk "No implementation provided for #{name}"
|
20
|
+
end
|
21
|
+
end
|
22
|
+
end
|
23
|
+
end
|
data/test/test_joint.rb
ADDED
@@ -0,0 +1,108 @@
|
|
1
|
+
require 'helper'
|
2
|
+
|
3
|
+
class Foo
|
4
|
+
include MongoMapper::Document
|
5
|
+
plugin Joint
|
6
|
+
|
7
|
+
attachment :image
|
8
|
+
attachment :pdf
|
9
|
+
end
|
10
|
+
|
11
|
+
class JointTest < Test::Unit::TestCase
|
12
|
+
def setup
|
13
|
+
MongoMapper.database.collections.each(&:remove)
|
14
|
+
@grid = Mongo::Grid.new(MongoMapper.database)
|
15
|
+
|
16
|
+
dir = File.dirname(__FILE__) + '/fixtures'
|
17
|
+
@pdf = File.open("#{dir}/unixref.pdf", 'r')
|
18
|
+
@image = File.open("#{dir}/mr_t.jpg", 'r')
|
19
|
+
|
20
|
+
@pdf_contents = File.read("#{dir}/unixref.pdf")
|
21
|
+
@image_contents = File.read("#{dir}/mr_t.jpg")
|
22
|
+
|
23
|
+
@doc = Foo.create(:image => @image, :pdf => @pdf)
|
24
|
+
@doc.reload
|
25
|
+
end
|
26
|
+
|
27
|
+
def teardown
|
28
|
+
@pdf.close
|
29
|
+
@image.close
|
30
|
+
end
|
31
|
+
|
32
|
+
test "assigns grid fs content type correctly" do
|
33
|
+
assert_equal "image/jpeg", @grid.get(@doc.image_id).content_type
|
34
|
+
assert_equal "application/pdf", @grid.get(@doc.pdf_id).content_type
|
35
|
+
end
|
36
|
+
|
37
|
+
test "assigns keys correctly" do
|
38
|
+
assert_equal 13661, @doc.image_size
|
39
|
+
assert_equal 68926, @doc.pdf_size
|
40
|
+
|
41
|
+
assert_equal "image/jpeg", @doc.image_type
|
42
|
+
assert_equal "application/pdf", @doc.pdf_type
|
43
|
+
|
44
|
+
assert_not_nil @doc.image_id
|
45
|
+
assert_not_nil @doc.pdf_id
|
46
|
+
|
47
|
+
assert_kind_of Mongo::ObjectID, @doc.image_id
|
48
|
+
assert_kind_of Mongo::ObjectID, @doc.pdf_id
|
49
|
+
end
|
50
|
+
|
51
|
+
test "accessing keys through attachment proxy" do
|
52
|
+
assert_equal 13661, @doc.image.size
|
53
|
+
assert_equal 68926, @doc.pdf.size
|
54
|
+
|
55
|
+
assert_equal "image/jpeg", @doc.image.type
|
56
|
+
assert_equal "application/pdf", @doc.pdf.type
|
57
|
+
|
58
|
+
assert_not_nil @doc.image.id
|
59
|
+
assert_not_nil @doc.pdf.id
|
60
|
+
|
61
|
+
assert_kind_of Mongo::ObjectID, @doc.image.id
|
62
|
+
assert_kind_of Mongo::ObjectID, @doc.pdf.id
|
63
|
+
end
|
64
|
+
|
65
|
+
test "sends unknown proxy methods to grid io object" do
|
66
|
+
assert_equal 13661, @doc.image.file_length
|
67
|
+
assert_equal 'image/jpeg', @doc.image.content_type
|
68
|
+
assert_equal 'mr_t.jpg', @doc.image.filename
|
69
|
+
assert_equal @doc.image_id, @doc.image.files_id
|
70
|
+
end
|
71
|
+
|
72
|
+
test "assigns file name from path if original file name not available" do
|
73
|
+
assert_equal 'mr_t.jpg', @doc.image_name
|
74
|
+
assert_equal 'unixref.pdf', @doc.pdf_name
|
75
|
+
end
|
76
|
+
|
77
|
+
test "assigns file name from original filename if available" do
|
78
|
+
begin
|
79
|
+
file = Tempfile.new('testing.txt')
|
80
|
+
def file.original_filename
|
81
|
+
'testing.txt'
|
82
|
+
end
|
83
|
+
doc = Foo.create(:image => file)
|
84
|
+
assert_equal 'testing.txt', doc.image_name
|
85
|
+
ensure
|
86
|
+
file.close
|
87
|
+
end
|
88
|
+
end
|
89
|
+
|
90
|
+
test "responds to keys" do
|
91
|
+
[ :pdf_size, :pdf_id, :pdf_name, :pdf_type,
|
92
|
+
:image_size, :image_id, :image_name, :image_type
|
93
|
+
].each do |method|
|
94
|
+
assert @doc.respond_to?(method)
|
95
|
+
end
|
96
|
+
end
|
97
|
+
|
98
|
+
test "saves attachments correctly" do
|
99
|
+
assert_equal @pdf_contents, @doc.pdf.read
|
100
|
+
assert_equal @image_contents, @doc.image.read
|
101
|
+
end
|
102
|
+
|
103
|
+
test "cleans up attachments on destroy" do
|
104
|
+
@doc.destroy
|
105
|
+
assert_raises(Mongo::GridError) { @grid.get(@doc.image_id) }
|
106
|
+
assert_raises(Mongo::GridError) { @grid.get(@doc.pdf_id) }
|
107
|
+
end
|
108
|
+
end
|
metadata
ADDED
@@ -0,0 +1,120 @@
|
|
1
|
+
--- !ruby/object:Gem::Specification
|
2
|
+
name: joint
|
3
|
+
version: !ruby/object:Gem::Version
|
4
|
+
prerelease: false
|
5
|
+
segments:
|
6
|
+
- 0
|
7
|
+
- 1
|
8
|
+
version: "0.1"
|
9
|
+
platform: ruby
|
10
|
+
authors:
|
11
|
+
- John Nunemaker
|
12
|
+
autorequire:
|
13
|
+
bindir: bin
|
14
|
+
cert_chain: []
|
15
|
+
|
16
|
+
date: 2010-03-26 00:00:00 -04:00
|
17
|
+
default_executable:
|
18
|
+
dependencies:
|
19
|
+
- !ruby/object:Gem::Dependency
|
20
|
+
name: wand
|
21
|
+
prerelease: false
|
22
|
+
requirement: &id001 !ruby/object:Gem::Requirement
|
23
|
+
requirements:
|
24
|
+
- - ">="
|
25
|
+
- !ruby/object:Gem::Version
|
26
|
+
segments:
|
27
|
+
- 0
|
28
|
+
- 2
|
29
|
+
- 1
|
30
|
+
version: 0.2.1
|
31
|
+
type: :runtime
|
32
|
+
version_requirements: *id001
|
33
|
+
- !ruby/object:Gem::Dependency
|
34
|
+
name: mime-types
|
35
|
+
prerelease: false
|
36
|
+
requirement: &id002 !ruby/object:Gem::Requirement
|
37
|
+
requirements:
|
38
|
+
- - ">="
|
39
|
+
- !ruby/object:Gem::Version
|
40
|
+
segments:
|
41
|
+
- 0
|
42
|
+
version: "0"
|
43
|
+
type: :runtime
|
44
|
+
version_requirements: *id002
|
45
|
+
- !ruby/object:Gem::Dependency
|
46
|
+
name: jeweler
|
47
|
+
prerelease: false
|
48
|
+
requirement: &id003 !ruby/object:Gem::Requirement
|
49
|
+
requirements:
|
50
|
+
- - ">="
|
51
|
+
- !ruby/object:Gem::Version
|
52
|
+
segments:
|
53
|
+
- 0
|
54
|
+
version: "0"
|
55
|
+
type: :development
|
56
|
+
version_requirements: *id003
|
57
|
+
- !ruby/object:Gem::Dependency
|
58
|
+
name: mongo_mapper
|
59
|
+
prerelease: false
|
60
|
+
requirement: &id004 !ruby/object:Gem::Requirement
|
61
|
+
requirements:
|
62
|
+
- - ">="
|
63
|
+
- !ruby/object:Gem::Version
|
64
|
+
segments:
|
65
|
+
- 0
|
66
|
+
version: "0"
|
67
|
+
type: :development
|
68
|
+
version_requirements: *id004
|
69
|
+
description: MongoMapper and GridFS united in file upload love.
|
70
|
+
email: nunemaker@gmail.com
|
71
|
+
executables: []
|
72
|
+
|
73
|
+
extensions: []
|
74
|
+
|
75
|
+
extra_rdoc_files:
|
76
|
+
- README.rdoc
|
77
|
+
files:
|
78
|
+
- README.rdoc
|
79
|
+
- Rakefile
|
80
|
+
- lib/joint.rb
|
81
|
+
- lib/joint/version.rb
|
82
|
+
- test/fixtures/mr_t.jpg
|
83
|
+
- test/fixtures/unixref.pdf
|
84
|
+
- test/helper.rb
|
85
|
+
- test/test_joint.rb
|
86
|
+
has_rdoc: true
|
87
|
+
homepage: http://github.com/jnunemaker/joint
|
88
|
+
licenses: []
|
89
|
+
|
90
|
+
post_install_message:
|
91
|
+
rdoc_options:
|
92
|
+
- --charset=UTF-8
|
93
|
+
require_paths:
|
94
|
+
- lib
|
95
|
+
required_ruby_version: !ruby/object:Gem::Requirement
|
96
|
+
requirements:
|
97
|
+
- - ">="
|
98
|
+
- !ruby/object:Gem::Version
|
99
|
+
segments:
|
100
|
+
- 0
|
101
|
+
version: "0"
|
102
|
+
required_rubygems_version: !ruby/object:Gem::Requirement
|
103
|
+
requirements:
|
104
|
+
- - ">="
|
105
|
+
- !ruby/object:Gem::Version
|
106
|
+
segments:
|
107
|
+
- 0
|
108
|
+
version: "0"
|
109
|
+
requirements: []
|
110
|
+
|
111
|
+
rubyforge_project:
|
112
|
+
rubygems_version: 1.3.6
|
113
|
+
signing_key:
|
114
|
+
specification_version: 3
|
115
|
+
summary: MongoMapper and GridFS united in file upload love.
|
116
|
+
test_files:
|
117
|
+
- test/fixtures/mr_t.jpg
|
118
|
+
- test/fixtures/unixref.pdf
|
119
|
+
- test/helper.rb
|
120
|
+
- test/test_joint.rb
|