flockdb 0.3.2 → 0.4.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/README.md CHANGED
@@ -47,9 +47,10 @@ The client supports a rich query api to build up complex set operations:
47
47
  CONTRIBUTORS
48
48
  ------------
49
49
 
50
- Nick Kallen
51
- Matt Freels
52
- Rael Dornfest
50
+ Nick Kallen
51
+ Matt Freels
52
+ Rael Dornfest
53
+ Tina Huang
53
54
 
54
55
 
55
56
  LICENSE
data/VERSION CHANGED
@@ -1 +1 @@
1
- 0.3.2
1
+ 0.4.0
@@ -5,11 +5,11 @@
5
5
 
6
6
  Gem::Specification.new do |s|
7
7
  s.name = %q{flockdb}
8
- s.version = "0.3.2"
8
+ s.version = "0.4.0"
9
9
 
10
10
  s.required_rubygems_version = Gem::Requirement.new(">= 0") if s.respond_to? :required_rubygems_version=
11
11
  s.authors = ["Matt Freels", "Rael Dornfest", "Nick Kallen"]
12
- s.date = %q{2010-04-29}
12
+ s.date = %q{2010-05-11}
13
13
  s.description = %q{Get your flock on in Ruby.}
14
14
  s.email = %q{freels@twitter.com}
15
15
  s.extra_rdoc_files = [
@@ -41,8 +41,11 @@ Gem::Specification.new do |s|
41
41
  "lib/flock/thrift/flock_types.rb",
42
42
  "lib/flock/thrift/shards.rb",
43
43
  "lib/flockdb.rb",
44
+ "spec/execute_operations_spec.rb",
44
45
  "spec/flock_spec.rb",
45
46
  "spec/mock_service_spec.rb",
47
+ "spec/query_term_spec.rb",
48
+ "spec/simple_operation_spec.rb",
46
49
  "spec/spec.opts",
47
50
  "spec/spec_helper.rb"
48
51
  ]
@@ -52,8 +55,11 @@ Gem::Specification.new do |s|
52
55
  s.rubygems_version = %q{1.3.6}
53
56
  s.summary = %q{Ruby Flock client}
54
57
  s.test_files = [
55
- "spec/flock_spec.rb",
58
+ "spec/execute_operations_spec.rb",
59
+ "spec/flock_spec.rb",
56
60
  "spec/mock_service_spec.rb",
61
+ "spec/query_term_spec.rb",
62
+ "spec/simple_operation_spec.rb",
57
63
  "spec/spec_helper.rb"
58
64
  ]
59
65
 
@@ -30,6 +30,12 @@ module Flock
30
30
  end
31
31
  end
32
32
 
33
+ class UnknownStateError < FlockException
34
+ def initialize(state)
35
+ super("Unable to look up id for state #{state.inspect}. Valid states are #{ Flock::Client::STATES.keys.sort.map{|s| s.inspect }.join(', ') }")
36
+ end
37
+ end
38
+
33
39
  class << self
34
40
  attr_accessor :default_service_class, :graphs
35
41
 
@@ -1,5 +1,10 @@
1
1
  class Flock::Client
2
2
 
3
+ # symbol => state_id map
4
+ STATES = Flock::Edges::EdgeState::VALUE_MAP.inject({}) do |states, (id, name)|
5
+ states.update name.downcase.to_sym => id
6
+ end.freeze
7
+
3
8
  attr_accessor :graphs
4
9
  attr_reader :service
5
10
 
@@ -49,7 +54,7 @@ class Flock::Client
49
54
  end
50
55
 
51
56
  def contains(*query)
52
- query = _query_args(query)
57
+ query = _query_args(query)[0, 3]
53
58
  _cache :contains, query do
54
59
  service.contains(*query)
55
60
  end
@@ -68,14 +73,17 @@ class Flock::Client
68
73
  def update(method, source_id, graph, destination_id, priority = Flock::Priority::High)
69
74
  _cache_clear
70
75
  ops = current_transaction || Flock::ExecuteOperations.new(@service, priority)
71
- ops.send(method, *_query_args([source_id, graph, destination_id]))
76
+ ops.send(method, *_query_args([source_id, graph, destination_id])[0, 3])
72
77
  ops.apply unless in_transaction?
