boombera 0.1.2 → 0.2.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/Gemfile CHANGED
@@ -3,9 +3,9 @@ source "http://rubygems.org"
3
3
  gem 'couchrest', '~> 1.0.2'
4
4
 
5
5
  group :development do
6
- gem "rspec", "~> 2.3.0"
6
+ gem "rspec", "~> 2.6.0"
7
7
  gem "bundler", "~> 1.0.0"
8
- gem "jeweler", :git => "git://github.com/jwilger/jeweler.git"
8
+ gem "jeweler", "~> 1.6.1"
9
9
  gem "rcov", ">= 0"
10
10
  gem "reek", "~> 1.2.8"
11
11
  gem "rdoc", "~> 3.6.1"
@@ -1,12 +1,3 @@
1
- GIT
2
- remote: git://github.com/jwilger/jeweler.git
3
- revision: d7461cba474852ab16d31618e6465b56b00db49e
4
- specs:
5
- jeweler (1.6.0)
6
- bundler (~> 1.0.0)
7
- git (>= 1.2.5)
8
- rake
9
-
10
1
  GEM
11
2
  remote: http://rubygems.org/
12
3
  specs:
@@ -22,6 +13,10 @@ GEM
22
13
  rest-client (~> 1.6.1)
23
14
  diff-lcs (1.1.2)
24
15
  git (1.2.5)
16
+ jeweler (1.6.1)
17
+ bundler (~> 1.0.0)
18
+ git (>= 1.2.5)
19
+ rake
25
20
  json (1.5.1)
26
21
  mime-types (1.16)
27
22
  rake (0.9.0)
@@ -33,14 +28,14 @@ GEM
33
28
  sexp_processor (~> 3.0)
34
29
  rest-client (1.6.1)
35
30
  mime-types (>= 1.16)
36
- rspec (2.3.0)
37
- rspec-core (~> 2.3.0)
38
- rspec-expectations (~> 2.3.0)
39
- rspec-mocks (~> 2.3.0)
40
- rspec-core (2.3.1)
41
- rspec-expectations (2.3.0)
31
+ rspec (2.6.0)
32
+ rspec-core (~> 2.6.0)
33
+ rspec-expectations (~> 2.6.0)
34
+ rspec-mocks (~> 2.6.0)
35
+ rspec-core (2.6.3)
36
+ rspec-expectations (2.6.0)
42
37
  diff-lcs (~> 1.1.2)
43
- rspec-mocks (2.3.0)
38
+ rspec-mocks (2.6.0)
44
39
  ruby2ruby (1.2.5)
45
40
  ruby_parser (~> 2.0)
46
41
  sexp_processor (~> 3.0)
@@ -58,8 +53,8 @@ DEPENDENCIES
58
53
  autotest-growl (~> 0.2.9)
59
54
  bundler (~> 1.0.0)
60
55
  couchrest (~> 1.0.2)
61
- jeweler!
56
+ jeweler (~> 1.6.1)
62
57
  rcov
63
58
  rdoc (~> 3.6.1)
64
59
  reek (~> 1.2.8)
65
- rspec (~> 2.3.0)
60
+ rspec (~> 2.6.0)
data/VERSION CHANGED
@@ -1 +1 @@
1
- 0.1.2
1
+ 0.2.0
@@ -5,7 +5,7 @@
5
5
 
6
6
  Gem::Specification.new do |s|
7
7
  s.name = %q{boombera}
8
- s.version = "0.1.2"
8
+ s.version = "0.2.0"
9
9
 
10
10
  s.required_rubygems_version = Gem::Requirement.new(">= 0") if s.respond_to? :required_rubygems_version=
11
11
  s.authors = ["John Wilger"]
@@ -47,9 +47,9 @@ Gem::Specification.new do |s|
47
47
 
48
48
  if Gem::Version.new(Gem::VERSION) >= Gem::Version.new('1.2.0') then
49
49
  s.add_runtime_dependency(%q<couchrest>, ["~> 1.0.2"])
50
- s.add_development_dependency(%q<rspec>, ["~> 2.3.0"])
50
+ s.add_development_dependency(%q<rspec>, ["~> 2.6.0"])
51
51
  s.add_development_dependency(%q<bundler>, ["~> 1.0.0"])
