joint 0.5 → 0.5.5

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/.gitignore ADDED
@@ -0,0 +1,7 @@
1
+ .DS_Store
2
+ log/*
3
+ tmp/*
4
+ doc/*
5
+ pkg/*
6
+ .bundle
7
+ *.swp
data/Gemfile ADDED
@@ -0,0 +1,5 @@
1
+ source "http://rubygems.org"
2
+
3
+ gem 'i18n', '0.4.2'
4
+ gem 'bson_ext', '~> 1.1', :require => false
5
+ gemspec
data/Gemfile.lock ADDED
@@ -0,0 +1,49 @@
1
+ PATH
2
+ remote: .
3
+ specs:
4
+ joint (0.5.2)
5
+ mime-types
6
+ mongo_mapper (~> 0.8.6)
7
+ wand (~> 0.4)
8
+
9
+ GEM
10
+ remote: http://rubygems.org/
11
+ specs:
12
+ activesupport (3.0.3)
13
+ bson (1.1.5)
14
+ bson_ext (1.1.5)
15
+ i18n (0.4.2)
16
+ jnunemaker-matchy (0.4.0)
17
+ jnunemaker-validatable (1.8.4)
18
+ activesupport (>= 2.3.4)
19
+ mime-types (1.16)
20
+ mocha (0.9.10)
21
+ rake
22
+ mongo (1.1.5)
23
+ bson (>= 1.1.5)
24
+ mongo_mapper (0.8.6)
25
+ activesupport (>= 2.3.4)
26
+ jnunemaker-validatable (~> 1.8.4)
27
+ plucky (~> 0.3.6)
28
+ plucky (0.3.6)
29
+ mongo (~> 1.1)
30
+ rake (0.8.7)
31
+ safe_shell (1.0.0)
32
+ shoulda (2.11.3)
33
+ wand (0.4)
34
+ mime-types
35
+ safe_shell (~> 1.0.0)
36
+
37
+ PLATFORMS
38
+ ruby
39
+
40
+ DEPENDENCIES
41
+ bson_ext (~> 1.1)
42
+ i18n (= 0.4.2)
43
+ jnunemaker-matchy
44
+ joint!
45
+ mime-types
46
+ mocha
47
+ mongo_mapper (~> 0.8.6)
48
+ shoulda
49
+ wand (~> 0.4)
data/Rakefile ADDED
@@ -0,0 +1,12 @@
1
+ require 'bundler'
2
+ Bundler::GemHelper.install_tasks
3
+
4
+ require 'rake/testtask'
5
+ Rake::TestTask.new do |test|
6
+ test.libs << 'lib' << 'test'
7
+ test.pattern = 'test/**/test_*.rb'
8
+ test.ruby_opts = ['-rubygems']
9
+ test.verbose = true
10
+ end
11
+
12
+ task :default => :test
data/joint.gemspec ADDED
@@ -0,0 +1,25 @@
1
+ # encoding: UTF-8
2
+ require File.expand_path('../lib/joint/version', __FILE__)
3
+
4
+ Gem::Specification.new do |s|
5
+ s.name = "joint"
6
+ s.summary = %Q{MongoMapper and GridFS joined in file upload love.}
7
+ s.description = %Q{MongoMapper and GridFS joined in file upload love.}
8
+ s.email = "nunemaker@gmail.com"
9
+ s.homepage = "http://github.com/jnunemaker/joint"
10
+ s.authors = ["John Nunemaker"]
11
+ s.version = Joint::Version
12
+
13
+ s.add_dependency 'wand', '~> 0.4'
14
+ s.add_dependency 'mime-types'
15
+ s.add_dependency 'mongo_mapper', '~> 0.8.6'
16
+
17
+ s.add_development_dependency 'shoulda'
18
+ s.add_development_dependency 'mocha'
19
+ s.add_development_dependency 'jnunemaker-matchy'
20
+
21
+ s.files = `git ls-files`.split("\n")
22
+ s.test_files = `git ls-files -- {test,spec,features}/*`.split("\n")
23
+ s.executables = `git ls-files -- bin/*`.split("\n").map{ |f| File.basename(f) }
24
+ s.require_paths = ["lib"]
25
+ end
@@ -0,0 +1,35 @@
1
+ module Joint
2
+ class AttachmentProxy
3
+ def initialize(instance, name)
4
+ @instance, @name = instance, name
5
+ end
6
+
7
+ def id
8
+ @instance.send("#{@name}_id")
9
+ end
10
+
11
+ def name
12
+ @instance.send("#{@name}_name")
13
+ end
14
+
15
+ def size
16
+ @instance.send("#{@name}_size")
17
+ end
18
+
19
+ def type
20
+ @instance.send("#{@name}_type")
21
+ end
22
+
23
+ def nil?
24
+ !@instance.send("#{@name}?")
25
+ end
26
+
27
+ def grid_io
28
+ @grid_io ||= @instance.grid.get(id)
29
+ end
30
+
31
+ def method_missing(method, *args, &block)
32
+ grid_io.send(method, *args, &block)
33
+ end
34
+ end
35
+ end
@@ -0,0 +1,49 @@
1
+ module Joint
2
+ module ClassMethods
3
+ def attachment_accessor_module
4
+ @attachment_accessor_module ||= Module.new
5
+ end
6
+
7
+ def attachment(name, options = {})
8
+ options.symbolize_keys!
9
+ name = name.to_sym
10
+
11
+ self.attachment_names << name
12
+
13
+ after_save :save_attachments
14
+ after_save :destroy_nil_attachments
15
+ before_destroy :destroy_all_attachments
16
+
17
+ key :"#{name}_id", ObjectId
18
+ key :"#{name}_name", String
19
+ key :"#{name}_size", Integer
20
+ key :"#{name}_type", String
21
+
22
+ validates_presence_of(name) if options[:required]
23
+
24
+ attachment_accessor_module.module_eval <<-EOC
25
+ def #{name}
26
+ @#{name} ||= AttachmentProxy.new(self, :#{name})
27
+ end
28
+
29
+ def #{name}?
30
+ !nil_attachments.include?(:#{name}) && send(:#{name}_id?)
31
+ end
32
+
33
+ def #{name}=(file)
34
+ if file.nil?
35
+ nil_attachments << :#{name}
36
+ assigned_attachments.delete(:#{name})
37
+ else
38
+ send("#{name}_id=", BSON::ObjectId.new) if send("#{name}_id").nil?
39
+ send("#{name}_name=", Joint.name(file))
40
+ send("#{name}_size=", Joint.size(file))
41
+ send("#{name}_type=", Joint.type(file))
42
+ assigned_attachments[:#{name}] = file
43
+ nil_attachments.delete(:#{name})
44
+ end
45
+ end
46
+ EOC
47
+ end
48
+ end
49
+ end
@@ -0,0 +1,54 @@
1
+ module Joint
2
+ module InstanceMethods
3
+ def grid
4
+ @grid ||= Mongo::Grid.new(database)
5
+ end
6
+
7
+ private
8
+ def assigned_attachments
9
+ @assigned_attachments ||= {}
10
+ end
11
+
12
+ def nil_attachments
13
+ @nil_attachments ||= Set.new
14
+ end
15
+
16
+ # IO must respond to read and rewind
17
+ def save_attachments
18
+ assigned_attachments.each_pair do |name, io|
19
+ next unless io.respond_to?(:read)
20
+ io.rewind if io.respond_to?(:rewind)
21
+ grid.delete(send(name).id)
22
+ grid.put(io.read, {
23
+ :_id => send(name).id,
24
+ :filename => send(name).name,
25
+ :content_type => send(name).type,
26
+ })
27
+ end
28
+ assigned_attachments.clear
29
+ end
30
+
31
+ def destroy_nil_attachments
32
+ # currently MM does not send sets to instance as well
33
+ nil_attachments.each do |name|
34
+ grid.delete(send(name).id)
35
+ send(:"#{name}_id=", nil)
36
+ send(:"#{name}_size=", nil)
37
+ send(:"#{name}_type=", nil)
38
+ send(:"#{name}_name=", nil)
39
+ set({
40
+ :"#{name}_id" => nil,
41
+ :"#{name}_size" => nil,
42
+ :"#{name}_type" => nil,
43
+ :"#{name}_name" => nil,
44
+ })
45
+ end
46
+
47
+ nil_attachments.clear
48
+ end
49
+
50
+ def destroy_all_attachments
51
+ self.class.attachment_names.map { |name| grid.delete(send(name).id) }
52
+ end
53
+ end
54
+ end
data/lib/joint/io.rb ADDED
@@ -0,0 +1,14 @@
1
+ module Joint
2
+ class IO
3
+ attr_accessor :name, :content, :type, :size
4
+
5
+ def initialize(attrs={})
6
+ attrs.each { |key, value| send("#{key}=", value) }
7
+ @type ||= 'plain/text'
8
+ @size ||= @content.size unless @content.nil?
9
+ end
10
+
11
+ alias path name
12
+ alias read content
13
+ end
14
+ end
data/lib/joint/version.rb CHANGED
@@ -1,3 +1,3 @@
1
1
  module Joint
2
- Version = '0.5'
2
+ Version = '0.5.5'
3
3
  end
data/lib/joint.rb CHANGED
@@ -3,150 +3,40 @@ require 'mime/types'
3
3
  require 'wand'
4
4
 
5
5
  module Joint
6
- autoload :Version, 'joint/version'
7
-
8
6
  def self.configure(model)
9
7
  model.class_inheritable_accessor :attachment_names
10
8
  model.attachment_names = Set.new
11
9
  model.send(:include, model.attachment_accessor_module)
12
10
  end
13
11
 
14
- module ClassMethods
15
- def attachment_accessor_module
16
- @attachment_accessor_module ||= Module.new
17
- end
18
-
19
- def attachment(name, options = {})
20
- options.symbolize_keys!
21
- name = name.to_sym
22
-
23
- self.attachment_names << name
24
-
25
- after_save :save_attachments
26
- after_save :destroy_nil_attachments
27
- before_destroy :destroy_all_attachments
28
-
29
- key :"#{name}_id", ObjectId
30
- key :"#{name}_name", String
31
- key :"#{name}_size", Integer
32
- key :"#{name}_type", String
33
-
34
- validates_presence_of(name) if options[:required]
35
-
36
- attachment_accessor_module.module_eval <<-EOC
37
- def #{name}
38
- @#{name} ||= AttachmentProxy.new(self, :#{name})
39
- end
40
-
41
- def #{name}?
42
- !nil_attachments.include?(:#{name}) && send(:#{name}_id?)
43
- end
44
-
45
- def #{name}=(file)
46
- if file.nil?
47
- nil_attachments << :#{name}
48
- assigned_attachments.delete(:#{name})
49
- else
50
- send("#{name}_id=", BSON::ObjectId.new) if send("#{name}_id").nil?
51
- send("#{name}_size=", File.size(file))
52
- send("#{name}_type=", Wand.wave(file.path))
53
- send("#{name}_name=", Joint.file_name(file))
54
- assigned_attachments[:#{name}] = file
55
- nil_attachments.delete(:#{name})
56
- end
57
- end
58
- EOC
12
+ def self.name(file)
13
+ if file.respond_to?(:original_filename)
14
+ file.original_filename
15
+ else
16
+ File.basename(file.path)
59
17
  end
60
18
  end
61
19
 
62
- module InstanceMethods
63
- def grid
64
- @grid ||= Mongo::Grid.new(database)
20
+ def self.size(file)
21
+ if file.respond_to?(:size)
22
+ file.size
23
+ else
24
+ File.size(file)
65
25
  end
66
-
67
- private
68
- def assigned_attachments
69
- @assigned_attachments ||= {}
70
- end
71
-
72
- def nil_attachments
73
- @nil_attachments ||= Set.new
74
- end
75
-
76
- # IO must respond to read and rewind
77
- def save_attachments
78
- assigned_attachments.each_pair do |name, io|
79
- next unless io.respond_to?(:read)
80
- io.rewind if io.respond_to?(:rewind)
81
- grid.delete(send(name).id)
82
- grid.put(io.read, {
83
- :_id => send(name).id,
84
- :filename => send(name).name,
85
- :content_type => send(name).type,
86
- })
87
- end
88
- assigned_attachments.clear
89
- end
90
-
91
- def destroy_nil_attachments
92
- # currently MM does not send sets to instance as well
93
- nil_attachments.each do |name|
94
- grid.delete(send(name).id)
95
- send(:"#{name}_id=", nil)
96
- send(:"#{name}_size=", nil)
97
- send(:"#{name}_type=", nil)
98
- send(:"#{name}_name=", nil)
99
- set({
100
- :"#{name}_id" => nil,
101
- :"#{name}_size" => nil,
102
- :"#{name}_type" => nil,
103
- :"#{name}_name" => nil,
104
- })
105
- end
106
-
107
- nil_attachments.clear
108
- end
109
-
110
- def destroy_all_attachments
111
- self.class.attachment_names.map { |name| grid.delete(send(name).id) }
112
- end
113
26
  end
114
27
 
115
- class AttachmentProxy
116
- def initialize(instance, name)
117
- @instance, @name = instance, name
118
- end
119
-
120
- def id
121
- @instance.send("#{@name}_id")
122
- end
123
-
124
- def name
125
- @instance.send("#{@name}_name")
126
- end
127
-
128
- def size
129
- @instance.send("#{@name}_size")
130
- end
131
-
132
- def type
133
- @instance.send("#{@name}_type")
134
- end
135
-
136
- def nil?
137
- !@instance.send("#{@name}?")
138
- end
139
-
140
- def grid_io
141
- @grid_io ||= @instance.grid.get(id)
142
- end
143
-
144
- def method_missing(method, *args, &block)
145
- grid_io.send(method, *args, &block)
146
- end
28
+ def self.type(file)
29
+ return file.type if file.is_a?(Joint::IO)
30
+ Wand.wave(file.path, :original_filename => Joint.name(file))
147
31
  end
148
32
 
149
- def self.file_name(file)
150
- file.respond_to?(:original_filename) ? file.original_filename : File.basename(file.path)
33
+ private
34
+ def self.blank?(str)
35
+ str.nil? || str !~ /\S/
151
36
  end
152
37
  end
38
+
39
+ require 'joint/class_methods'
40
+ require 'joint/instance_methods'
41
+ require 'joint/attachment_proxy'
42
+ require 'joint/io'
data/specs.watchr ADDED
@@ -0,0 +1,38 @@
1
+ def run(cmd)
2
+ puts(cmd)
3
+ output = ""
4
+ IO.popen(cmd) do |com|
5
+ com.each_char do |c|
6
+ print c
7
+ output << c
8
+ $stdout.flush
9
+ end
10
+ end
11
+ output
12
+ end
13
+
14
+ def run_test_file(file)
15
+ run %Q(ruby -I"lib:test" -rubygems #{file})
16
+ end
17
+
18
+ def run_all_tests
19
+ run "rake test"
20
+ end
21
+
22
+ def related_test_files(path)
23
+ Dir['test/**/*.rb'].select { |file| file =~ /test_#{File.basename(path)}/ }
24
+ end
25
+
26
+ watch('test/test_helper\.rb') { system('clear'); run_all_tests }
27
+ watch('test/.*test_.*\.rb') { |m| system('clear'); run_test_file(m[0]) }
28
+ watch('lib/.*') { |m| related_test_files(m[0]).each { |file| run_test_file(file) } }
29
+
30
+ # Ctrl-\
31
+ Signal.trap('QUIT') do
32
+ puts " --- Running all tests ---\n\n"
33
+ run_all_tests
34
+ end
35
+
36
+ # Ctrl-C
37
+ Signal.trap('INT') { abort("\n") }
38
+
Binary file
Binary file
data/test/helper.rb CHANGED
@@ -37,4 +37,44 @@ class Test::Unit::TestCase
37
37
  def assert_no_grid_difference(&block)
38
38
  assert_grid_difference(0, &block)
39
39
  end
40
+ end
41
+
42
+ class Asset
43
+ include MongoMapper::Document
44
+ plugin Joint
45
+
46
+ key :title, String
47
+ attachment :image
48
+ attachment :file
49
+ end
50
+
51
+ class BaseModel
52
+ include MongoMapper::Document
53
+ plugin Joint
54
+ attachment :file
55
+ end
56
+
57
+ class Image < BaseModel; attachment :image end
58
+ class Video < BaseModel; attachment :video end
59
+
60
+ module JointTestHelpers
61
+ def all_files
62
+ [@file, @image, @image2, @test1, @test2]
63
+ end
64
+
65
+ def rewind_files
66
+ all_files.each { |file| file.rewind }
67
+ end
68
+
69
+ def open_file(name)
70
+ File.open(File.join(File.dirname(__FILE__), 'fixtures', name), 'r')
71
+ end
72
+
73
+ def grid
74
+ @grid ||= Mongo::Grid.new(MongoMapper.database)
75
+ end
76
+
77
+ def key_names
78
+ [:id, :name, :type, :size]
79
+ end
40
80
  end
@@ -0,0 +1,28 @@
1
+ require 'helper'
2
+
3
+ class IOTest < Test::Unit::TestCase
4
+ context "#initialize" do
5
+ should "set attributes from hash" do
6
+ Joint::IO.new(:name => 'foo').name.should == 'foo'
7
+ end
8
+ end
9
+
10
+ should "default type to plain text" do
11
+ Joint::IO.new.type.should == 'plain/text'
12
+ end
13
+
14
+ should "default size to content size" do
15
+ content = 'This is my content'
16
+ Joint::IO.new(:content => content).size.should == content.size
17
+ end
18
+
19
+ should "alias path to name" do
20
+ Joint::IO.new(:name => 'foo').path.should == 'foo'
21
+ end
22
+
23
+ context "#read" do
24
+ should "return content" do
25
+ Joint::IO.new(:content => 'Testing').read.should == 'Testing'
26
+ end
27
+ end
28
+ end
data/test/test_joint.rb CHANGED
@@ -1,45 +1,5 @@
1
1
  require 'helper'
2
2
 
3
- class Asset
4
- include MongoMapper::Document
5
- plugin Joint
6
-
7
- key :title, String
8
- attachment :image
9
- attachment :file
10
- end
11
-
12
- class BaseModel
13
- include MongoMapper::Document
14
- plugin Joint
15
- attachment :file
16
- end
17
-
18
- class Image < BaseModel; attachment :image end
19
- class Video < BaseModel; attachment :video end
20
-
21
- module JointTestHelpers
22
- def all_files
23
- [@file, @image, @image2, @test1, @test2]
24
- end
25
-
26
- def rewind_files
27
- all_files.each { |file| file.rewind }
28
- end
29
-
30
- def open_file(name)
31
- File.open(File.join(File.dirname(__FILE__), 'fixtures', name), 'r')
32
- end
33
-
34
- def grid
35
- @grid ||= Mongo::Grid.new(MongoMapper.database)
36
- end
37
-
38
- def key_names
39
- [:id, :name, :type, :size]
40
- end
41
- end
42
-
43
3
  class JointTest < Test::Unit::TestCase
44
4
  include JointTestHelpers
45
5
 
@@ -56,6 +16,43 @@ class JointTest < Test::Unit::TestCase
56
16
  all_files.each { |file| file.close }
57
17
  end
58
18
 
19
+ context ".name" do
20
+ should "return original_filename" do
21
+ def @image.original_filename
22
+ 'frank.jpg'
23
+ end
24
+ Joint.name(@image).should == 'frank.jpg'
25
+ end
26
+
27
+ should "fall back to File.basename" do
28
+ Joint.name(@image).should == 'mr_t.jpg'
29
+ end
30
+ end
31
+
32
+ context ".size" do
33
+ should "return size" do
34
+ def @image.size
35
+ 25
36
+ end
37
+ Joint.size(@image).should == 25
38
+ end
39
+
40
+ should "fall back to File.size" do
41
+ Joint.size(@image).should == 13661
42
+ end
43
+ end
44
+
45
+ context ".type" do
46
+ should "return type if Joint::Io instance" do
47
+ file = Joint::IO.new(:type => 'image/jpeg')
48
+ Joint.type(@image).should == 'image/jpeg'
49
+ end
50
+
51
+ should "fall back to Wand" do
52
+ Joint.type(@image).should == 'image/jpeg'
53
+ end
54
+ end
55
+
59
56
  context "Using Joint plugin" do
60
57
  should "add each attachment to attachment_names" do
61
58
  Asset.attachment_names.should == Set.new([:image, :file])
@@ -343,4 +340,53 @@ class JointTest < Test::Unit::TestCase
343
340
  model.should be_valid
344
341
  end
345
342
  end
343
+
344
+ context "Assigning joint io instance" do
345
+ setup do
346
+ io = Joint::IO.new({
347
+ :name => 'foo.txt',
348
+ :type => 'plain/text',
349
+ :content => 'This is my stuff',
350
+ :size => 19,
351
+ })
352
+ @asset = Asset.create(:file => io)
353
+ end
354
+
355
+ should "work" do
356
+ @asset.file_name.should == 'foo.txt'
357
+ @asset.file_size.should == 19
358
+ @asset.file_type.should == 'plain/text'
359
+ @asset.file.read.should == 'This is my stuff'
360
+ end
361
+ end
362
+
363
+ context "A font file" do
364
+ setup do
365
+ @file = open_file('font.eot')
366
+ @doc = Asset.create(:file => @file)
367
+ end
368
+ subject { @doc }
369
+
370
+ should "assign joint keys" do
371
+ subject.file_size.should == 17610
372
+ subject.file_type.should == "application/octet-stream"
373
+ subject.file_id.should_not be_nil
374
+ subject.file_id.should be_instance_of(BSON::ObjectId)
375
+ end
376
+ end
377
+
378
+ context "A music file" do
379
+ setup do
380
+ @file = open_file('example.m4r')
381
+ @doc = Asset.create(:file => @file)
382
+ end
383
+ subject { @doc }
384
+
385
+ should "assign joint keys" do
386
+ subject.file_size.should == 50790
387
+ subject.file_type.should == "audio/mp4"
388
+ subject.file_id.should_not be_nil
389
+ subject.file_id.should be_instance_of(BSON::ObjectId)
390
+ end
391
+ end
346
392
  end
metadata CHANGED
@@ -6,7 +6,8 @@ version: !ruby/object:Gem::Version
6
6
  segments:
7
7
  - 0
8
8
  - 5
9
- version: "0.5"
9
+ - 5
10
+ version: 0.5.5
10
11
  platform: ruby
11
12
  authors:
12
13
  - John Nunemaker
@@ -14,7 +15,7 @@ autorequire:
14
15
  bindir: bin
15
16
  cert_chain: []
16
17
 
17
- date: 2010-12-06 00:00:00 -05:00
18
+ date: 2011-01-22 00:00:00 -05:00
18
19
  default_executable:
19
20
  dependencies:
20
21
  - !ruby/object:Gem::Dependency
@@ -25,12 +26,11 @@ dependencies:
25
26
  requirements:
26
27
  - - ~>
27
28
  - !ruby/object:Gem::Version
28
- hash: 21
29
+ hash: 3
29
30
  segments:
30
31
  - 0
31
- - 2
32
- - 1
33
- version: 0.2.1
32
+ - 4
33
+ version: "0.4"
34
34
  type: :runtime
35
35
  version_requirements: *id001
36
36
  - !ruby/object:Gem::Dependency
@@ -114,17 +114,30 @@ extensions: []
114
114
  extra_rdoc_files: []
115
115
 
116
116
  files:
117
- - lib/joint/version.rb
117
+ - .gitignore
118
+ - Gemfile
119
+ - Gemfile.lock
120
+ - LICENSE
121
+ - README.rdoc
122
+ - Rakefile
123
+ - joint.gemspec
118
124
  - lib/joint.rb
125
+ - lib/joint/attachment_proxy.rb
126
+ - lib/joint/class_methods.rb
127
+ - lib/joint/instance_methods.rb
128
+ - lib/joint/io.rb
129
+ - lib/joint/version.rb
130
+ - specs.watchr
131
+ - test/fixtures/example.m4r
132
+ - test/fixtures/font.eot
119
133
  - test/fixtures/harmony.png
120
134
  - test/fixtures/mr_t.jpg
121
135
  - test/fixtures/test1.txt
122
136
  - test/fixtures/test2.txt
123
137
  - test/fixtures/unixref.pdf
124
138
  - test/helper.rb
139
+ - test/joint/test_io.rb
125
140
  - test/test_joint.rb
126
- - LICENSE
127
- - README.rdoc
128
141
  has_rdoc: true
129
142
  homepage: http://github.com/jnunemaker/joint
130
143
  licenses: []
@@ -160,10 +173,13 @@ signing_key:
160
173
  specification_version: 3
161
174
  summary: MongoMapper and GridFS joined in file upload love.
162
175
  test_files:
176
+ - test/fixtures/example.m4r
177
+ - test/fixtures/font.eot
163
178
  - test/fixtures/harmony.png
164
179
  - test/fixtures/mr_t.jpg
165
180
  - test/fixtures/test1.txt
166
181
  - test/fixtures/test2.txt
167
182
  - test/fixtures/unixref.pdf
168
183
  - test/helper.rb
184
+ - test/joint/test_io.rb
169
185
  - test/test_joint.rb