73
78
  end
74
79
 
75
- [:add, :remove, :archive, :unarchive].each do |method|
80
+ Flock::Edges::ExecuteOperationType::VALUE_MAP.values.each do |method|
81
+ method = method.downcase
76
82
  class_eval "def #{method}(*args); update(#{method.inspect}, *args) end", __FILE__, __LINE__
77
83
  end
78
84
 
85
+ alias unarchive add
86
+
79
87
  def transaction(priority = Flock::Priority::High, &block)
80
88
  new_transaction = !in_transaction?
81
89
 
@@ -113,9 +121,19 @@ class Flock::Client
113
121
  end
114
122
  end
115
123
 
124
+ def _lookup_states(states)
125
+ states = states.flatten.compact.map do |s|
126
+ if s.is_a? Integer
127
+ s
128
+ else
129
+ STATES[s] or raise UnknownStateError.new(s)
130
+ end
131
+ end
132
+ end
133
+
116
134
  def _query_args(args)
117
- source, graph, destination = *((args.length == 1) ? args.first : args)
118
- [_node_arg(source), _lookup_graph(graph), _node_arg(destination)]
135
+ source, graph, destination, *states = *((args.length == 1) ? args.first : args)
136
+ [_node_arg(source), _lookup_graph(graph), _node_arg(destination), _lookup_states(states)]
119
137
  end
120
138
 
121
139
  def _node_arg(node)
@@ -5,7 +5,8 @@ module Flock
5
5
  EXEC_OPS = {
6
6
  Edges::ExecuteOperationType::Add => :add,
7
7
  Edges::ExecuteOperationType::Remove => :remove,
8
- Edges::ExecuteOperationType::Archive => :archive
8
+ Edges::ExecuteOperationType::Archive => :archive,
9
+ Edges::ExecuteOperationType::Negate => :negate
9
10
  }
10
11
 
11
12
  attr_accessor :timeout, :fixtures
@@ -51,7 +52,7 @@ module Flock
51
52
  end
52
53
 
53
54
  def contains(source, graph, dest)
54
- forward_edges[graph][:normal][source].include?(dest)
55
+ forward_edges[graph][Edges::EdgeState::Positive][source].include?(dest)
55
56
  end
56
57
 
57
58
  def count(select_operations)
@@ -67,7 +68,8 @@ module Flock
67
68
  when Edges::SelectOperationType::SimpleQuery
68
69
  term = select_operation.term
69
70
  source = term.is_forward ? forward_edges : backward_edges
70
- data = source[term.graph_id][:normal][term.source_id]
71
+ states = term.state_ids || [Edges::EdgeState::Positive]
72
+ data = source[term.graph_id].inject([]) {|r, (s, e)| states.include?(s) ? r.concat(e[term.source_id]) : r }.uniq
71
73
  stack.push(term.destination_ids ? (term.destination_ids.unpack('Q*') & data) : data)
72
74
  when Edges::SelectOperationType::Intersection
73
75
  stack.push(stack.pop & stack.pop)
@@ -158,39 +160,36 @@ module Flock
158
160
  end.compact
159
161
  end
160
162
 
161
- def unarchive_node(source, graph, dest)
162
- remove_node(source, graph, dest, :archived).tap do |deleted|
163
- deleted.each {|d| add_edge(source, graph, dest, :normal) }
164
- end
165
- end
163
+ def color_node(source, graph, dest, dest_state)
164
+ raise ArgumentError unless Edges::EdgeState::VALUE_MAP.keys.include? dest_state
166
165
 
167
- # actual graph helpers
166
+ source_states = (Edges::EdgeState::VALUE_MAP.keys - [dest_state])
168
167
 
169
- def add(source, graph, dest)
170
168
  if source.nil? or dest.nil?
171
- unarchive_node(source, graph, dest)
169
+ source_states.each do |source_state|
170
+ remove_node(source, graph, dest, source_state).tap do |deleted|
171
+ deleted.each {|source, graph, dest| add_edge(source, graph, dest, dest_state) }
172
+ end
173
+ end
174
+
172
175
  else
173
176
  sources, dests = Array(source), Array(dest)
174
177
 
175
178
  sources.each do |s|
176
179
  dests.each do |d|