52
- s.add_development_dependency(%q<jeweler>, [">= 0"])
52
+ s.add_development_dependency(%q<jeweler>, ["~> 1.6.1"])
53
53
  s.add_development_dependency(%q<rcov>, [">= 0"])
54
54
  s.add_development_dependency(%q<reek>, ["~> 1.2.8"])
55
55
  s.add_development_dependency(%q<rdoc>, ["~> 3.6.1"])
@@ -58,9 +58,9 @@ Gem::Specification.new do |s|
58
58
  s.add_development_dependency(%q<autotest-fsevent>, ["~> 0.2.5"])
59
59
  else
60
60
  s.add_dependency(%q<couchrest>, ["~> 1.0.2"])
61
- s.add_dependency(%q<rspec>, ["~> 2.3.0"])
61
+ s.add_dependency(%q<rspec>, ["~> 2.6.0"])
62
62
  s.add_dependency(%q<bundler>, ["~> 1.0.0"])
63
- s.add_dependency(%q<jeweler>, [">= 0"])
63
+ s.add_dependency(%q<jeweler>, ["~> 1.6.1"])
64
64
  s.add_dependency(%q<rcov>, [">= 0"])
65
65
  s.add_dependency(%q<reek>, ["~> 1.2.8"])
66
66
  s.add_dependency(%q<rdoc>, ["~> 3.6.1"])
@@ -70,9 +70,9 @@ Gem::Specification.new do |s|
70
70
  end
71
71
  else
72
72
  s.add_dependency(%q<couchrest>, ["~> 1.0.2"])
73
- s.add_dependency(%q<rspec>, ["~> 2.3.0"])
73
+ s.add_dependency(%q<rspec>, ["~> 2.6.0"])
74
74
  s.add_dependency(%q<bundler>, ["~> 1.0.0"])
75
- s.add_dependency(%q<jeweler>, [">= 0"])
75
+ s.add_dependency(%q<jeweler>, ["~> 1.6.1"])
76
76
  s.add_dependency(%q<rcov>, [">= 0"])
77
77
  s.add_dependency(%q<reek>, ["~> 1.2.8"])
78
78
  s.add_dependency(%q<rdoc>, ["~> 3.6.1"])
@@ -11,6 +11,7 @@ require 'boombera/information'
11
11
 
12
12
  class Boombera
13
13
  VersionMismatch = Class.new(StandardError)
14
+ InvalidMapping = Class.new(RuntimeError)
14
15
 
15
16
  extend Boombera::Information
16
17
 
@@ -22,13 +23,19 @@ class Boombera
22
23
  end
23
24
 
24
25
  def put(path, body)
25
- content_item = get(path) and content_item.body = body
26
+ content_item = get(path, :resolve_map => false) and content_item.body = body
26
27
  content_item ||= ContentItem.new(path, body, db)
27
28
  content_item.save
28
29
  end
29
30
 
30
- def get(path)
31
- ContentItem.get(path, db)
31
+ def get(path, options = {})
32
+ ContentItem.get(path, db, options)
33
+ end
34
+
35
+ def map(path, source_path)
36
+ content_map = get(path, :resolve_map => false) || ContentItem.new(path, nil, db)
37
+ content_map.map_to source_path
38
+ content_map.save
32
39
  end
33
40
 
34
41
  private
@@ -1,15 +1,20 @@
1
1
  class Boombera::ContentItem < CouchRest::Document
2
2
  class << self
3
- def get(path, database)
3
+ def get(path, database, options = {})
4
4
  rows = database.view('boombera/content_map', :key => path)['rows']
5
5
  return nil if rows.empty?
6
- id = rows.first['id']
7
- new(database.get(id))
6
+ match = rows.first
7
+ maps_to = match['value']
8
+ if maps_to == path || options[:resolve_map] == false
9
+ new(database.get(match['id']))
10
+ else
11
+ get(maps_to, database)
12
+ end
8
13
  end
9
14
  end
10
15
 
11
16
  attr_accessor :body
12
- attr_reader :path
17
+ attr_reader :path, :maps_to
13
18
 
14
19
  def initialize(doc_or_path, body = nil, database = nil)
