change_agent 0.0.1 → 0.1.0

Sign up to get free protection for your applications and to get access to all the features.
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA1:
3
- metadata.gz: b77277230a8527bd86be1e4caa5500834b9aef1b
4
- data.tar.gz: 43b6adf0ecc54612020d28b9050970cd3279b4c6
3
+ metadata.gz: 637bed85fb1871ec16e91eccd2e654cacc3718b1
4
+ data.tar.gz: ac8434e78322e91231c2b8d446df980eb8472a94
5
5
  SHA512:
6
- metadata.gz: 308dc37a2ce06801aac82ee1bde0113ee23181bc40c3cf0093f72bf03b3c4c47a4f8672f53b4e032d25c596e217428b1590da20b530054ab35b36f2dbe73a3e0
7
- data.tar.gz: 26a15963bff1938fdd7418169dfd9bdf0b7e731ba17b527032800e87a209e12340850cb96fe8bd7969071d1b53f69670c7e770344919d997db11b3ea9f3ff721
6
+ metadata.gz: 6b12aa9d46caeefcd917f72bf9a2ca4f514dcb55044a5a559d24031c83e8747680879a1873578e1d5875faa31b4a19831726af3dc5f4f6de0dd6de287355252a
7
+ data.tar.gz: 652be6634b377ccba3b04bcadeafae8dc314e5fa3ccced55377e3e53dc6dd46a4de6eb5c8c97a9ca448ab98912020d8404ec8bd89f2a3a807f9f71b70e403355
data/.travis.yml ADDED
@@ -0,0 +1,4 @@
1
+ langauage: ruby
2
+ script: "script/cibuild"
3
+ sudo: false
4
+ cache: bundler
data/README.md CHANGED
@@ -2,6 +2,8 @@
2
2
 
3
3
  *A Git-backed key-value store, for tracking changes to documents and other files over time.*
4
4
 
