fauna 0.2.6 → 1.1.0
Sign up to get free protection for your applications and to get access to all the features.
- data/CHANGELOG +2 -0
- data/Manifest +0 -31
- data/README.md +18 -150
- data/Rakefile +1 -1
- data/examples/welcome.rb +3 -3
- data/fauna.gemspec +5 -11
- data/lib/fauna.rb +11 -88
- data/lib/fauna/cache.rb +58 -0
- data/lib/fauna/client.rb +14 -67
- data/lib/fauna/connection.rb +21 -27
- data/lib/fauna/provided_classes.rb +39 -0
- data/lib/fauna/rails.rb +28 -8
- data/lib/fauna/resource.rb +34 -121
- data/lib/fauna/set.rb +184 -0
- data/lib/fauna/util.rb +19 -0
- data/lib/tasks/fauna.rake +61 -1
- data/test/class_test.rb +29 -58
- data/test/client_test.rb +18 -18
- data/test/connection_test.rb +8 -8
- data/test/database_test.rb +47 -0
- data/test/readme_test.rb +2 -3
- data/test/set_test.rb +94 -0
- data/test/test_helper.rb +27 -15
- metadata +14 -57
- data/lib/fauna/ddl.rb +0 -157
- data/lib/fauna/event_set.rb +0 -197
- data/lib/fauna/model.rb +0 -73
- data/lib/fauna/model/class.rb +0 -32
- data/lib/fauna/model/user.rb +0 -23
- data/lib/fauna/publisher.rb +0 -8
- data/test/association_test.rb +0 -23
- data/test/event_set_test.rb +0 -92
- data/test/fixtures.rb +0 -70
- data/test/publisher_test.rb +0 -48
- data/test/user_test.rb +0 -80
- data/test/validation_test.rb +0 -28
data/lib/fauna/set.rb
ADDED
@@ -0,0 +1,184 @@
|
|
1
|
+
module Fauna
|
2
|
+
class Set
|
3
|
+
|
4
|
+
attr_reader :ref
|
5
|
+
|
6
|
+
def initialize(ref)
|
7
|
+
@ref = ref
|
8
|
+
end
|
9
|
+
|
10
|
+
def page(pagination = {})
|
11
|
+
SetPage.find(ref, {}, pagination)
|
12
|
+
end
|
13
|
+
|
14
|
+
def events(pagination = {})
|
15
|
+
EventsPage.find("#{ref}/events", {}, pagination)
|
16
|
+
end
|
17
|
+
|
18
|
+
# query DSL
|
19
|
+
|
20
|
+
def self.query(&block)
|
21
|
+
module_eval(&block)
|
22
|
+
end
|
23
|
+
|
24
|
+
def self.union(*args)
|
25
|
+
QuerySet.new('union', *args)
|
26
|
+
end
|
27
|
+
|
28
|
+
def self.intersection(*args)
|
29
|
+
QuerySet.new('intersection', *args)
|
30
|
+
end
|
31
|
+
|
32
|
+
def self.difference(*args)
|
33
|
+
QuerySet.new('difference', *args)
|
34
|
+
end
|
35
|
+
|
36
|
+
def self.merge(*args)
|
37
|
+
QuerySet.new('merge', *args)
|
38
|
+
end
|
39
|
+
|
40
|
+
def self.join(*args)
|
41
|
+
QuerySet.new('join', *args)
|
42
|
+
end
|
43
|
+
|
44
|
+
def self.match(*args)
|
45
|
+
QuerySet.new('match', *args)
|
46
|
+
end
|
47
|
+
|
48
|
+
# although each is handled via the query DSL, it might make more
|
49
|
+
# sense to add it as a modifier on Set instances, similar to events.
|
50
|
+
|
51
|
+
def self.each(*args)
|
52
|
+
EachSet.new(*args)
|
53
|
+
end
|
54
|
+
end
|
55
|
+
|
56
|
+
class QuerySet < Set
|
57
|
+
def initialize(function, *params)
|
58
|
+
@function = function
|
59
|
+
@params = params
|
60
|
+
end
|
61
|
+
|
62
|
+
def param_strings
|
63
|
+
@param_strings ||= @params.map do |p|
|
64
|
+
if p.respond_to? :expr
|
65
|
+
p.expr
|
66
|
+
elsif p.respond_to? :ref
|
67
|
+
p.ref
|
68
|
+
else
|
69
|
+
p
|
70
|
+
end
|
71
|
+
end
|
72
|
+
end
|
73
|
+
|
74
|
+
def expr
|
75
|
+
@expr ||= "#{@function}(#{param_strings.join ','})"
|
76
|
+
end
|
77
|
+
|
78
|
+
def ref
|
79
|
+
"query?q=#{expr}"
|
80
|
+
end
|
81
|
+
|
82
|
+
def page(pagination = {})
|
83
|
+
SetPage.find('query', { 'q' => expr }, pagination)
|
84
|
+
end
|
85
|
+
|
86
|
+
def events(pagination = {})
|
87
|
+
EventsPage.find("query", { 'q' => "events(#{expr})" }, pagination)
|
88
|
+
end
|
89
|
+
end
|
90
|
+
|
91
|
+
class EachSet < QuerySet
|
92
|
+
def initialize(*params)
|
93
|
+
super('each', *params)
|
94
|
+
end
|
95
|
+
|
96
|
+
def events(pagination = {})
|
97
|
+
query = param_strings.first
|
98
|
+
subqueries = param_strings.drop(1).join ','
|
99
|
+
EventsPage.find("query", { 'q' => "each(events(#{query}),#{subqueries})" }, pagination)
|
100
|
+
end
|
101
|
+
end
|
102
|
+
|
103
|
+
class CustomSet < Set
|
104
|
+
def add(resource)
|
105
|
+
self.class.add(self, resource)
|
106
|
+
end
|
107
|
+
|
108
|
+
def remove(resource)
|
109
|
+
self.class.remove(self, resource)
|
110
|
+
end
|
111
|
+
|
112
|
+
def self.add(set, resource)
|
113
|
+
set = set.ref if set.respond_to? :ref
|
114
|
+
resource = resource.ref if resource.respond_to? :ref
|
115
|
+
Fauna::Client.put("#{set}/#{resource}")
|
116
|
+
end
|
117
|
+
|
118
|
+
def self.remove(set, resource)
|
119
|
+
set = set.ref if set.respond_to? :ref
|
120
|
+
resource = resource.ref if resource.respond_to? :ref
|
121
|
+
Fauna::Client.delete("#{set}/#{resource}")
|
122
|
+
end
|
123
|
+
end
|
124
|
+
|
125
|
+
class SetPage < Fauna::Resource
|
126
|
+
include Enumerable
|
127
|
+
|
128
|
+
def refs
|
129
|
+
@refs ||= struct['resources']
|
130
|
+
end
|
131
|
+
|
132
|
+
def each(&block)
|
133
|
+
refs.each(&block)
|
134
|
+
end
|
135
|
+
|
136
|
+
def empty?
|
137
|
+
refs.empty?
|
138
|
+
end
|
139
|
+
|
140
|
+
def length; refs.length end
|
141
|
+
def size; refs.size end
|
142
|
+
end
|
143
|
+
|
144
|
+
class EventsPage < Fauna::Resource
|
145
|
+
include Enumerable
|
146
|
+
|
147
|
+
def events
|
148
|
+
@events ||= struct['events'].map { |e| Event.new(e) }
|
149
|
+
end
|
150
|
+
|
151
|
+
def each(&block)
|
152
|
+
events.each(&block)
|
153
|
+
end
|
154
|
+
|
155
|
+
def empty?
|
156
|
+
events.empty?
|
157
|
+
end
|
158
|
+
|
159
|
+
def length; events.length end
|
160
|
+
def size; events.size end
|
161
|
+
end
|
162
|
+
|
163
|
+
class Event
|
164
|
+
def initialize(attrs)
|
165
|
+
@attrs = attrs
|
166
|
+
end
|
167
|
+
|
168
|
+
def ts
|
169
|
+
Fauna.time_from_usecs(@attrs['ts'])
|
170
|
+
end
|
171
|
+
|
172
|
+
def resource
|
173
|
+
@attrs['resource']
|
174
|
+
end
|
175
|
+
|
176
|
+
def set
|
177
|
+
@attrs['set']
|
178
|
+
end
|
179
|
+
|
180
|
+
def action
|
181
|
+
@attrs['action']
|
182
|
+
end
|
183
|
+
end
|
184
|
+
end
|
data/lib/fauna/util.rb
ADDED
@@ -0,0 +1,19 @@
|
|
1
|
+
module Fauna
|
2
|
+
def self.stringify_keys!(hash)
|
3
|
+
hash.keys.each do |k|
|
4
|
+
hash[k.to_s] = hash.delete k
|
5
|
+
end
|
6
|
+
end
|
7
|
+
|
8
|
+
def self.stringify_keys(hash)
|
9
|
+
stringify_keys!(hash.dup)
|
10
|
+
end
|
11
|
+
|
12
|
+
def self.time_from_usecs(microseconds)
|
13
|
+
Time.at(microseconds/1_000_000, microseconds % 1_000_000)
|
14
|
+
end
|
15
|
+
|
16
|
+
def self.usecs_from_time(time)
|
17
|
+
time.to_i * 1000000 + time.usec
|
18
|
+
end
|
19
|
+
end
|
data/lib/tasks/fauna.rake
CHANGED
@@ -2,10 +2,70 @@
|
|
2
2
|
task :environment
|
3
3
|
|
4
4
|
namespace :fauna do
|
5
|
-
desc "Migrate your
|
5
|
+
desc "Migrate your Fauna database to the latest version of your schema"
|
6
6
|
task :migrate => :environment do
|
7
|
+
puts "Migrating #{Rails.env} Fauna database schema"
|
7
8
|
Fauna::Client.context(Fauna.connection) do
|
8
9
|
Fauna.migrate_schema!
|
9
10
|
end
|
10
11
|
end
|
12
|
+
|
13
|
+
desc "Completely reset your Fauna database"
|
14
|
+
task :reset => :environment do
|
15
|
+
if Rails.env.production?
|
16
|
+
puts "Won't reset #{Rails.env} Fauna database"
|
17
|
+
else
|
18
|
+
Fauna::Client.context(Fauna.root_connection) do
|
19
|
+
puts "Resetting #{Rails.env} Fauna database"
|
20
|
+
Fauna::Client.delete("everything")
|
21
|
+
end
|
22
|
+
Fauna.auth!
|
23
|
+
end
|
24
|
+
end
|
25
|
+
|
26
|
+
desc "Dump the contents of your Fauna database to 'test/fixtures/fauna'"
|
27
|
+
task :dump => :environment do
|
28
|
+
puts "Dumping #{Rails.env} Fauna database contents to #{Fauna::FIXTURES_DIR}"
|
29
|
+
Fauna::Client.context(Fauna.connection) do
|
30
|
+
FileUtils.mkdir_p(Fauna::FIXTURES_DIR)
|
31
|
+
|
32
|
+
class_configs = Fauna.connection.get("/classes")["references"] || {}
|
33
|
+
class_configs["users"] = nil
|
34
|
+
|
35
|
+
Dir.chdir(Fauna::FIXTURES_DIR) do
|
36
|
+
class_configs.each do |ref, value|
|
37
|
+
class_name = ref[0..-8]
|
38
|
+
FileUtils.mkdir_p(class_name)
|
39
|
+
|
40
|
+
Dir.chdir(class_name) do
|
41
|
+
# FIXME shouldn't round trip JSON
|
42
|
+
File.open("config.json", "w") { |f| f.write(value.to_json)} if value
|
43
|
+
(Fauna.connection.get(class_name)["references"] || {}).each do |ref, value|
|
44
|
+
File.open("#{ref.split("/").last}.json", "w") { |f| f.write(value.to_json) }
|
45
|
+
end
|
46
|
+
end
|
47
|
+
end
|
48
|
+
end
|
49
|
+
end
|
50
|
+
end
|
51
|
+
|
52
|
+
desc "Load the contents of your Fauna database"
|
53
|
+
task :load => :environment do
|
54
|
+
puts "Loading #{Rails.env} Fauna database contents from #{Fauna::FIXTURES_DIR}"
|
55
|
+
Fauna::Client.context(Fauna.connection) do
|
56
|
+
Dir.chdir(Fauna::FIXTURES_DIR) do
|
57
|
+
Dir["**/*.json"].map do |filename|
|
58
|
+
value = JSON.parse(File.open(filename) { |f| f.read })
|
59
|
+
begin
|
60
|
+
Fauna.connection.put(value["ref"], value)
|
61
|
+
rescue Fauna::Connection::NotFound
|
62
|
+
Fauna.connection.post(value["ref"].split("/")[0..-2].join("/"), value)
|
63
|
+
end
|
64
|
+
end
|
65
|
+
end
|
66
|
+
end
|
67
|
+
end
|
68
|
+
|
11
69
|
end
|
70
|
+
|
71
|
+
task :test => ["fauna:reset", "fauna:migrate", "fauna:load"]
|
data/test/class_test.rb
CHANGED
@@ -1,111 +1,82 @@
|
|
1
1
|
require File.expand_path('../test_helper', __FILE__)
|
2
2
|
|
3
|
-
class
|
4
|
-
include ActiveModel::Lint::Tests
|
3
|
+
class InstanceTest < MiniTest::Unit::TestCase
|
5
4
|
|
6
5
|
def setup
|
7
6
|
super
|
8
|
-
@model =
|
9
|
-
end
|
10
|
-
|
11
|
-
def test_class_name
|
12
|
-
assert_equal 'classes/pigs', Pig.fauna_class
|
13
|
-
assert_equal 'classes/pigs/config', Pig.config_ref
|
14
|
-
end
|
15
|
-
|
16
|
-
def test_class_save
|
17
|
-
Pig.update_data! do |data|
|
18
|
-
data["class_visited"] = true
|
19
|
-
end
|
20
|
-
assert Pig.data["class_visited"]
|
7
|
+
@model = Fauna::Resource.new('classes/pigs')
|
21
8
|
end
|
22
9
|
|
23
10
|
def test_create
|
24
|
-
pig =
|
25
|
-
assert_equal
|
11
|
+
pig = Fauna::Resource.create 'classes/pigs', :data => { :visited => true }
|
12
|
+
assert_equal true, pig.data['visited']
|
26
13
|
assert pig.persisted?
|
27
14
|
assert pig.ref
|
28
15
|
end
|
29
16
|
|
30
17
|
def test_all
|
31
|
-
pig =
|
32
|
-
assert
|
18
|
+
pig = Fauna::Resource.create 'classes/pigs'
|
19
|
+
assert Fauna::Set.new('classes/pigs/instances').page.include?(pig.ref)
|
33
20
|
end
|
34
21
|
|
35
22
|
def test_save
|
36
|
-
pig =
|
23
|
+
pig = Fauna::Resource.new 'classes/pigs'
|
37
24
|
pig.save
|
38
25
|
assert pig.persisted?
|
39
26
|
end
|
40
27
|
|
41
28
|
def test_update
|
42
|
-
pig =
|
29
|
+
pig = Fauna::Resource.new 'classes/pigs', :data => { :visited => false }
|
43
30
|
pig.save
|
44
|
-
pig.
|
45
|
-
assert pig.visited
|
46
|
-
end
|
47
|
-
|
48
|
-
def test_changes
|
49
|
-
pig = Pig.new(:visited => true)
|
31
|
+
pig.data['visited'] = true
|
50
32
|
pig.save
|
51
|
-
pig.update(:visited => false)
|
52
|
-
assert_equal pig.changes.page.events.length, 2
|
53
|
-
end
|
54
33
|
|
55
|
-
|
56
|
-
pig
|
57
|
-
pig1 = Pig.find_by_ref(pig.ref)
|
58
|
-
assert_equal pig.ref, pig1.ref
|
59
|
-
assert pig1.persisted?
|
34
|
+
assert pig.data['visited']
|
35
|
+
assert_equal pig.events.length, 2
|
60
36
|
end
|
61
37
|
|
62
|
-
def
|
63
|
-
pig =
|
64
|
-
pig1 =
|
38
|
+
def test_find
|
39
|
+
pig = Fauna::Resource.create 'classes/pigs'
|
40
|
+
pig1 = Fauna::Resource.find(pig.ref)
|
65
41
|
assert_equal pig.ref, pig1.ref
|
66
42
|
assert pig1.persisted?
|
67
43
|
end
|
68
44
|
|
69
|
-
def
|
70
|
-
pig =
|
71
|
-
|
72
|
-
pig1 = Pig.find(pig.id)
|
45
|
+
def test_find_by_constraint
|
46
|
+
pig = Fauna::Resource.create 'classes/pigs', :constraints => { :name => "the pig" }
|
47
|
+
pig1 = Fauna::Resource.find('classes/pigs/constraints/name/the%20pig')
|
73
48
|
assert_equal pig.ref, pig1.ref
|
74
49
|
assert pig1.persisted?
|
75
|
-
|
76
|
-
pig2 = Pig.find_by_id(pig.id)
|
77
|
-
assert_equal pig.ref, pig2.ref
|
78
|
-
assert pig2.persisted?
|
79
50
|
end
|
80
51
|
|
81
|
-
def
|
82
|
-
pig =
|
83
|
-
pig.
|
84
|
-
assert pig.
|
52
|
+
def test_delete
|
53
|
+
pig = Fauna::Resource.create 'classes/pigs'
|
54
|
+
pig.delete
|
55
|
+
assert pig.deleted?
|
85
56
|
end
|
86
57
|
|
87
58
|
def test_ts
|
88
|
-
pig =
|
59
|
+
pig = Fauna::Resource.create 'classes/pigs'
|
89
60
|
assert_instance_of(Time, pig.ts)
|
90
61
|
|
91
|
-
pig =
|
62
|
+
pig = Fauna::Resource.new 'classes/pigs'
|
92
63
|
assert_nil pig.ts
|
93
64
|
end
|
94
65
|
|
95
66
|
def test_ts_assignment
|
96
67
|
time = Time.at(0)
|
97
|
-
pig =
|
68
|
+
pig = Fauna::Resource.create 'classes/pigs'
|
98
69
|
pig.ts = time
|
99
70
|
|
100
|
-
Fauna::Client.context(@
|
101
|
-
pig2 =
|
102
|
-
|
71
|
+
Fauna::Client.context(@server_connection) do
|
72
|
+
pig2 = Fauna::Resource.find(pig.ref)
|
73
|
+
assert(time != pig2.ts)
|
103
74
|
end
|
104
75
|
|
105
76
|
pig.save
|
106
77
|
|
107
|
-
Fauna::Client.context(@
|
108
|
-
pig3 =
|
78
|
+
Fauna::Client.context(@server_connection) do
|
79
|
+
pig3 = Fauna::Resource.find(pig.ref)
|
109
80
|
# Waiting on server support for timestamp overrides
|
110
81
|
# assert_equal time, pig3.ts
|
111
82
|
end
|
data/test/client_test.rb
CHANGED
@@ -6,17 +6,17 @@ class ClientTest < MiniTest::Unit::TestCase
|
|
6
6
|
@attributes = { "name" => "Princess Eilonwy", "email" => email, "password" => password }
|
7
7
|
end
|
8
8
|
|
9
|
-
def
|
10
|
-
Fauna::Client.context(@
|
9
|
+
def test_database_context
|
10
|
+
Fauna::Client.context(@server_connection) do
|
11
11
|
user = Fauna::Client.post("users", @attributes)
|
12
|
-
user = Fauna::Client.get(user
|
13
|
-
Fauna::Client.delete(user
|
12
|
+
user = Fauna::Client.get(user['ref'])
|
13
|
+
Fauna::Client.delete(user['ref'])
|
14
14
|
end
|
15
15
|
end
|
16
16
|
|
17
17
|
def test_client_context
|
18
18
|
Fauna::Client.context(@client_connection) do
|
19
|
-
user = Fauna::
|
19
|
+
user = Fauna::Resource.create("users", @attributes)
|
20
20
|
Fauna::Client.context(@client_connection) do
|
21
21
|
assert_raises(Fauna::Connection::Unauthorized) do
|
22
22
|
Fauna::Client.get(user.ref)
|
@@ -26,7 +26,7 @@ class ClientTest < MiniTest::Unit::TestCase
|
|
26
26
|
end
|
27
27
|
|
28
28
|
def test_token_context
|
29
|
-
Fauna::Client.context(@
|
29
|
+
Fauna::Client.context(@server_connection) do
|
30
30
|
Fauna::Client.post("users", @attributes)
|
31
31
|
end
|
32
32
|
|
@@ -34,28 +34,28 @@ class ClientTest < MiniTest::Unit::TestCase
|
|
34
34
|
@token = Fauna::Client.post("tokens", @attributes)
|
35
35
|
end
|
36
36
|
|
37
|
-
Fauna::Client.context(Fauna::Connection.new(:token => @
|
38
|
-
user = Fauna::Client.get(@token
|
39
|
-
Fauna::Client.delete(user
|
37
|
+
Fauna::Client.context(Fauna::Connection.new(:secret => @token['secret'], :domain => @server_connection.domain, :scheme => @server_connection.scheme)) do
|
38
|
+
user = Fauna::Client.get(@token['user'])
|
39
|
+
Fauna::Client.delete(user['ref'])
|
40
40
|
end
|
41
41
|
end
|
42
42
|
|
43
43
|
def test_caching_1
|
44
|
-
Fauna::Client.context(@
|
45
|
-
|
46
|
-
@
|
47
|
-
Fauna::Client.get(
|
44
|
+
Fauna::Client.context(@server_connection) do
|
45
|
+
user = Fauna::Client.post("users", @attributes)
|
46
|
+
@server_connection.expects(:get).never
|
47
|
+
Fauna::Client.get(user['ref'])
|
48
48
|
end
|
49
49
|
end
|
50
50
|
|
51
51
|
def test_caching_2
|
52
52
|
Fauna::Client.context(@client_connection) do
|
53
|
-
|
53
|
+
user = Fauna::Client.post("users", @attributes)
|
54
54
|
|
55
|
-
Fauna::Client.context(@
|
56
|
-
Fauna::Client.get(
|
57
|
-
@
|
58
|
-
Fauna::Client.get(
|
55
|
+
Fauna::Client.context(@server_connection) do
|
56
|
+
Fauna::Client.get(user['ref'])
|
57
|
+
@server_connection.expects(:get).never
|
58
|
+
Fauna::Client.get(user['ref'])
|
59
59
|
end
|
60
60
|
end
|
61
61
|
end
|