content_type 1.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.
- data/Rakefile +38 -0
- data/ext/content_type.c +117 -0
- data/ext/extconf.rb +8 -0
- data/spec/content_type_spec.rb +83 -0
- metadata +64 -0
    
        data/Rakefile
    ADDED
    
    | @@ -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
         | 
    
        data/ext/content_type.c
    ADDED
    
    | @@ -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 | 
            +
            }
         | 
    
        data/ext/extconf.rb
    ADDED
    
    
| @@ -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
         |