fauna 0.2.6 → 1.1.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/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
|