metahash 0.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/.gitignore ADDED
@@ -0,0 +1,6 @@
1
+ *.gem
2
+ .bundle
3
+ Gemfile.lock
4
+ .rspec
5
+ pkg/*
6
+ vendor
data/Gemfile ADDED
@@ -0,0 +1,4 @@
1
+ source "http://rubygems.org"
2
+
3
+ # Specify your gem's dependencies in metahash.gemspec
4
+ gemspec
data/Rakefile ADDED
@@ -0,0 +1 @@
1
+ require "bundler/gem_tasks"
@@ -0,0 +1,3 @@
1
+ module Metahash
2
+ VERSION = "0.0.1"
3
+ end
data/lib/metahash.rb ADDED
@@ -0,0 +1,91 @@
1
+ require 'metahash/version'
2
+ require 'rubygems'
3
+ require 'bson'
4
+ require 'json'
5
+ module Metahash
6
+ class Metahash
7
+ BOM = (JSON '["\uFEFF"]')[0]
8
+ SEARCH_BYTE_OFFSET = 4
9
+ SEARCH_BYTE_SEQUENCE = "\003bson-\357\273\277"
10
+ def initialize(path,options={})
11
+ raise ArgumentError unless File.exists? path
12
+ @path = path
13
+ @options = options
14
+ bson = read_for_search File.open(@path,"rb")
15
+ if bson
16
+ @bson_range = bson[:range]
17
+ @bson_size = bson[:size]
18
+ @bytes = bson[:bytes]
19
+ end
20
+ self
21
+ end
22
+ # returns current state of metahash, false if no tag attahed, true if tag attached
23
+ def tagged?
24
+ !@bson_size.nil?
25
+ end
26
+
27
+ # returns a hash
28
+ def to_h
29
+ return nil if @bytes.nil?
30
+ packet = BSON.deserialize(BSON::ByteBuffer.new(@bytes[@bson_range]))
31
+ packet["bson-#{BOM}"]["object"]
32
+ end
33
+
34
+ # allow transparent usage like hash
35
+ # TODO: This is ridiculous, truly a monkey patch to provide cool functionality, please implement hash correctly
36
+ def method_missing(method,*args)
37
+ if {}.respond_to?(method)
38
+ hash = to_h||{}
39
+ result = hash.send(method,*args)
40
+ write hash
41
+ result
42
+ else
43
+ super
44
+ end
45
+ end
46
+ def write(obj)
47
+ byte_string = BSON.serialize(wrap_obj(obj)).to_s
48
+ if @bytes
49
+ #take 0..bson_start
50
+ #serialize object
51
+ #with bson_end..-1
52
+ @bytes=@bytes[0..@bson_range.first]+byte_string+@bytes[@bson_range.last..-1]
53
+ #write and reinitialize
54
+ File.truncate(@path,0)
55
+ File.open(@path,"wb+") do |file|
56
+ file.write(@bytes)
57
+ end
58
+ else
59
+ File.open(@path,"ab+") do |file|
60
+ file.write(byte_string)
61
+ end
62
+ end
63
+ initialize(@path,@options)
64
+ end
65
+ private
66
+
67
+ # wraps obj in Meta Hash header
68
+ def wrap_obj(obj)
69
+ meta_bson = {
70
+ "bson-#{BOM}"=>{
71
+ "version"=>VERSION,
72
+ "ns"=>["http://json-schema.org/card.properties","http://groups.google.com/group/json-schema/browse_thread/thread/dd1a8c9e55035c67?pli=1"],
73
+ "object"=> obj
74
+ }
75
+ }
76
+ end
77
+ def read_for_search(io)
78
+ bytes = io.read
79
+ search_position = bytes.index(SEARCH_BYTE_SEQUENCE)
80
+ return nil unless search_position
81
+ bson_start = search_position - SEARCH_BYTE_OFFSET
82
+ bson_size = bytes[bson_start..(search_position-1)].unpack("S")[0]
83
+ bson_end = bson_start + bson_size
84
+ return {
85
+ :range => bson_start..bson_end,
86
+ :size => bson_size,
87
+ :bytes => bytes
88
+ }
89
+ end
90
+ end
91
+ end
data/metahash.gemspec ADDED
@@ -0,0 +1,28 @@
1
+ # -*- encoding: utf-8 -*-
2
+ $:.push File.expand_path("../lib", __FILE__)
3
+ require "metahash/version"
4
+
5
+ Gem::Specification.new do |s|
6
+ s.name = "metahash"
7
+ s.version = Metahash::VERSION
8
+ s.authors = ["Thomas Devol"]
9
+ s.email = ["thomas.devol@gmail.com"]
10
+ s.homepage = ""
11
+ s.summary = %q{Provides metadata management for many file types}
12
+ s.description = %q{Successor to exif/id3/xmp for metadata management}
13
+
14
+ s.rubyforge_project = "metahash"
15
+
16
+ s.files = `git ls-files`.split("\n")
17
+ s.test_files = `git ls-files -- {test,spec,features}/*`.split("\n")
18
+ s.executables = `git ls-files -- bin/*`.split("\n").map{ |f| File.basename(f) }
19
+ s.require_paths = ["lib"]
20
+
21
+ s.add_development_dependency "rspec"
22
+ s.add_development_dependency "ruby-debug"
23
+ s.add_runtime_dependency "bson"
24
+ s.add_runtime_dependency "json"
25
+ # specify any dependencies here; for example:
26
+ # s.add_development_dependency "rspec"
27
+ # s.add_runtime_dependency "rest-client"
28
+ end
@@ -0,0 +1,132 @@
1
+ require 'metahash'
2
+ require 'ruby-debug'
3
+ module Metahash
4
+ describe Metahash do
5
+ before {
6
+ @test_path = File.dirname(__FILE__)+"/test.txt"
7
+ @test_data = {"hello"=>"world"}
8
+ }
9
+
10
+ context "generally" do #high level expectations
11
+
12
+ it "should be able to append to a new file" do
13
+
14
+ mh = Metahash.new @test_path
15
+ mh.write({"hello"=>"world"})
16
+
17
+ mh = Metahash.new @test_path
18
+ obj = mh.to_h
19
+ obj.should be_a(Hash)
20
+ obj["hello"].should == "world"
21
+
22
+ end
23
+ it "should be able to modify an already tagged file" do
24
+ mh = Metahash.new @test_path
25
+ mh.write({"hello"=>"world"})
26
+
27
+ mh = Metahash.new @test_path
28
+ mh.write({"hello"=>"holmes"})
29
+
30
+ mh = Metahash.new @test_path
31
+ obj = mh.to_h
32
+ obj.should be_a(Hash)
33
+ obj["hello"].should == "holmes"
34
+ end
35
+ end
36
+ describe "specifically" do
37
+ let(:new_metahash_with_untagged_file) do
38
+ f = File.open @test_path,"w+"
39
+ f.write "Hello World"
40
+ f.close
41
+ Metahash.new @test_path
42
+ end
43
+ let(:new_metahash_with_tagged_file) do
44
+ mh = new_metahash_with_untagged_file
45
+ mh.write @test_data
46
+ mh
47
+ end
48
+
49
+ describe "#initialize" do
50
+ it "should throw an error on an invalid path" do
51
+ invalid_path = rand().to_s+@test_path
52
+ expect{
53
+ mh = Metahash.new invalid_path
54
+ }.to raise_error(ArgumentError)
55
+ end
56
+ it "should not throw an error on a valid path" do
57
+ lambda {
58
+ new_metahash_with_untagged_file
59
+ }.should_not raise_error(ArgumentError)
60
+ end
61
+ end
62
+ describe "#tagged?" do
63
+ it "should return false for untagged files" do
64
+ new_metahash_with_untagged_file.should_not be_tagged
65
+ end
66
+ it "should return true for tagged files" do
67
+ new_metahash_with_tagged_file.should be_tagged
68
+ end
69
+ end
70
+ describe "#write" do
71
+ context "with an untagged file" do
72
+ subject &:new_metahash_with_untagged_file
73
+ it "should call wrap obj and serialize" do
74
+ subject.should_receive(:wrap_obj).with(@test_data)
75
+ BSON.should_receive :serialize
76
+ subject.write @test_data
77
+ end
78
+ it "should call initialize after write" do
79
+ subject.should_receive(:initialize).exactly(1).times
80
+ subject.write @test_data
81
+ end
82
+ end
83
+ context "with a tagged file" do
84
+ subject &:new_metahash_with_tagged_file
85
+ it "should truncate already tagged file" do
86
+ File.should_receive(:truncate).with(@test_path,0)
87
+ subject.write({"hello"=>"holmes"})
88
+ end
89
+ end
90
+ end
91
+ describe "#to_h" do
92
+ context "with an untagged file" do
93
+ subject &:new_metahash_with_untagged_file
94
+ it "should return nil" do
95
+ subject.to_h.should be_nil
96
+ end
97
+ end
98
+ context "with a tagged file" do
99
+ subject &:new_metahash_with_tagged_file
100
+ it "should return test data" do
101
+ subject.to_h.should == @test_data
102
+ end
103
+ end
104
+ end
105
+ describe "#method_missing" do
106
+ context "with a tagged file" do
107
+ subject &:new_metahash_with_tagged_file
108
+ it "should read just like a hash" do
109
+ subject["hello"].should == "world"
110
+ end
111
+ it "should write and persist" do
112
+ #debugger
113
+ subject["foo"] = "bar"
114
+ subject["foo"].should == "bar"
115
+ end
116
+ end
117
+ context "with an untagged file" do
118
+ subject &:new_metahash_with_untagged_file
119
+ it "should just work" do
120
+ subject["artist"] = "dave matthews"
121
+
122
+ mh = Metahash.new @test_path
123
+ mh["artist"].should == "dave matthews"
124
+ end
125
+ end
126
+
127
+
128
+ end
129
+ end
130
+
131
+ end
132
+ end
data/spec/test.txt ADDED
Binary file
metadata ADDED
@@ -0,0 +1,118 @@
1
+ --- !ruby/object:Gem::Specification
2
+ name: metahash
3
+ version: !ruby/object:Gem::Version
4
+ prerelease: false
5
+ segments:
6
+ - 0
7
+ - 0
8
+ - 1
9
+ version: 0.0.1
10
+ platform: ruby
11
+ authors:
12
+ - Thomas Devol
13
+ autorequire:
14
+ bindir: bin
15
+ cert_chain: []
16
+
17
+ date: 2011-09-30 00:00:00 -05:00
18
+ default_executable:
19
+ dependencies:
20
+ - !ruby/object:Gem::Dependency
21
+ name: rspec
22
+ prerelease: false
23
+ requirement: &id001 !ruby/object:Gem::Requirement
24
+ requirements:
25
+ - - ">="
26
+ - !ruby/object:Gem::Version
27
+ segments:
28
+ - 0
29
+ version: "0"
30
+ type: :development
31
+ version_requirements: *id001
32
+ - !ruby/object:Gem::Dependency
33
+ name: ruby-debug
34
+ prerelease: false
35
+ requirement: &id002 !ruby/object:Gem::Requirement
36
+ requirements:
37
+ - - ">="
38
+ - !ruby/object:Gem::Version
39
+ segments:
40
+ - 0
41
+ version: "0"
42
+ type: :development
43
+ version_requirements: *id002
44
+ - !ruby/object:Gem::Dependency
45
+ name: bson
46
+ prerelease: false
47
+ requirement: &id003 !ruby/object:Gem::Requirement
48
+ requirements:
49
+ - - ">="
50
+ - !ruby/object:Gem::Version
51
+ segments:
52
+ - 0
53
+ version: "0"
54
+ type: :runtime
55
+ version_requirements: *id003
56
+ - !ruby/object:Gem::Dependency
57
+ name: json
58
+ prerelease: false
59
+ requirement: &id004 !ruby/object:Gem::Requirement
60
+ requirements:
61
+ - - ">="
62
+ - !ruby/object:Gem::Version
63
+ segments:
64
+ - 0
65
+ version: "0"
66
+ type: :runtime
67
+ version_requirements: *id004
68
+ description: Successor to exif/id3/xmp for metadata management
69
+ email:
70
+ - thomas.devol@gmail.com
71
+ executables: []
72
+
73
+ extensions: []
74
+
75
+ extra_rdoc_files: []
76
+
77
+ files:
78
+ - .gitignore
79
+ - Gemfile
80
+ - Rakefile
81
+ - lib/metahash.rb
82
+ - lib/metahash/version.rb
83
+ - metahash.gemspec
84
+ - spec/metahash_spec.rb
85
+ - spec/test.txt
86
+ has_rdoc: true
87
+ homepage: ""
88
+ licenses: []
89
+
90
+ post_install_message:
91
+ rdoc_options: []
92
+
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: metahash
112
+ rubygems_version: 1.3.6
113
+ signing_key:
114
+ specification_version: 3
115
+ summary: Provides metadata management for many file types
116
+ test_files:
117
+ - spec/metahash_spec.rb
118
+ - spec/test.txt