5
+ [![Gem Version](https://badge.fury.io/rb/change_agent.svg)](http://badge.fury.io/rb/change_agent) [![Build Status](https://travis-ci.org/benbalter/change_agent.svg)](https://travis-ci.org/benbalter/change_agent)
6
+
5
7
  ### A git-backed key value store sounds like a terrible idea. Why would you do that?
6
8
 
7
9
  Let's say you're building a scraper to see when Members of Congress post press releases to their websites and to track how those press releases change over time. You could build a purpose-built application, store each revision in a database, and then build an interface to view all the known press releases and compare their history. Stop the insanity!
@@ -41,6 +43,29 @@ change_agent.get "foo/bar"
41
43
 
42
44
  It's really up to you, but you'll get performance and usability bumps the more you namespace. I'd recommend thinking about what you want the Git repo to look like when browse, and work backwards from there.
43
45
 
46
+ ### Cloning an existing repo / datastore
47
+
48
+ ```ruby
49
+ repo = "https://github.com/benbalter/some_repo"
50
+ directory = "data"
51
+ change_agent = ChangeAgent::Client.new(directory, repo)
52
+
53
+ change_agent.get("foo")
54
+ => "bar"
55
+ ```
56
+
57
+ ### Pushing and pulling
58
+
59
+ Ready to push your Git repo to a server? Assuming you've already got a remote set up, it's as simple as
60
+
61
+ ```ruby
62
+ # pull in the latest data
63
+ change_agent.git.pull
64
+
65
+ # push the data
66
+ change_agent.git.push
67
+ ```
68
+
44
69
  ## Project status
45
70
 
46
71
  Initial proof of concept
data/change_agent.gemspec CHANGED
@@ -17,7 +17,6 @@ Gem::Specification.new do |spec|
17
17
  spec.test_files = spec.files.grep(%r{^(test|spec|features)/})
18
18
  spec.require_paths = ["lib"]
19
19
 
20
- spec.add_dependency "git"
21
20
  spec.add_dependency "rugged"
22
21
  spec.add_development_dependency "bundler", "~> 1.6"
23
22
  spec.add_development_dependency "rake"
data/lib/change_agent.rb CHANGED
@@ -1,12 +1,13 @@
1
1
  require_relative "change_agent/version"
2
2
  require_relative "change_agent/document"
3
3
  require_relative "change_agent/client"
4
- require "git"
4
+ require "rugged"
5
+ require 'pathname'
5
6
 
6
7
  module ChangeAgent
7
8
 
8
- def self.init(directory=nil)
9
- Client.new(directory)
9
+ def self.init(directory=nil, remote=nil)
10
+ Client.new(directory, remote)
10
11
  end
11
12
 
12
13
  end
@@ -3,12 +3,17 @@ module ChangeAgent
3
3
 
4
4
  attr_accessor :directory
5
5
 
6
- def initialize(directory=nil)
6
+ def initialize(directory=nil, remote=nil)
7
7
  @directory = File.expand_path(directory || Dir.pwd)
8
+ @remote = remote
8
9
  end
9
10
 
10
- def git
11
- @git ||= Git.init directory
11
+ def repo
12
+ if @remote.nil?
13
+ @repo ||= Rugged::Repository.init_at directory
14
+ else
15
+ @repo ||= Rugged::Repository.clone_at @remote, directory
16
+ end
12
17
  end
13
18
 
14
19
  def set(key, value)
@@ -1,11 +1,12 @@
1
1
  module ChangeAgent
2
2
  class Document
3
3
 
4
- attr_accessor :key
5
4
  attr_writer :contents
5
+ attr_accessor :path
6
+ alias_method :key, :path
6
7
 
7
- def initialize(key, client_or_directory=nil)
8
- @key = key
8
+ def initialize(path, client_or_directory=nil)
9
+ @path = path
9
10
  if client_or_directory.class == ChangeAgent::Client
10
11
  @client = client_or_directory
11
12
  else
@@ -13,51 +14,58 @@ module ChangeAgent
13
14
  end
14
15
  end
15
16
 
16
- def git
17
- @client.git
18
- end
19
-
20
- # base dir for repo
21
- def base_dir
22
- @client.directory
23
- end
24
-
25
- # directory containing file
26
- def directory
27
- @directory ||= File.dirname(path)
28
- end
29
-
30
- def path
31
- File.expand_path key, base_dir
32
- end
33
-
34
- def exists?
35
- File.exists? path
17
+ def repo
18
+ @client.repo
36
19
  end
37
20
 
38
21
  def contents
39
- @contents ||= File.open(path).read
40
- rescue Errno::ENOENT
22
+ @contents ||= begin
23
+ tree = repo.head.target.tree
24
+ blob = repo.lookup tree.path(path)[:oid]
25
+ blob.content
26
+ end
27
+ rescue Rugged::ReferenceError, Rugged::TreeError
41
28
  nil
42
29
  end
43
30
 
44
31
  def write
45
- FileUtils.mkdir_p directory unless Dir.exists? directory
46
- File.write(path, contents)
47
- commit
48
- end
32
+ clean_path
33
+ oid = repo.write contents, :blob
34
+ repo.index.add(path: path, oid: oid, mode: 0100644)
49
35
 
50
- def commit
51
- git.add(path)
52
- git.commit "Updating #{path}"
36
+ Rugged::Commit.create repo,
37
+ message: "Updating #{path}",
38
+ parents: repo.empty? ? [] : [ repo.head.target ],
39
+ tree: repo.index.write_tree(repo),
40
+ update_ref: 'HEAD'
53
41
  end
54
42
 
55
- def delete
56
- git.remove(path)
43
+ def delete(file=path)
44
+ repo.index.remove(file)
45
+
46
+ Rugged::Commit.create repo,
47
+ message: "Removing #{path}",
48
+ parents: [repo.head.target],
49
+ tree: repo.index.write_tree(repo),
50
+ update_ref: 'HEAD'
57
51
  end
58
52
 
59
53
  def inspect
60
- "#<ChangeAgent::Document key=\"#{key}\">"
54
+ "#<ChangeAgent::Document path=\"#{path}\">"
55
+ end
56
+
57
+ private
58
+
59
+ def clean_path
60
+ return if repo.empty?
61
+ dirs = []
62
+ tree = repo.head.target.tree
63
+ path.split("/").each do |part|
64
+ file = dirs.push(part).join("/")
65
+ delete(file) if tree.path(file)
66
+ end
67
+ rescue Rugged::TreeError
68
+ nil
61
69
  end
62
70
  end
63
71
  end
@@ -1,3 +1,3 @@
1
1
  module ChangeAgent
2
- VERSION = "0.0.1"
2
+ VERSION = "0.1.0"
3
3
  end
data/script/bootstrap CHANGED
@@ -1 +1,3 @@
1
+ #!/bin/sh
2
+
1
3
  bundle install
data/script/cibuild CHANGED
@@ -1 +1,15 @@
1
+ #!/bin/sh
2
+
3
+ git config --get user.name > /dev/null
4
+ if [ $? -eq 1 ]; then
5
+ git config --global user.name "Your Name"
6
+ fi
7
+
8
+ git config --get user.email > /dev/null
9
+ if [ $? -eq 1 ]; then
10
+ git config --global user.email "you@example.com"
11
+ fi
12
+
1
13
  bundle exec rake test
14
+
15
+ rm -Rf tmp
data/script/console CHANGED
@@ -1 +1,3 @@
1
+ #!/bin/sh
2
+
1
3
  bundle exec rake console
@@ -7,53 +7,50 @@ class TestChangeAgentClient < Minitest::Test
7
7
  @client = ChangeAgent::Client.new(tempdir)
8
8
  end
9
9
 
10
+ def teardown
11
+ FileUtils.rm_rf tempdir
12
+ end
13
+
10
14
  should "set the directory on init" do
11
15
  assert_equal tempdir, @client.directory
12
16
  end
13
17
 
18
+ should "clone into existing repos" do
19
+ repo = "https://github.com/benbalter/change_agent"
20
+ agent = ChangeAgent::Client.new(tempdir, repo)
21
+ assert_equal repo, agent.repo.remotes.first.url
22
+ assert Dir.entries(tempdir).count > 5
23
+ end
24
+
14
25
  should "default to the pwd" do
15
26
  assert_equal Dir.pwd, ChangeAgent::Client.new.directory
16
27
  end
17
28
 
18
29
  should "init the git object" do
19
- assert_equal Git::Base, @client.git.class
20
- expected = File.expand_path ".git", tempdir
21
- assert_equal expected, @client.git.repo.path
30
+ assert_equal Rugged::Repository, @client.repo.class
31
+ assert_equal tempdir + "/.git/", @client.repo.path
22
32
  end
23
33
 
24
34
  should "store a value" do
25
35
  @client.set "foo", "bar"
26
- file = File.expand_path "foo", tempdir
27
- assert File.exists? file
28
- assert_equal "bar", File.open(file).read
36
+ tree = @client.repo.head.target.tree
37
+ blob = @client.repo.lookup tree["foo"][:oid]
38
+ assert_equal "bar", blob.content
29
39
  end
30
40
 
31
41
  should "store a namespaced value" do
32
42
  @client.set "foo/bar", "baz"
33
- file = File.expand_path "foo/bar", tempdir
34
- assert File.exists? file
35
- assert_equal "baz", File.open(file).read
43
+ tree = @client.repo.head.target.tree
44
+ blob = @client.repo.lookup tree.path("foo/bar")[:oid]
45
+ assert_equal "baz", blob.content
36
46
  end
37
47
 
38
48
  should "retrieve a file's value" do
39
- file = File.expand_path "foo", tempdir
40
- File.write file, "bar"
41
- assert_equal "bar", @client.get("foo")
42
- end
43
-
44
- should "retrive a namespaced file's value" do
45
- file = File.expand_path "foo/bar", tempdir
46
- FileUtils.mkdir_p File.dirname(file)
47
- File.write file, "baz"
48
- assert_equal "baz", @client.get("foo/bar")
49
- end
50
-
51
- should "round trip a value" do
52
49
  @client.set "foo", "bar"
53
50
  assert_equal "bar", @client.get("foo")
54
51
  end
55
52
 
56
- should "round trip a namespaced value" do
53
+ should "retrive a namespaced file's value" do
57
54
  @client.set "foo/bar", "baz"
58
55
  assert_equal "baz", @client.get("foo/bar")
59
56
  end
@@ -4,12 +4,17 @@ class TestChangeAgentDocument < Minitest::Test
4
4
 
5
5
  def setup
6
6
  init_tempdir
7
- @document = ChangeAgent::Document.new("foo", tempdir)
8
- @namespaced_document = ChangeAgent::Document.new("bar/foo", tempdir)
7
+ @client = ChangeAgent::Client.new tempdir
8
+ @document = ChangeAgent::Document.new("foo", @client)
9
+ @namespaced_document = ChangeAgent::Document.new("bar/foo", @client)
9
10
  end
10
11
 
11
- should "store the document key on init" do
12
- assert_equal "foo", @document.key
12
+ def teardown
13
+ FileUtils.rm_rf tempdir
14
+ end
15
+
16
+ should "store the document path on init" do
17
+ assert_equal "foo", @document.path
13
18
  end
14
19
 
15
20
  should "accept a client if passed" do
@@ -22,58 +27,43 @@ class TestChangeAgentDocument < Minitest::Test
22
27
  end
23
28
 
24
29
  should "expose the git client" do
25
- assert_equal Git::Base, @document.git.class
26
- end
27
-
28
- should "calcuate the base_dir" do
29
- assert_equal tempdir, @document.base_dir
30
- end
31
-
32
- should "calcuate the directory" do
33
- assert_equal tempdir, @document.directory
34
-
35
- expected = File.expand_path "bar", tempdir
36
- assert_equal expected, @namespaced_document.directory
37
- end
38
-
39
- should "know the file path" do
40
- expected = File.expand_path "foo", tempdir
41
- assert_equal expected, @document.path
42
-
43
- expected = File.expand_path "bar/foo", tempdir
44
- assert_equal expected, @namespaced_document.path
45
- end
46
-
47
- should "know if a file exists" do
48
- refute @document.exists?
49
- FileUtils.touch @document.path
50
- assert @document.exists?
30
+ assert_equal Rugged::Repository, @document.repo.class
51
31
  end
52
32
 
53
33
  should "read a file's contents" do
54
- File.write @document.path, "bar"
34
+ @document.contents = "bar"
35
+ @document.write
55
36
  assert_equal "bar", @document.contents
56
37
  end
57
38
 
58
39
  should "write a file's contents" do
59
40
  @document.contents = "bar"
60
- assert_equal "bar", @document.contents
61
41
  @document.write
62
- assert_equal "bar", File.open(@document.path).read
42
+ assert_equal "bar", @client.get("foo")
63
43
  end
64
44
 
65
45
  should "commit the document to the repo" do
66
46
  @document.contents = "bar"
67
47
  @document.write
68
- assert_equal 1, @document.git.log.count
69
- assert_equal "Updating #{@document.path}", @document.git.log.first.message
48
+ assert_equal "Updating #{@document.key}", @document.repo.last_commit.message
70
49
  end
71
50
 
72
51
  should "delete the document" do
73
52
  @document.contents = "bar"
74
53
  @document.write
75
- assert File.exists? @document.path
54
+ assert @client.get "foo"
76
55
  @document.delete
77
- refute File.exists? @document.path
56
+ refute @client.get "foo"
57
+ assert_equal "Removing #{@document.key}", @document.repo.last_commit.message
58
+ end
59
+
60
+ should "clobber conflicting namespace" do
61
+ @document.contents = "bar"
62
+ @document.write
63
+
64
+ doc = ChangeAgent::Document.new("foo/bar", @client)
65
+ doc.contents = "baz"
66
+ doc.write
67
+ assert_equal "baz", @client.get("foo/bar")
78
68
  end
79
69
  end
metadata CHANGED
@@ -1,29 +1,15 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: change_agent
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.0.1
4
+ version: 0.1.0
5
5
  platform: ruby
6
6
  authors:
7
7
  - Ben Balter
8
8
  autorequire:
9
9
  bindir: bin
10
10
  cert_chain: []
11
- date: 2014-11-26 00:00:00.000000000 Z
11
+ date: 2014-11-27 00:00:00.000000000 Z
12
12
  dependencies:
13
- - !ruby/object:Gem::Dependency
14
- name: git
15
- requirement: !ruby/object:Gem::Requirement
16
- requirements:
17
- - - ">="
18
- - !ruby/object:Gem::Version
19
- version: '0'
20
- type: :runtime
21
- prerelease: false
22
- version_requirements: !ruby/object:Gem::Requirement
23
- requirements:
24
- - - ">="
25
- - !ruby/object:Gem::Version
26
- version: '0'
27
13
  - !ruby/object:Gem::Dependency
28
14
  name: rugged
29
15
  requirement: !ruby/object:Gem::Requirement
@@ -102,6 +88,7 @@ extensions: []
102
88
  extra_rdoc_files: []
103
89
  files:
104
90
  - ".gitignore"
91
+ - ".travis.yml"
105
92
  - Gemfile
106
93
  - LICENSE.txt
107
94
  - README.md