content_type 1.1.0

Sign up to get free protection for your applications and to get access to all the features.
@@ -0,0 +1,38 @@
1
+ require 'rake'
2
+ require 'spec'
3
+ require 'spec/rake/spectask'
4
+
5
+ require 'vendor/gems/environment'
6
+
7
+ task :default => [:clean, :make, :spec]
8
+
9
+ task :make do
10
+ Dir.chdir 'ext' do
11
+ puts `ruby extconf.rb`
12
+ puts `make`
13
+ end
14
+ end
15
+
16
+ Spec::Rake::SpecTask.new(:spec) do |t|
17
+ t.spec_opts = %w(-fs -c)
18
+ t.spec_files = FileList['spec/*_spec.rb']
19
+ end
20
+
21
+ task :clean do
22
+ `rm -r *.o *.so *.bundle conftest.dSYM 2>/dev/null`
23
+ end
24
+
25
+ require 'jeweler'
26
+ Jeweler::Tasks.new do |s|
27
+ s.name = 'content_type'
28
+ s.summary = 'libmagic bindings to quickly determine content type of files'
29
+ s.email = 'dsturnbull@me.com'
30
+ s.homepage = 'http://github.com/dsturnbull/content_type'
31
+ s.description =<<-eod
32
+ Provides ContentType#content_type, File#content_type and
33
+ File::content_type methods to determine mime type
34
+ eod
35
+ s.executables = []
36
+ s.authors = ['David Turnbull']
37
+ s.files = ['Rakefile', 'ext/content_type.c', 'ext/extconf.rb']
38
+ end
@@ -0,0 +1,117 @@
1
+ #include "ruby.h"
2
+ #include <magic.h>
3
+ #include <stdio.h>
4
+ #include <sys/stat.h>
5
+
6
+ #define MAGIC_OPTIONS MAGIC_SYMLINK | MAGIC_MIME_TYPE | MAGIC_PRESERVE_ATIME
7
+
8
+ VALUE content_type = Qnil;
9
+ VALUE content_type_initialize(VALUE self, VALUE path);
10
+ VALUE content_type_content_type(VALUE self);
11
+
12
+ VALUE file_content_type_wrap(VALUE self, VALUE path);
13
+ VALUE file_content_type(VALUE self);
14
+ VALUE file_singleton_content_type(VALUE self, VALUE path);
15
+
16
+ void magic_fail(const char *error);
17
+
18
+ void
19
+ Init_content_type()
20
+ {
21
+ // class definition
22
+ content_type = rb_define_class("ContentType", rb_cObject);
23
+
24
+ // instance methods
25
+ rb_define_method(content_type, "initialize", content_type_initialize, 1);
26
+ rb_define_method(content_type, "content_type", content_type_content_type, 0);
27
+
28
+ // instance attributes
29
+ rb_define_attr(content_type, "filepath", 1, 0);
30
+ rb_define_attr(content_type, "processed", 1, 0);
31
+
32
+ // hax on File
33
+ rb_define_method(rb_cFile,
34
+ "content_type", file_content_type, 0);
35
+ rb_define_singleton_method(rb_cFile,
36
+ "content_type", file_singleton_content_type, 1);
37
+ }
38
+
39
+ VALUE
40
+ content_type_initialize(VALUE self, VALUE path)
41
+ {
42
+ struct stat st;
43
+
44
+ SafeStringValue(path);
45
+
46
+ if ((stat(RSTRING_PTR(path), &st)) != 0)
47
+ rb_raise(rb_const_get(rb_cObject, rb_intern("ArgumentError")),
48
+ "invalid file");
49
+
50
+ rb_iv_set(self, "@content_type", rb_str_new("", 0));
51
+ rb_iv_set(self, "@filepath", path);
52
+ rb_iv_set(self, "@processed", Qfalse);
53
+
54
+ return self;
55
+ }
56
+
57
+ VALUE
58
+ content_type_content_type(VALUE self)
59
+ {
60
+ VALUE ct;
61
+ struct magic_set *mh;
62
+ const char *mime;
63
+
64
+ if (rb_iv_get(self, "@processed"))
65
+ return rb_iv_get(self, "@content_type");
66
+
67
+ if (!(mh = magic_open(MAGIC_OPTIONS)))
68
+ magic_fail("open");
69
+
70
+ if ((magic_load(mh, NULL)) != 0)
71
+ magic_fail("load");
72
+
73
+ if (!(mime = magic_file(mh, RSTRING_PTR(rb_iv_get(self, "@filepath")))))
74
+ magic_fail("file");
75
+
76
+ ct = rb_str_new(mime, strlen(mime));
77
+ rb_iv_set(self, "@content_type", ct);
78
+ rb_iv_set(self, "@processed", Qtrue);
79
+ magic_close(mh);
80
+
81
+ return ct;
82
+ }
83
+
84
+ VALUE
85
+ file_content_type_wrap(VALUE self, VALUE path)
86
+ {
87
+ VALUE ct, mime, args[1];
88
+
89
+ SafeStringValue(path);
90
+ args[0] = path;
91
+ ct = rb_class_new_instance(1, args, content_type);
92
+
93
+ return rb_funcall(ct, rb_intern("content_type"), 0);
94
+ }
95
+
96
+ VALUE
97
+ file_content_type(VALUE self)
98
+ {
99
+ return file_content_type_wrap(self, rb_funcall(self, rb_intern("path"), 0));
100
+ }
101
+
102
+ VALUE
103
+ file_singleton_content_type(VALUE self, VALUE path)
104
+ {
105
+ return file_content_type_wrap(self, path);
106
+ }
107
+
108
+ void
109
+ magic_fail(const char *error)
110
+ {
111
+ const char *format = "magic_%s() error";
112
+ char *error_message;
113
+
114
+ error_message = malloc(sizeof(char *) * (strlen(format) + strlen(error)));
115
+ sprintf((char *)error_message, format, error);
116
+ rb_raise(rb_const_get(rb_cObject, rb_intern("RuntimeError")), error_message);
117
+ }
@@ -0,0 +1,8 @@
1
+ require 'mkmf'
2
+ extension_name = 'content_type'
3
+ dir_config(extension_name)
4
+
5
+ find_header('magic.h', '/opt/local/include')
6
+ find_library('magic', 'magic_open', '/opt/local/lib')
7
+
8
+ create_makefile(extension_name)
@@ -0,0 +1,83 @@
1
+ require 'rubygems'
2
+ require 'should_be_faster'
3
+ require 'tempfile'
4
+ require 'ext/content_type'
5
+
6
+ describe ContentType do
7
+ before do
8
+ @img = 'spec/fixtures/grindewald.jpg'
9
+ @pdf = 'spec/fixtures/pdftest.pdf'
10
+ @lzm = 'spec/fixtures/compressed.jpg.lz'
11
+ end
12
+
13
+ context 'initialising' do
14
+ it 'should take a file path' do
15
+ path = @img
16
+ ct = ContentType.new(path)
17
+ ct.filepath.should == path
18
+ end
19
+
20
+ it 'should check that the file exists' do
21
+ lambda {
22
+ ct = ContentType.new('poopstain')
23
+ }.should raise_error ArgumentError
24
+ end
25
+
26
+ it 'should memoize the content type' do
27
+ ct = ContentType.new(@img)
28
+ lambda {
29
+ ct.content_type.should == 'image/jpeg'
30
+ }.should change(ct, :processed).to(true)
31
+ end
32
+ end
33
+
34
+ context 'detecting mime type' do
35
+ it 'should detect images' do
36
+ ct = ContentType.new(@img)
37
+ ct.content_type.should == 'image/jpeg'
38
+ end
39
+
40
+ it 'should detect pdfs' do
41
+ ct = ContentType.new(@pdf)
42
+ ct.content_type.should == 'application/pdf'
43
+ end
44
+
45
+ it 'should detect lzma files' do
46
+ ct = ContentType.new(@lzm)
47
+ ct.content_type.should == 'application/x-lzip'
48
+ end
49
+ end
50
+
51
+ context 'comparing with shelling out' do
52
+ it 'should be much faster' do
53
+ shell = lambda { `file -i #{@img}` }
54
+ ext = lambda { ContentType.new(@img).content_type }
55
+ ext.should be_at_least(5).times.faster_than(shell)
56
+ end
57
+
58
+ it 'should slower to reinstantiate' do
59
+ @ct = ContentType.new(@img)
60
+ memo = lambda { @ct.content_type }
61
+ kgo = lambda { ContentType.new(@img).content_type }
62
+ kgo.should be_at_least(5).times.slower_than(memo, :iterations => 1000)
63
+ end
64
+ end
65
+
66
+ context 'when using the file class methods' do
67
+ it 'should have File.content_type(path)' do
68
+ File.content_type(@img).should == 'image/jpeg'
69
+ end
70
+
71
+ it 'should have File#content_type' do
72
+ File.open(@img).content_type.should == 'image/jpeg'
73
+ end
74
+
75
+ it 'should make it into tempfile' do
76
+ t = Tempfile.new('hi')
77
+ t.content_type.should == 'application/x-empty'
78
+ t << File.read(@img)
79
+ t.content_type.should == 'image/jpeg'
80
+ File.unlink(t.path)
81
+ end
82
+ end
83
+ end
metadata ADDED
@@ -0,0 +1,64 @@
1
+ --- !ruby/object:Gem::Specification
2
+ name: content_type
3
+ version: !ruby/object:Gem::Version
4
+ prerelease: false
5
+ segments:
6
+ - 1
7
+ - 1
8
+ - 0
9
+ version: 1.1.0
10
+ platform: ruby
11
+ authors:
12
+ - David Turnbull
13
+ autorequire:
14
+ bindir: bin
15
+ cert_chain: []
16
+
17
+ date: 2010-03-16 00:00:00 +11:00
18
+ default_executable:
19
+ dependencies: []
20
+
21
+ description: " Provides ContentType#content_type, File#content_type and\n File::content_type methods to determine mime type\n"
22
+ email: dsturnbull@me.com
23
+ executables: []
24
+
25
+ extensions:
26
+ - ext/extconf.rb
27
+ extra_rdoc_files: []
28
+
29
+ files:
30
+ - Rakefile
31
+ - ext/content_type.c
32
+ - ext/extconf.rb
33
+ has_rdoc: true
34
+ homepage: http://github.com/dsturnbull/content_type
35
+ licenses: []
36
+
37
+ post_install_message:
38
+ rdoc_options:
39
+ - --charset=UTF-8
40
+ require_paths:
41
+ - lib
42
+ required_ruby_version: !ruby/object:Gem::Requirement
43
+ requirements:
44
+ - - ">="
45
+ - !ruby/object:Gem::Version
46
+ segments:
47
+ - 0
48
+ version: "0"
49
+ required_rubygems_version: !ruby/object:Gem::Requirement
50
+ requirements:
51
+ - - ">="
52
+ - !ruby/object:Gem::Version
53
+ segments:
54
+ - 0
55
+ version: "0"
56
+ requirements: []
57
+
58
+ rubyforge_project:
59
+ rubygems_version: 1.3.6
60
+ signing_key:
61
+ specification_version: 3
62
+ summary: libmagic bindings to quickly determine content type of files
63
+ test_files:
64
+ - spec/content_type_spec.rb