gaga 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.
@@ -0,0 +1,5 @@
1
+ *.gem
2
+ .bundle
3
+ .rvmrc
4
+ Gemfile.lock
5
+ pkg/*
data/Gemfile ADDED
@@ -0,0 +1,4 @@
1
+ source "http://rubygems.org"
2
+
3
+ # Specify your gem's dependencies in gaga.gemspec
4
+ gemspec
@@ -0,0 +1,43 @@
1
+ Gaga
2
+ ==========
3
+
4
+ Git as a key-value store! Build with [Grit](https://github.com/mojombo/grit), it supports SET, GET, KEYS, and DELETE operations. In addition, we can also get the change history key/values.
5
+
6
+ It can easily be enhanced to include other git features such as branches, diffs, etc
7
+
8
+ Example:
9
+
10
+ ```ruby
11
+
12
+ @gaga = Gaga.new(:path => File.expand_path('..', __FILE__))
13
+ @gaga.clear
14
+
15
+ # SET
16
+
17
+ @gaga['lady'] = "gaga"
18
+
19
+ # GET
20
+
21
+ @gaga['lady'] #=> "gaga"
22
+
23
+ # KEYS
24
+
25
+ @gaga.keys #=> ['lady']
26
+
27
+ # DELETE
28
+
29
+ @gaga.delete('lady') #=> 'gaga'
30
+
31
+ # LOG
32
+
33
+ @gaga.log('key')
34
+
35
+ # Produces:
36
+
37
+ [
38
+ {"message"=>"all clear","committer"=>{"name"=>"Matt Sears", "email"=>"matt@mattsears.com"}, "committed_date"=>"2011-09-05..."},
39
+ {"message"=>"set 'lady' ", "committer"=>{"name"=>"Matt Sears", "email"=>"matt@mattsears.com"}, "committed_date"=>"2011-09-05..."}
40
+ {"message"=>"delete 'lady' ", "committer"=>{"name"=>"Matt Sears", "email"=>"matt@mattsears.com"}, "committed_date"=>"2011-09-05..."}
41
+ ]
42
+
43
+ ```
@@ -0,0 +1,13 @@
1
+ require 'bundler'
2
+ Bundler::GemHelper.install_tasks
3
+
4
+ require 'rake/testtask'
5
+ task :default => :test
6
+
7
+ desc 'Run tests (default)'
8
+ Rake::TestTask.new(:test) do |t|
9
+ t.test_files = FileList['test/**/*_test.rb']
10
+ t.ruby_opts = ['-Itest', '-W0']
11
+ t.libs << "lib" << "test"
12
+ t.ruby_opts << '-rubygems' if defined? Gem
13
+ end
@@ -0,0 +1,23 @@
1
+ # -*- encoding: utf-8 -*-
2
+ $:.push File.expand_path("../lib", __FILE__)
3
+ require "gaga/version"
4
+
5
+ Gem::Specification.new do |s|
6
+ s.name = "gaga"
7
+ s.version = Gaga::VERSION
8
+ s.authors = ["Matt Sears"]
9
+ s.email = ["matt@mattsears.com"]
10
+ s.homepage = "http://github.com/mattsears/gaga"
11
+ s.summary = %q{Git as a key value store}
12
+ s.description = %q{Git as a key value store}
13
+ s.rubyforge_project = "gaga"
14
+
15
+ s.files = `git ls-files`.split("\n")
16
+ s.test_files = `git ls-files -- {test,spec,features}/*`.split("\n")
17
+ s.executables = `git ls-files -- bin/*`.split("\n").map{ |f| File.basename(f) }
18
+ s.require_paths = ["lib"]
19
+
20
+ # specify any dependencies here; for example:
21
+ # s.add_development_dependency "rspec"
22
+ s.add_runtime_dependency "grit"
23
+ end
@@ -0,0 +1,168 @@
1
+ require 'yaml'
2
+ require 'grit'
3
+ require 'gaga/version'
4
+
5
+ class Gaga
6
+
7
+ def initialize(options = {})
8
+ @options = options
9
+ unless ::File.exists?(File.join(path,'.git'))
10
+ Grit::Repo.init(path)
11
+ end
12
+ end
13
+
14
+ # Add the value to the to the store
15
+ #
16
+ # Example
17
+ # @store.set('key', 'value')
18
+ #
19
+ # Returns nothing
20
+ def set(key, value)
21
+ save("set '#{key}'") do |index|
22
+ index.add(key_for(key), encode(value))
23
+ end
24
+ end
25
+
26
+ # Shortcut for #set
27
+ #
28
+ # Example:
29
+ # @store[key] = 'value'
30
+ #
31
+ def []=(key, value)
32
+ set(key, value)
33
+ end
34
+
35
+ # Retrieve the value for the given key with a default value
36
+ #
37
+ # Example:
38
+ # @store.get(key) #=> value
39
+ #
40
+ # Returns the object found in the repo matching the key
41
+ def get(key, value = nil, *)
42
+ if head && blob = head.commit.tree / key_for(key)
43
+ decode(blob.data)
44
+ end
45
+ end
46
+
47
+ # Shortcut for #get
48
+ #
49
+ # Example:
50
+ # @store['key'] #=> value
51
+ #
52
+ def [](key)
53
+ get(key)
54
+ end
55
+
56
+ # Returns an array of key names contained in store
57
+ #
58
+ # Example:
59
+ # @store.keys #=> ['key1', 'key2']
60
+ #
61
+ def keys
62
+ head.commit.tree.contents.map{|blob| deserialize(blob.name) }
63
+ end
64
+
65
+ # Deletes commits matching the given key
66
+ #
67
+ # Example:
68
+ # @store.delete('key')
69
+ #
70
+ # Returns nothing
71
+ def delete(key, *)
72
+ self[key].tap do
73
+ save("deleted #{key}") {|index| index.delete(key_for(key)) }
74
+ end
75
+ end
76
+
77
+ # Deletes all contents of the store
78
+ #
79
+ # Returns nothing
80
+ def clear
81
+ save("all clear") do |index|
82
+ if tree = index.current_tree
83
+ tree.contents.each do |entry|
84
+ index.delete(key_for(entry.name))
85
+ end
86
+ end
87
+ end
88
+ end
89
+
90
+ # The commit log for the given key
91
+ #
92
+ # Example:
93
+ # @store.log('key') #=> [{"message"=>"Updated key"...}]
94
+ #
95
+ # Returns Array of commit data
96
+ def log(key)
97
+ git.log(branch, key_for(key)).map{ |commit| commit.to_hash }
98
+ end
99
+
100
+ # Find the key if exists in the git repo
101
+ #
102
+ # Example:
103
+ # @store.key? 'key' #=> true
104
+ #
105
+ # Returns true if found; false if not found
106
+ def key?(key)
107
+ !(head && head.commit.tree / key_for(key)).nil?
108
+ end
109
+
110
+ private
111
+
112
+ # Format the given key so that it ensures it's git worthy
113
+ def key_for(key)
114
+ key.is_a?(String) ? key : serialize(key)
115
+ end
116
+
117
+ # Given the file path, return a new Grit::Repo if found
118
+ def git
119
+ @git ||= Grit::Repo.new(path)
120
+ end
121
+
122
+ # The git branch to use for this store
123
+ def branch
124
+ @options[:branch] || 'master'
125
+ end
126
+
127
+ # Checks out the branch on the repo
128
+ def head
129
+ git.get_head(branch)
130
+ end
131
+
132
+ # Commits the the value into the git repository with the given commit message
133
+ def save(message)
134
+ index = git.index
135
+ if head
136
+ commit = head.commit
137
+ index.current_tree = commit.tree
138
+ end
139
+ yield index
140
+ index.commit(message, :parents => Array(commit), :head => branch) if index.tree.any?
141
+ end
142
+
143
+ # Converts the value to yaml format
144
+ def encode(value)
145
+ value.to_yaml
146
+ end
147
+
148
+ # Loads value as a Yaml structure
149
+ def decode(value)
150
+ YAML.load(value)
151
+ end
152
+
153
+ # Convert value to byte stream. This allows keys to be objects too
154
+ def serialize(value)
155
+ Marshal.dump(value)
156
+ end
157
+
158
+ # Converts value back to an object.
159
+ def deserialize(value)
160
+ Marshal.restore(value) rescue value
161
+ end
162
+
163
+ # Given that repo path set in the options, return the expanded file path
164
+ def path(key = '')
165
+ @path ||= File.join(File.expand_path(@options[:repo]), key)
166
+ end
167
+
168
+ end
@@ -0,0 +1,3 @@
1
+ class Gaga
2
+ VERSION = "0.0.1"
3
+ end
@@ -0,0 +1,93 @@
1
+ $:.unshift File.dirname(__FILE__)
2
+ require 'helper'
3
+
4
+ describe Gaga do
5
+
6
+ @types = {
7
+ "String" => ["lady", "gaga"],
8
+ "Object" => [{:lady => :gaga}, {:gaga => :ohai}]
9
+ }
10
+
11
+ before do
12
+ @store = Gaga.new(:repo => tmp_dir)
13
+ @store.clear
14
+ end
15
+
16
+ @types.each do |type, (key, key2)|
17
+
18
+ it "writes String values to keys" do
19
+ @store[key] = "value"
20
+ @store[key].must_equal "value"
21
+ end
22
+
23
+ it "reads from keys" do
24
+ @store[key].must_be_nil
25
+ end
26
+
27
+ it "returns a list of keys" do
28
+ @store[key] = "value"
29
+ @store.keys.must_include(key)
30
+ end
31
+
32
+ it "guarantees that a different String value is retrieved" do
33
+ value = "value"
34
+ @store[key] = value
35
+ @store[key].wont_be_same_as(value)
36
+ end
37
+
38
+ it "writes Object values to keys" do
39
+ @store[key] = {:foo => :bar}
40
+ @store[key].must_equal({:foo => :bar})
41
+ end
42
+
43
+ it "guarantees that a different Object value is retrieved" do
44
+ value = {:foo => :bar}
45
+ @store[key] = value
46
+ @store[key].wont_be_same_as(:foo => :bar)
47
+ end
48
+
49
+ it "returns false from key? if a key is not available" do
50
+ @store.key?(key).must_equal false
51
+ end
52
+
53
+ it "returns true from key? if a key is available" do
54
+ @store[key] = "value"
55
+ @store.key?(key).must_equal true
56
+ end
57
+
58
+ it "removes and return an element with a key from the store via delete if it exists" do
59
+ @store[key] = "value"
60
+ @store.delete(key).must_equal "value"
61
+ @store.key?(key).must_equal false
62
+ end
63
+
64
+ it "returns nil from delete if an element for a key does not exist" do
65
+ @store.delete(key).must_be_nil
66
+ end
67
+
68
+ it "removes all keys from the store with clear" do
69
+ @store[key] = "value"
70
+ @store[key2] = "value2"
71
+ @store.clear
72
+ @store.key?(key).wont_equal true
73
+ @store.key?(key2).wont_equal true
74
+ end
75
+
76
+ it "does not run the block if the #{type} key is available" do
77
+ @store[key] = "value"
78
+ unaltered = "unaltered"
79
+ @store.get(key) { unaltered = "altered" }
80
+ unaltered.must_equal "unaltered"
81
+ end
82
+
83
+ it "stores #{key} values with #set" do
84
+ @store.set(key, "value")
85
+ @store[key].must_equal "value"
86
+ end
87
+
88
+ it "returns a list of commit history for the key" do
89
+ @store.log(key).wont_be_empty
90
+ end
91
+ end
92
+
93
+ end
@@ -0,0 +1,21 @@
1
+ require "bundler"
2
+ Bundler.setup
3
+
4
+ require 'minitest/autorun'
5
+ require 'minitest/pride'
6
+
7
+ $LOAD_PATH.unshift(File.dirname(__FILE__))
8
+ $LOAD_PATH.unshift(File.join(File.dirname(__FILE__), '..', 'lib'))
9
+
10
+ require 'gaga'
11
+
12
+ TMP_DIR = '/tmp/gaga_test'
13
+
14
+ def tmp_dir
15
+ TMP_DIR
16
+ end
17
+
18
+ def remove_tmpdir!(passed_dir = nil)
19
+ FileUtils.rm_rf(passed_dir || tmp_dir)
20
+ end
21
+
metadata ADDED
@@ -0,0 +1,74 @@
1
+ --- !ruby/object:Gem::Specification
2
+ name: gaga
3
+ version: !ruby/object:Gem::Version
4
+ prerelease:
5
+ version: 0.0.1
6
+ platform: ruby
7
+ authors:
8
+ - Matt Sears
9
+ autorequire:
10
+ bindir: bin
11
+ cert_chain: []
12
+
13
+ date: 2011-10-23 00:00:00 Z
14
+ dependencies:
15
+ - !ruby/object:Gem::Dependency
16
+ name: grit
17
+ prerelease: false
18
+ requirement: &id001 !ruby/object:Gem::Requirement
19
+ none: false
20
+ requirements:
21
+ - - ">="
22
+ - !ruby/object:Gem::Version
23
+ version: "0"
24
+ type: :runtime
25
+ version_requirements: *id001
26
+ description: Git as a key value store
27
+ email:
28
+ - matt@mattsears.com
29
+ executables: []
30
+
31
+ extensions: []
32
+
33
+ extra_rdoc_files: []
34
+
35
+ files:
36
+ - .gitignore
37
+ - Gemfile
38
+ - README.md
39
+ - Rakefile
40
+ - gaga.gemspec
41
+ - lib/gaga.rb
42
+ - lib/gaga/version.rb
43
+ - test/gaga_test.rb
44
+ - test/helper.rb
45
+ homepage: http://github.com/mattsears/gaga
46
+ licenses: []
47
+
48
+ post_install_message:
49
+ rdoc_options: []
50
+
51
+ require_paths:
52
+ - lib
53
+ required_ruby_version: !ruby/object:Gem::Requirement
54
+ none: false
55
+ requirements:
56
+ - - ">="
57
+ - !ruby/object:Gem::Version
58
+ version: "0"
59
+ required_rubygems_version: !ruby/object:Gem::Requirement
60
+ none: false
61
+ requirements:
62
+ - - ">="
63
+ - !ruby/object:Gem::Version
64
+ version: "0"
65
+ requirements: []
66
+
67
+ rubyforge_project: gaga
68
+ rubygems_version: 1.8.10
69
+ signing_key:
70
+ specification_version: 3
71
+ summary: Git as a key value store
72
+ test_files:
73
+ - test/gaga_test.rb
74
+ - test/helper.rb