177
- add_edge(s, graph, d, :normal)
178
- remove_edge(s, graph, d, :archived)
180
+ add_edge(s, graph, d, dest_state)
181
+ source_states.each { |state| remove_edge(s, graph, d, state) }
179
182
  end
180
183
  end
181
184
  end
182
185
  end
183
186
 
184
- def remove(source, graph, dest)
185
- remove_node(source, graph, dest, :archived)
186
- remove_node(source, graph, dest, :normal)
187
- end
187
+ # must map manually since execute operations do not line up with
188
+ # their actual colors
189
+ op_color_map = {:add => 0, :remove => 1, :archive => 2, :negate => 3}
188
190
 
189
- def archive(source, graph, dest)
190
- remove_node(source, graph, dest, :normal).tap do |deleted|
191
- p deleted
192
- deleted.each {|source, graph, dest| add_edge(source, graph, dest, :archived) }
193
- end
191
+ op_color_map.each do |op, color|
192
+ class_eval "def #{op}(s, g, d); color_node(s, g, d, #{color}) end", __FILE__, __LINE__
194
193
  end
195
194
  end
196
195
  end
@@ -11,4 +11,4 @@ module Flock
11
11
  op
12
12
  end
13
13
  end
14
- end
14
+ end
@@ -4,19 +4,9 @@ module Flock
4
4
  @service, @operations, @priority = service, [], priority
5
5
  end
6
6
 
7
- def add(source_id, graph_id, destination_id)
8
- @operations << ExecuteOperation.new(Edges::ExecuteOperationType::Add, [source_id, graph_id, destination_id])
9
- self
10
- end
11
-
12
- def remove(source_id, graph_id, destination_id)
13
- @operations << ExecuteOperation.new(Edges::ExecuteOperationType::Remove, [source_id, graph_id, destination_id])
14
- self
15
- end
16
-
17
- def archive(source_id, graph_id, destination_id)
18
- @operations << ExecuteOperation.new(Edges::ExecuteOperationType::Archive, [source_id, graph_id, destination_id])
19
- self
7
+ Flock::Edges::ExecuteOperationType::VALUE_MAP.each do |op_id, op|
8
+ op = op.downcase
9
+ class_eval "def #{op}(s, g, d); @operations << ExecuteOperation.new(#{op_id}, [s, g, d]); self end", __FILE__, __LINE__
20
10
  end
21
11
 
22
12
  def apply
@@ -29,7 +19,5 @@ module Flock
29
19
  operations.priority = @priority
30
20
  operations
31
21
  end
32
-
33
- alias_method :unarchive, :add
34
22
  end
35
23
  end
@@ -1,25 +1,37 @@
1
1
  module Flock
2
2
  class QueryTerm
3
+ attr_accessor :source, :graph, :destination, :states
4
+
3
5
  def initialize(query)
4
- @query = query
6
+ case query.size
7
+ when 3, 4
8
+ @source, @graph, @destination, @states = *query
9
+ else
10
+ raise ArgumentError
11
+ end
5
12
  end
6
13
 
7
- def to_thrift
8
- raise ArgumentError unless @query.size == 3
14
+ def forward?
15
+ @source.is_a? Numeric
16
+ end
9
17
 
18
+ def to_thrift
10
19
  term = Edges::QueryTerm.new
11
- case @query.first
12
- when Numeric
13
- term.source_id = @query.first
14
- term.destination_ids = Array(@query.last).pack("Q*") if @query.last
15
- term.is_forward = true
16
- else
17
- term.source_id = @query.last
18
- term.destination_ids = Array(@query.first).pack("Q*") if @query.first
19
- term.is_forward = false
20
- end
21
- term.graph_id = @query[1]
20
+ term.graph_id = @graph
21
+ term.state_ids = @states unless @states.nil? or @states.empty?
22
+ term.is_forward = forward?
23
+
24
+ source, destination =
25
+ if term.is_forward
26
+ [@source, @destination]
27
+ else
28
+ [@destination, @source]
29
+ end
30
+
31
+ term.source_id = source
32
+ term.destination_ids = Array(destination).pack("Q*") if destination
33
+
22
34
  term
23
35
  end
24
36
  end