15
20
  case doc_or_path
@@ -24,6 +29,22 @@ class Boombera::ContentItem < CouchRest::Document
24
29
  end
25
30
  end
26
31
 
32
+ def map_to(source_path)
33
+ rows = @database.view('boombera/content_map', :key => source_path)['rows']
34
+ if rows.empty?
35
+ raise Boombera::InvalidMapping,
36
+ "Tried to map #{path} to #{source_path}, but #{source_path} doesn't exist."
37
+ else
38
+ self.body = nil
39
+ self[:maps_to] = source_path
40
+ end
41
+ end
42
+
43
+ def referenced_by
44
+ rows = @database.view('boombera/map_references', :key => path)['rows']
45
+ rows.map{ |r| r['value'] }.sort
46
+ end
47
+
27
48
  # :nodoc:
28
49
  def path
29
50
  self[:path]
@@ -37,5 +58,11 @@ class Boombera::ContentItem < CouchRest::Document
37
58
  # :nodoc:
38
59
  def body=(new_body)
39
60
  self[:body] = new_body
61
+ self[:maps_to] = path unless new_body.nil?
62
+ end
63
+
64
+ # :nodoc:
65
+ def maps_to
66
+ self[:maps_to]
40
67
  end
41
68
  end
@@ -26,7 +26,20 @@ module Boombera::Information
26
26
  'map' => <<-EOF
