flockdb 0.3.2 → 0.4.0

Sign up to get free protection for your applications and to get access to all the features.
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