25
- end
37
+ end
@@ -0,0 +1,11 @@
1
+ require 'spec_helper'
2
+
3
+ describe Flock::ExecuteOperations do
4
+ describe "#negate" do
5
+ it "should create a negate opperation" do
6
+ mock(Flock::ExecuteOperation).new(Flock::Edges::ExecuteOperationType::Negate, [1,1,3])
7
+ operation = Flock::ExecuteOperations.new(Flock::Client.new(Flock::MockService), nil)
8
+ operation.negate(1,1,3)
9
+ end
10
+ end
11
+ end
@@ -6,20 +6,29 @@ describe Flock do
6
6
  before do
7
7
  @flock = new_flock_client
8
8
 
9
- flock.add(1,1,1)
10
9
  flock.add(1,1,2)
10
+ flock.add(1,1,3)
11
+ flock.add(2,1,1)
12
+ flock.add(4,1,1)
11
13
  end
12
14
 
13
15
  describe 'add' do
14
16
  it 'works' do
15
- flock.contains(1,1,1).should == true
17
+ flock.contains(1,1,2).should == true
16
18
  end
17
19
  end
18
20
 
19
21
  describe 'remove' do
20
22
  it 'works' do
21
- flock.remove(1,1,1)
22
- flock.contains(1,1,1).should == false
23
+ flock.remove(1,1,2)
24
+ flock.contains(1,1,2).should == false
25
+ end
26
+ end
27
+
28
+ describe 'negate' do
29
+ it 'works' do
30
+ flock.negate(1,1,2)
31
+ flock.contains(1,1,2).should == false
23
32
  end
24
33
  end
25
34
 
@@ -29,6 +38,104 @@ describe Flock do
29
38
  end
30
39
  end
31
40
 
41
+ describe 'archive' do
42
+ describe 'forward' do
43
+ describe 'one' do
44
+ it 'works' do
45
+ flock.archive(1,1,2)
46
+ flock.contains(1,1,2).should == false
47
+ end
48
+ end
49
+
50
+ describe 'many' do
51
+ it 'works' do
52
+ flock.archive(1,1,[2, 3])
53
+ flock.contains(1,1,2).should == false
54
+ flock.contains(1,1,3).should == false
55
+ end
56
+ end
57
+
58
+ describe 'all' do
59
+ it 'works' do
60
+ flock.archive(1,1,nil)
61
+ flock.contains(1,1,2).should == false
62
+ flock.contains(1,1,3).should == false
63
+ end
64
+ end
65
+ end
66
+
67
+ describe 'backwards' do
68
+ describe 'many' do
69
+ it 'works' do
70
+ flock.archive([2,4],1,1)
71
+ flock.contains(2,1,1).should == false
72
+ flock.contains(4,1,1).should == false
73
+ end
74
+ end
75
+
76
+ describe 'all' do
77
+ it 'works' do
78
+ flock.archive(nil,1,1)
79
+ flock.contains(2,1,1).should == false
80
+ flock.contains(4,1,1).should == false
81
+ end
82
+ end
83
+ end
84
+ end
85
+
86
+ describe 'unarchive' do
87
+ describe 'forward' do
88
+ before do
89
+ flock.archive(1,1,nil)
90
+ end
91
+
92
+ describe 'one' do
93
+ it 'works' do
94
+ flock.unarchive(1,1,2)
95
+ flock.contains(1,1,2).should == true
96
+ end
97
+ end
98
+
99
+ describe 'many' do
100
+ it 'works' do
101
+ flock.unarchive(1,1,[2,3])
102
+ flock.contains(1,1,2).should == true
103
+ flock.contains(1,1,3).should == true
104
+ end
105
+ end
106
+
107
+ describe 'all' do
108
+ it 'works' do
109
+ flock.unarchive(1,1,nil)
110
+ flock.contains(1,1,2).should == true
111
+ flock.contains(1,1,3).should == true
112
+ end
113
+ end
114
+ end
115
+
116
+ describe 'backwards' do
117
+ before do
118
+ flock.archive(nil,1,1)
119
+ end
120
+
121
+ describe 'many' do
122
+ it 'works' do
123
+ flock.unarchive([2,4],1,1)
124
+ flock.contains(2,1,1).should == true
125
+ flock.contains(4,1,1).should == true
126
+ end
127
+ end
128
+
129
+ describe 'all' do
130
+ it 'works' do
131
+ flock.unarchive(nil,1,1)
132
+ flock.contains(2,1,1).should == true
133
+ flock.contains(4,1,1).should == true
134
+ end
135
+ end
136
+ end
137
+ end
138
+
32
139
  describe 'select' do