27
27
  function(doc) {
28
28
  if (doc['path']) {
29
- emit(doc.path, doc.path);
29
+ if (doc['maps_to']) {
30
+ emit(doc.path, doc.maps_to);
31
+ } else {
32
+ emit(doc.path, doc.path);
33
+ }
34
+ }
35
+ }
36
+ EOF
37
+ },
38
+ 'map_references' => {
39
+ 'map' => <<-EOF
40
+ function(doc) {
41
+ if(doc['maps_to'] && doc.maps_to != doc.path) {
42
+ emit(doc.maps_to, doc.path);
30
43
  }
31
44
  }
32
45
  EOF
@@ -81,6 +81,52 @@ describe 'The Boombera library:' do
81
81
  document['path'].should == '/foo'
82
82
  document['body'].should == 'the new content'
83
83
  end
84
+
85
+ it 'turns a pointer into a content item' do
86
+ boombera.put('/foo', 'foo bar baz')
87
+ boombera.map('/bar', '/foo')
88
+ boombera.put('/bar', 'the new content')
89
+ results = db.view('boombera/content_map', :key => '/bar')['rows']
90
+ results.length.should == 1
91
+ document = db.get(results.first['id'])
92
+ document['path'].should == '/bar'
93
+ document['body'].should == 'the new content'
94
+ document['points_to'].should be_nil
95
+ end
96
+ end
97
+
98
+ describe 'mapping content aliases' do
99
+ it 'creates a pointer from a path to another path' do
100
+ boombera.put('/foo', 'foo bar baz')
101
+ boombera.map('/bar', '/foo')
102
+ results = db.view('boombera/content_map', :key => '/bar')['rows']
103
+ results.length.should == 1
104
+ map_item = results.first
105
+ map_item['value'].should == '/foo'
106
+ end
107
+
108
+ it 'updates a pointer from a path to another path' do
109
+ boombera.put('/foo', 'foo bar baz')
110
+ boombera.put('/spam', 'ham spam can')
111
+ boombera.map('/bar', '/foo')
112
+ boombera.map('/bar', '/spam')
113
+ results = db.view('boombera/content_map', :key => '/bar')['rows']
114
+ results.length.should == 1
115
+ map_item = results.first
116
+ map_item['value'].should == '/spam'
117
+ end
118
+
119
+ it 'turns a content item into a pointer' do
120
+ boombera.put('/foo', 'foo bar baz')
121
+ boombera.put('/bar', 'some old bar content')
122
+ boombera.map('/bar', '/foo')
123
+ results = db.view('boombera/content_map', :key => '/bar')['rows']
124
+ results.length.should == 1
125
+ map_item = results.first
126
+ map_item['value'].should == '/foo'
127
+ doc = db.get(map_item['id'])
128
+ doc['body'].should be_nil
129
+ end
84
130
  end
85
131
 
86
132
  describe 'getting content from the database' do
@@ -94,6 +140,14 @@ describe 'The Boombera library:' do
94
140
  result.path.should == '/index'
95
141
  result.body.should == 'Hello, World!'
96
142
  end
143
+
144
+ it 'gives you the resulting ContentItem when a pointer is requested' do
145
+ boombera.put('/foo', 'some content')
146
+ boombera.map('/bar', '/foo')
147
+ result = boombera.get('/bar')
148
+ result.path.should == '/foo'
149
+ result.body.should == 'some content'
150
+ end
97
151
  end
98
152
 
99
153
  describe 'working with ContentItem' do
@@ -104,5 +158,16 @@ describe 'The Boombera library:' do
104
158
  content.save
105
159
  boombera.get('/foo').body.should == 'new content'
106
160
  end
161
+
162
+ it 'knows which pointers reference it' do
163
+ boombera.put('/foo', 'some content')
164
+ boombera.map('/zurg', '/foo')
165
+ boombera.map('/bar', '/foo')
166
+ boombera.map('/bar/baz', '/foo')
167
+ boombera.put('/spam', 'spam content')
168
+ boombera.map('/spam2', '/spam')
169
+ content = boombera.get('/foo')
170
+ content.referenced_by.should == ['/bar', '/bar/baz', '/zurg']
171
+ end
107
172
  end
108
173
  end
@@ -4,7 +4,7 @@ describe Boombera::ContentItem do
4
4
  describe '.get' do
5
5
  context 'with an existing content item' do
6
6
  it 'returns a ContentItem instance for the found document' do
7
- view_result = {'rows' => [{'id' => '123'}]}
7
+ view_result = {'rows' => [{'id' => '123', 'value' => '/foo'}]}
8
8
  db = mock(CouchRest::Database)
9
9
  db.should_receive(:view) \
10
10
  .with('boombera/content_map', :key => '/foo') \
@@ -17,6 +17,51 @@ describe Boombera::ContentItem do
17
17
  result.body.should == 'bar'
18
18
  end
19
19
  end
20
+
21
+ context 'with a non-existant content item' do
22
+ it 'returns nil' do
23
+ view_result = {'rows' => []}
24
+ db = mock(CouchRest::Database)
25
+ db.should_receive(:view) \
26
+ .with('boombera/content_map', :key => '/foo') \
27
+ .and_return(view_result)
28
+ Boombera::ContentItem.get('/foo', db).should == nil
29
+ end
30
+ end
31
+
32
+ context 'with a path that maps to another content item' do
33
+ it 'returns the mapped content item' do
34
+ map_view_result = {'rows' => [{'id' => '123', 'value' => '/bar'}]}
35
+ content_view_result = {'rows' => [{'id' => '456', 'value' => '/bar'}]}
36
+ db = mock(CouchRest::Database)
37
+ db.should_receive(:view) \
38
+ .with('boombera/content_map', :key => '/foo') \
39
+ .and_return(map_view_result)
40
+ db.should_receive(:view) \
41
+ .with('boombera/content_map', :key => '/bar') \
42
+ .and_return(content_view_result)
43
+ db.should_receive(:get) \
44
+ .with('456') \
45
+ .and_return(CouchRest::Document.new('path' => '/bar', 'body' => 'bar'))
46
+ result = Boombera::ContentItem.get('/foo', db)
47
+ result.path.should == '/bar'
48
+ result.body.should == 'bar'
49
+ end
50
+
51
+ it 'returns the pointer content item when passed the :resolve_map option as false' do
52
+ map_view_result = {'rows' => [{'id' => '123', 'value' => '/bar'}]}
53
+ db = mock(CouchRest::Database)
54
+ db.should_receive(:view) \
55
+ .with('boombera/content_map', :key => '/foo') \
56
+ .and_return(map_view_result)
57
+ db.should_receive(:get) \
58
+ .with('123') \
59
+ .and_return(CouchRest::Document.new('path' => '/foo', 'maps_to' => '/bar'))
60
+ result = Boombera::ContentItem.get('/foo', db, :resolve_map => false)
61
+ result.path.should == '/foo'
62
+ result.maps_to.should == '/bar'
63
+ end
64
+ end
20
65
  end
21
66
 
22
67
  describe '.new' do
@@ -52,10 +97,58 @@ describe Boombera::ContentItem do
52
97
  end
53
98
 
54
99
  describe '#body=' do
100
+ let(:db) { stub(CouchRest::Database) }
101
+ let(:content) { Boombera::ContentItem.new('/foo', 'not bar', db) }
102
+
55
103
  it 'overwrites the current contents of the document body' do
56
- content = Boombera::ContentItem.new('/foo', 'not bar')
57
104
  content.body = 'bar'
58
105
  content.body.should == 'bar'
59
106
  end
107
+
108
+ it 'sets the maps_to attribute equal to the path attribute if argument is not nil' do
109
+ db.stub!(:view => {'rows' => [{'value' => '/foo'}]})
110
+ content.map_to '/bar'
111
+ content.body = 'something'
112
+ content.maps_to.should == '/foo'
113
+ end
114
+
115
+ it 'does not change the maps_to attribute if the argument is nil' do
116
+ db.stub!(:view => {'rows' => [{'value' => '/foo'}]})
117
+ content.map_to '/bar'
118
+ content.body = nil
119
+ content.maps_to.should == '/bar'
120
+ end
121
+ end
122
+
123
+ describe '#map_to' do
124
+ context 'the source content item does not exist' do
125
+ it 'raises an InvalidMapping exception' do
126
+ db = stub(CouchRest::Database)
127
+ db.stub!(:view => {'rows' => []})
128
+ content = Boombera::ContentItem.new('/bar', nil, db)
129
+ lambda { content.map_to('/foo') }.should \
130
+ raise_error(Boombera::InvalidMapping, "Tried to map /bar to /foo, but /foo doesn't exist.")
131
+ end
132
+ end
133
+
134
+ context 'the source content item is a normal content item' do
135
+ let(:db) { stub(CouchRest::Database) }
136
+ let(:content) { Boombera::ContentItem.new('/bar', 'foo bar', db) }
137
+
138
+ before(:each) do
139
+ db.should_receive(:view) \
140
+ .with('boombera/content_map', :key => '/foo') \
141
+ .and_return({'rows' => [{'value' => '/foo'}]})
142
+ content.map_to '/foo'
143
+ end
144
+
145
+ it 'sets the maps_to attribute to the path of the source content' do
146
+ content.maps_to.should == '/foo'
147
+ end
148
+
149
+ it 'sets the body attribute to nil' do
150
+ content.body.should be_nil
151
+ end
152
+ end
60
153
  end
61
154
  end
@@ -7,6 +7,9 @@ describe Boombera do
7
7
  db
8
8
  end
9
9
 
10
+ let(:content_item) { stub(Boombera::ContentItem) }
11
+ let(:boombera) { Boombera.new('boombera_test') }
12
+
10
13
  before(:each) do
11
14
  Boombera.stub!(:version => '1.2.3')
12
15
  Boombera.stub!(:database_version => '1.2.3')
@@ -35,30 +38,25 @@ describe Boombera do
35
38
  end
36
39
 
37
40
  describe '#put' do
38
- let(:content_item) { mock(Boombera::ContentItem) }
39
- let(:content_item_save_expectations) do
40
- lambda {
41
- content_item.should_receive(:save).and_return(true)
42
- boombera = Boombera.new('boombera_test')
43
- boombera.put('/foo', 'bar').should == true
44
- }
45
- end
46
-
47
41
  context "to an existing path" do
48
42
  it 'updates and saves the existing content item' do
49
- Boombera::ContentItem.should_receive(:get).with('/foo', db).and_return(content_item)
43
+ Boombera::ContentItem.should_receive(:get) \
44
+ .with('/foo', db, :resolve_map => false) \
45
+ .and_return(content_item)
50
46
  content_item.should_receive(:body=).with('bar')
51
- content_item_save_expectations.call
47
+ content_item.should_receive(:save).and_return(true)
48
+ boombera.put('/foo', 'bar').should == true
52
49
  end
53
50
  end
54
51
 
55
52
  context "to a new path" do
56
- it 'creates and saves the existing content item' do
53
+ it 'creates and saves the content item' do
57
54
  Boombera::ContentItem.stub!(:get => nil)
58
55
  Boombera::ContentItem.should_receive(:new) \
59
56
  .with('/foo', 'bar', db) \
60
57
  .and_return(content_item)
61
- content_item_save_expectations.call
58
+ content_item.should_receive(:save).and_return(true)
59
+ boombera.put('/foo', 'bar').should == true
62
60
  end
63
61
  end
64
62
  end
@@ -66,12 +64,52 @@ describe Boombera do
66
64
  describe '#get' do
67
65
  it 'gets the content item at the specified path from the current database' do
68
66
  db.as_null_object
69
- Boombera::ContentItem.should_receive(:get).with('/foo', db)
70
- boombera = Boombera.new('boombera_test')
67
+ Boombera::ContentItem.should_receive(:get).with('/foo', db, {})
71
68
  boombera.get('/foo')
72
69
  end
73
70
  end
74
71
 
72
+ describe '#map' do
73
+ context 'to a new path' do
74
+ before(:each) do
75
+ Boombera::ContentItem.stub!(:get)
76
+ Boombera::ContentItem.should_receive(:new).with('/bar', nil, db).and_return(content_item)
77
+ end
78
+
79
+ it 'creates and saves ContentItem as pointer' do
80
+ content_item.should_receive(:map_to).with('/foo')
81
+ content_item.should_receive(:save).and_return(true)
82
+ boombera.map('/bar', '/foo').should == true
83
+ end
84
+
85
+ it 'raises an InvalidMapping exception if the source document does not exist' do
86
+ content_item.stub!(:map_to).and_raise(Boombera::InvalidMapping)
87
+ lambda { boombera.map('/bar', '/foo') }.should \
88
+ raise_error(Boombera::InvalidMapping)
89
+ end
90
+ end
91
+
92
+ context 'to an existing path' do
93
+ before(:each) do
94
+ Boombera::ContentItem.should_receive(:get) \
95
+ .with('/bar', db, :resolve_map => false) \
96
+ .and_return(content_item)
97
+ end
98
+
99
+ it 'updates ContentItem as pointer' do
100
+ content_item.should_receive(:map_to).with('/foo')
101
+ content_item.should_receive(:save).and_return(true)
102
+ boombera.map('/bar', '/foo').should == true
103
+ end
104
+
105
+ it 'raises an InvalidMapping exception if the source document does not exist' do
106
+ content_item.stub!(:map_to).and_raise(Boombera::InvalidMapping)
107
+ lambda { boombera.map('/bar', '/foo') }.should \
108
+ raise_error(Boombera::InvalidMapping)
109
+ end
110
+ end
111
+ end
112
+
75
113
  describe '.install_design_doc!' do
76
114
  context 'when the design doc does not yet exist' do
77
115
  it 'creates the design doc on the specified database' do
metadata CHANGED
@@ -2,7 +2,7 @@
2
2
  name: boombera
3
3
  version: !ruby/object:Gem::Version
4
4
  prerelease:
5
- version: 0.1.2
5
+ version: 0.2.0
6
6
  platform: ruby
7
7
  authors:
8
8
  - John Wilger
@@ -31,7 +31,7 @@ dependencies:
31
31
  requirements:
32
32
  - - ~>
33
33
  - !ruby/object:Gem::Version
34
- version: 2.3.0
34
+ version: 2.6.0
35
35
  type: :development
36
36
  prerelease: false
37
37
  version_requirements: *id002
@@ -51,9 +51,9 @@ dependencies:
51
51
  requirement: &id004 !ruby/object:Gem::Requirement
52
52
  none: false
53
53
  requirements:
54
- - - ">="
54
+ - - ~>
55
55
  - !ruby/object:Gem::Version
56
- version: "0"
56
+ version: 1.6.1
57
57
  type: :development
58
58
  prerelease: false
59
59
  version_requirements: *id004
@@ -165,7 +165,7 @@ required_ruby_version: !ruby/object:Gem::Requirement
165
165
  requirements:
166
166
  - - ">="
167
167
  - !ruby/object:Gem::Version
168
- hash: 2330631431022327628
168
+ hash: -941585902333991149
169
169
  segments:
170
170
  - 0
171
171
  version: "0"