33
140
  attr_accessor :query
34
141
 
@@ -37,21 +144,21 @@ describe Flock do
37
144
  end
38
145
 
39
146
  it "supports old style of select" do
40
- flock.select([1,1,nil]).to_a.sort.should == [1,2]
147
+ flock.select([1,1,nil]).to_a.sort.should == [2, 3]
41
148
  end
42
149
 
43
150
  it "supports selecting multiple nodes" do
44
- flock.select(1,1,[1,2]).to_a.sort.should == [1,2]
151
+ flock.select(1,1,[2,3]).to_a.sort.should == [2, 3]
45
152
  end
46
153
 
47
154
  it "turns nodes into ints if possible" do
48
155
  user = mock!.to_i { 1 }.subject
49
- flock.select(user, 1, nil).to_a.sort.should == [1,2]
156
+ flock.select(user, 1, nil).to_a.sort.should == [2, 3]
50
157
  end
51
158
 
52
159
  describe 'to_a' do
53
160
  it 'works' do
54
- flock.select(1,1,nil).to_a.sort.should == [1,2]
161
+ flock.select(1,1,nil).to_a.sort.should == [2, 3]
55
162
  end
56
163
  end
57
164
 
@@ -59,13 +166,13 @@ describe Flock do
59
166
  it 'works' do
60
167
  page, next_cursor, prev_cursor = query.paginate(1).unapply
61
168
 
62
- page.should == [1]
169
+ page.should == [2]
63
170
  prev_cursor.should == Flock::CursorEnd
64
171
  next_cursor.should_not == Flock::CursorEnd
65
172
 
66
173
  page, next_cursor, prev_cursor = query.paginate(1, next_cursor).unapply
67
174
 
68
- page.should == [2]
175
+ page.should == [3]
69
176
  prev_cursor.should == Flock::CursorStart
70
177
  next_cursor.should == Flock::CursorEnd
71
178
  end
@@ -75,10 +182,10 @@ describe Flock do
75
182
  results = query.paginate(1)
76
183
 
77
184
  results.next_page?.should == true
78
- results.next_page.should == [1]
185
+ results.next_page.should == [2]
79
186
 
80
187
  results.next_page?.should == true
81
- results.next_page.should == [2]
188
+ results.next_page.should == [3]
82
189
 
83
190
  results.next_page?.should == false
84
191
  end
@@ -89,10 +196,10 @@ describe Flock do
89
196
  results = query.paginate(2)
90
197
 
91
198
  results.next?.should == true
92
- results.next.should == 1
199
+ results.next.should == 2
93
200
 
94
201
  results.next?.should == true
95
- results.next.should == 2
202
+ results.next.should == 3
96
203
 
97
204
  results.next?.should == false
98
205
  end
@@ -0,0 +1,65 @@
1
+ require 'spec_helper'
2
+
3
+ describe Flock::QueryTerm do
4
+ describe '#new' do
5
+ it 'requires a query of length 4' do
6
+ lambda { Flock::QueryTerm.new([1, 1, 1, 1, 1]) }.should raise_error(ArgumentError)
7
+ end
8
+
9
+ it 'has a source, graph, destination, and states' do
10
+ qt = Flock::QueryTerm.new([1, 1, 2, [Flock::Edges::EdgeState::Positive, Flock::Edges::EdgeState::Negative]])
11
+ qt.source.should == 1
12
+ qt.graph.should == 1
13
+ qt.destination.should == 2
14
+ qt.states.should == [Flock::Edges::EdgeState::Positive, Flock::Edges::EdgeState::Negative]
15
+ end
16
+ end
17
+
18
+ it "is forward if source is a single integer" do
19
+ qt = Flock::QueryTerm.new([1, 1, 2, []])
20
+ qt.should be_forward
21
+
22
+ qt = Flock::QueryTerm.new([1, 1, nil, []])
23
+ qt.should be_forward
24
+
25
+ qt = Flock::QueryTerm.new([1, 1, [2, 3], []])
26
+ qt.should be_forward
27
+ end
28
+
29
+ it "is not forward if source is not a single integer" do
30
+ qt = Flock::QueryTerm.new([[1, 2], 1, 3, []])
31
+ qt.should_not be_forward
32
+
33
+ qt = Flock::QueryTerm.new([nil, 1, 3, []])
34
+ qt.should_not be_forward
35
+ end
36
+
37
+ describe "#to_thrift" do
38
+ before do
39
+ @term = Flock::QueryTerm.new([1, 1, 2, [Flock::Edges::EdgeState::Positive, Flock::Edges::EdgeState::Negative]])
40
+ @backward = Flock::QueryTerm.new([nil, 1, 2, [Flock::Edges::EdgeState::Positive, Flock::Edges::EdgeState::Negative]])
41
+ end
42
+
43
+ it "should set is_forward correctly" do
44
+ @term.to_thrift.is_forward.should == true
45
+ @term.source = nil
46
+ @term.to_thrift.is_forward.should == false
47
+ end
48
+
49
+ it "should switch source and destination ids for a backwards term" do
50
+ @term.to_thrift.source_id.should == @term.source
51
+ @term.to_thrift.destination_ids.should == [@term.destination].pack('Q*')
52
+
53
+ @term.source = nil
54
+ @term.to_thrift.source_id.should == @term.destination
55
+ @term.to_thrift.destination_ids.should == nil
56
+
57
+ @term.source = [1, 2]
58
+ @term.to_thrift.destination_ids.should == @term.source.pack('Q*')
59
+ end
60
+
61
+ it "should add state to thrift object" do
62
+ @term.to_thrift.state_ids.should == [Flock::Edges::EdgeState::Positive, Flock::Edges::EdgeState::Negative]
63
+ end
64
+ end
65
+ end
@@ -0,0 +1,11 @@
1
+ require 'spec_helper'
2
+
3
+ describe Flock::ExecuteOperations do
4
+ describe "#negate" do
5
+ it "should create a negate operation" do
6
+ mock(Flock::ExecuteOperation).new(Flock::Edges::ExecuteOperationType::Negate, [1,1,3])
7
+ operation = Flock::ExecuteOperations.new(Flock::Client.new(Flock::MockService), nil)
8
+ operation.negate(1,1,3)
9
+ end
10
+ end
11
+ end
metadata CHANGED
@@ -4,9 +4,9 @@ version: !ruby/object:Gem::Version
4
4
  prerelease: false
5
5
  segments:
6
6
  - 0
7
- - 3
8
- - 2
9
- version: 0.3.2
7
+ - 4
8
+ - 0
9
+ version: 0.4.0
10
10
  platform: ruby
11
11
  authors:
12
12
  - Matt Freels
@@ -16,7 +16,7 @@ autorequire:
16
16
  bindir: bin
17
17
  cert_chain: []
18
18
 
19
- date: 2010-04-29 00:00:00 -07:00
19
+ date: 2010-05-11 00:00:00 -07:00
20
20
  default_executable:
21
21
  dependencies:
22
22
  - !ruby/object:Gem::Dependency
@@ -105,8 +105,11 @@ files:
105
105
  - lib/flock/thrift/flock_types.rb
106
106
  - lib/flock/thrift/shards.rb
107
107
  - lib/flockdb.rb
108
+ - spec/execute_operations_spec.rb
108
109
  - spec/flock_spec.rb
109
110
  - spec/mock_service_spec.rb
111
+ - spec/query_term_spec.rb
112
+ - spec/simple_operation_spec.rb
110
113
  - spec/spec.opts
111
114
  - spec/spec_helper.rb
112
115
  has_rdoc: true
@@ -140,6 +143,9 @@ signing_key:
140
143
  specification_version: 3
141
144
  summary: Ruby Flock client
142
145
  test_files:
146
+ - spec/execute_operations_spec.rb
143
147
  - spec/flock_spec.rb
144
148
  - spec/mock_service_spec.rb
149
+ - spec/query_term_spec.rb
150
+ - spec/simple_operation_spec.rb
145
151
  - spec/spec_helper.rb