Skip to content
GitLab
Projects
Groups
Snippets
Help
Loading...
Help
What's new
10
Help
Support
Community forum
Keyboard shortcuts
?
Submit feedback
Contribute to GitLab
Sign in
Toggle navigation
Open sidebar
dms3
go-datastore
Commits
d645c4d3
Commit
d645c4d3
authored
Jan 19, 2014
by
Juan Batiz-Benet
Browse files
Options
Browse Files
Download
Email Patches
Plain Diff
added key logic.
parent
01e32a5c
Changes
2
Hide whitespace changes
Inline
Side-by-side
Showing
2 changed files
with
310 additions
and
0 deletions
+310
-0
key.go
key.go
+184
-0
key_test.go
key_test.go
+126
-0
No files found.
key.go
0 → 100644
View file @
d645c4d3
package
datastore
import
(
"code.google.com/p/go-uuid/uuid"
"path"
"strings"
)
/*
A Key represents the unique identifier of an object.
Our Key scheme is inspired by file systems and Google App Engine key model.
Keys are meant to be unique across a system. Keys are hierarchical,
incorporating more and more specific namespaces. Thus keys can be deemed
'children' or 'ancestors' of other keys::
Key("/Comedy")
Key("/Comedy/MontyPython")
Also, every namespace can be parametrized to embed relevant object
information. For example, the Key `name` (most specific namespace) could
include the object type::
Key("/Comedy/MontyPython/Actor:JohnCleese")
Key("/Comedy/MontyPython/Sketch:CheeseShop")
Key("/Comedy/MontyPython/Sketch:CheeseShop/Character:Mousebender")
*/
type
Key
struct
{
string
}
func
NewKey
(
s
string
)
Key
{
k
:=
Key
{
s
}
k
.
Clean
()
return
k
}
// Cleans up a Key, using path.Clean.
func
(
k
*
Key
)
Clean
()
{
k
.
string
=
path
.
Clean
(
"/"
+
k
.
string
)
}
// Returns the string value of Key
func
(
k
Key
)
String
()
string
{
return
k
.
string
}
// Returns the `list` representation of this Key.
// NewKey("/Comedy/MontyPython/Actor:JohnCleese").List()
// ["Comedy", "MontyPythong", "Actor:JohnCleese"]
func
(
k
Key
)
List
()
[]
string
{
return
strings
.
Split
(
k
.
string
,
"/"
)[
1
:
]
}
// Returns the reverse of this Key.
// NewKey("/Comedy/MontyPython/Actor:JohnCleese").Reverse()
// NewKey("/Actor:JohnCleese/MontyPython/Comedy")
func
(
k
Key
)
Reverse
()
Key
{
l
:=
k
.
List
()
r
:=
make
([]
string
,
len
(
l
),
len
(
l
))
for
i
,
e
:=
range
l
{
r
[
len
(
l
)
-
i
-
1
]
=
e
}
return
NewKey
(
strings
.
Join
(
r
,
"/"
))
}
// Returns the `namespaces` making up this Key.
// NewKey("/Comedy/MontyPython/Actor:JohnCleese").List()
// ["Comedy", "MontyPythong", "Actor:JohnCleese"]
func
(
k
Key
)
Namespaces
()
[]
string
{
return
k
.
List
()
}
// Returns the "base" namespace of this key (like path.Base(filename))
// NewKey("/Comedy/MontyPython/Actor:JohnCleese").BaseNamespace()
// "Actor:JohnCleese"
func
(
k
Key
)
BaseNamespace
()
string
{
n
:=
k
.
Namespaces
()
return
n
[
len
(
n
)
-
1
]
}
// Returns the "type" of this key (value of last namespace).
// NewKey("/Comedy/MontyPython/Actor:JohnCleese").List()
// "Actor"
func
(
k
Key
)
Type
()
string
{
return
NamespaceType
(
k
.
BaseNamespace
())
}
// Returns the "name" of this key (field of last namespace).
// NewKey("/Comedy/MontyPython/Actor:JohnCleese").List()
// "Actor"
func
(
k
Key
)
Name
()
string
{
return
NamespaceValue
(
k
.
BaseNamespace
())
}
// Returns an "instance" of this type key (appends value to namespace).
// NewKey("/Comedy/MontyPython/Actor:JohnCleese").List()
// "JohnCleese"
func
(
k
Key
)
Instance
(
s
string
)
Key
{
return
NewKey
(
k
.
string
+
":"
+
s
)
}
// Returns the "path" of this key (parent + type).
// NewKey("/Comedy/MontyPython/Actor:JohnCleese").Path()
// NewKey("/Comedy/MontyPython/Actor")
func
(
k
Key
)
Path
()
Key
{
s
:=
k
.
Parent
()
.
string
+
"/"
+
NamespaceType
(
k
.
BaseNamespace
())
return
NewKey
(
s
)
}
// Returns the `parent` Key of this Key.
// NewKey("/Comedy/MontyPython/Actor:JohnCleese").Parent()
// NewKey("/Comedy/MontyPython")
func
(
k
Key
)
Parent
()
Key
{
n
:=
k
.
List
()
if
len
(
n
)
==
1
{
return
NewKey
(
"/"
)
}
return
NewKey
(
strings
.
Join
(
n
[
:
len
(
n
)
-
1
],
"/"
))
}
// Returns the `child` Key of this Key.
// NewKey("/Comedy/MontyPython").Child("Actor:JohnCleese")
// NewKey("/Comedy/MontyPython/Actor:JohnCleese")
func
(
k
Key
)
Child
(
s
string
)
Key
{
return
NewKey
(
k
.
string
+
"/"
+
s
)
}
// Returns whether this key is an ancestor of `other`
// NewKey("/Comedy").IsAncestorOf("/Comedy/MontyPython")
// true
func
(
k
Key
)
IsAncestorOf
(
other
Key
)
bool
{
if
other
.
string
==
k
.
string
{
return
false
}
return
strings
.
HasPrefix
(
other
.
string
,
k
.
string
)
}
// Returns whether this key is a descendent of `other`
// NewKey("/Comedy/MontyPython").IsDescendantOf("/Comedy")
// true
func
(
k
Key
)
IsDescendantOf
(
other
Key
)
bool
{
if
other
.
string
==
k
.
string
{
return
false
}
return
strings
.
HasPrefix
(
k
.
string
,
other
.
string
)
}
func
(
k
Key
)
IsTopLevel
()
bool
{
return
len
(
k
.
List
())
==
1
}
// Returns a randomly (uuid) generated key.
// RandomKey()
// NewKey("/f98719ea086343f7b71f32ea9d9d521d")
func
RandomKey
()
Key
{
return
NewKey
(
strings
.
Replace
(
uuid
.
New
(),
"-"
,
""
,
-
1
))
}
/*
A Key Namespace is like a path element.
A namespace can optionally include a type (delimited by ':')
> NamespaceValue("Song:PhilosopherSong")
PhilosopherSong
> NamespaceType("Song:PhilosopherSong")
Song
> NamespaceType("Music:Song:PhilosopherSong")
Music:Song
*/
func
NamespaceType
(
namespace
string
)
string
{
parts
:=
strings
.
Split
(
namespace
,
":"
)
if
len
(
parts
)
<
2
{
return
""
}
return
strings
.
Join
(
parts
[
0
:
len
(
parts
)
-
1
],
":"
)
}
func
NamespaceValue
(
namespace
string
)
string
{
parts
:=
strings
.
Split
(
namespace
,
":"
)
return
parts
[
len
(
parts
)
-
1
]
}
key_test.go
0 → 100644
View file @
d645c4d3
package
datastore_test
import
(
"bytes"
.
"github.com/jbenet/datastore.go"
.
"launchpad.net/gocheck"
"math/rand"
"path"
"testing"
"strings"
)
// Hook up gocheck into the "go test" runner.
func
Test
(
t
*
testing
.
T
)
{
TestingT
(
t
)
}
func
randomString
()
string
{
chars
:=
"abcdefghijklmnopqrstuvwxyz1234567890"
var
buf
bytes
.
Buffer
l
:=
rand
.
Intn
(
50
)
for
j
:=
0
;
j
<
l
;
j
++
{
buf
.
WriteByte
(
chars
[
rand
.
Intn
(
len
(
chars
))])
}
return
buf
.
String
()
}
type
KeySuite
struct
{}
var
_
=
Suite
(
&
KeySuite
{})
func
(
ks
*
KeySuite
)
SubtestKey
(
s
string
,
c
*
C
)
{
fixed
:=
path
.
Clean
(
"/"
+
s
)
namespaces
:=
strings
.
Split
(
fixed
,
"/"
)[
1
:
]
lastNamespace
:=
namespaces
[
len
(
namespaces
)
-
1
]
lnparts
:=
strings
.
Split
(
lastNamespace
,
":"
)
ktype
:=
""
if
len
(
lnparts
)
>
1
{
ktype
=
strings
.
Join
(
lnparts
[
:
len
(
lnparts
)
-
1
],
":"
)
}
kname
:=
lnparts
[
len
(
lnparts
)
-
1
]
kchild
:=
path
.
Clean
(
fixed
+
"/cchildd"
)
kparent
:=
"/"
+
strings
.
Join
(
append
(
namespaces
[
:
len
(
namespaces
)
-
1
]),
"/"
)
kpath
:=
path
.
Clean
(
kparent
+
"/"
+
ktype
)
kinstance
:=
fixed
+
":"
+
"inst"
c
.
Log
(
"Testing: "
,
NewKey
(
s
))
c
.
Check
(
NewKey
(
s
)
.
String
(),
Equals
,
fixed
)
c
.
Check
(
NewKey
(
s
),
Equals
,
NewKey
(
s
))
c
.
Check
(
NewKey
(
s
)
.
String
(),
Equals
,
NewKey
(
s
)
.
String
())
c
.
Check
(
NewKey
(
s
)
.
Name
(),
Equals
,
kname
)
c
.
Check
(
NewKey
(
s
)
.
Type
(),
Equals
,
ktype
)
c
.
Check
(
NewKey
(
s
)
.
Path
()
.
String
(),
Equals
,
kpath
)
c
.
Check
(
NewKey
(
s
)
.
Instance
(
"inst"
)
.
String
(),
Equals
,
kinstance
)
c
.
Check
(
NewKey
(
s
)
.
Child
(
"cchildd"
)
.
String
(),
Equals
,
kchild
)
c
.
Check
(
NewKey
(
s
)
.
Child
(
"cchildd"
)
.
Parent
()
.
String
(),
Equals
,
fixed
)
c
.
Check
(
NewKey
(
s
)
.
Parent
()
.
String
(),
Equals
,
kparent
)
c
.
Check
(
len
(
NewKey
(
s
)
.
List
()),
Equals
,
len
(
namespaces
))
c
.
Check
(
len
(
NewKey
(
s
)
.
Namespaces
()),
Equals
,
len
(
namespaces
))
for
i
,
e
:=
range
NewKey
(
s
)
.
List
()
{
c
.
Check
(
namespaces
[
i
],
Equals
,
e
)
}
}
func
(
ks
*
KeySuite
)
TestKeyBasic
(
c
*
C
)
{
ks
.
SubtestKey
(
""
,
c
)
ks
.
SubtestKey
(
"abcde"
,
c
)
ks
.
SubtestKey
(
"disahfidsalfhduisaufidsail"
,
c
)
ks
.
SubtestKey
(
"/fdisahfodisa/fdsa/fdsafdsafdsafdsa/fdsafdsa/"
,
c
)
ks
.
SubtestKey
(
"4215432143214321432143214321"
,
c
)
ks
.
SubtestKey
(
"/fdisaha////fdsa////fdsafdsafdsafdsa/fdsafdsa/"
,
c
)
ks
.
SubtestKey
(
"abcde:fdsfd"
,
c
)
ks
.
SubtestKey
(
"disahfidsalfhduisaufidsail:fdsa"
,
c
)
ks
.
SubtestKey
(
"/fdisahfodisa/fdsa/fdsafdsafdsafdsa/fdsafdsa/:"
,
c
)
ks
.
SubtestKey
(
"4215432143214321432143214321:"
,
c
)
ks
.
SubtestKey
(
"fdisaha////fdsa////fdsafdsafdsafdsa/fdsafdsa/f:fdaf"
,
c
)
}
func
CheckTrue
(
c
*
C
,
cond
bool
)
{
c
.
Check
(
cond
,
Equals
,
true
)
}
func
(
ks
*
KeySuite
)
TestKeyAncestry
(
c
*
C
)
{
k1
:=
NewKey
(
"/A/B/C"
)
k2
:=
NewKey
(
"/A/B/C/D"
)
c
.
Check
(
k1
.
String
(),
Equals
,
"/A/B/C"
)
c
.
Check
(
k2
.
String
(),
Equals
,
"/A/B/C/D"
)
CheckTrue
(
c
,
k1
.
IsAncestorOf
(
k2
))
CheckTrue
(
c
,
k2
.
IsDescendantOf
(
k1
))
CheckTrue
(
c
,
NewKey
(
"/A"
)
.
IsAncestorOf
(
k2
))
CheckTrue
(
c
,
NewKey
(
"/A"
)
.
IsAncestorOf
(
k1
))
CheckTrue
(
c
,
!
NewKey
(
"/A"
)
.
IsDescendantOf
(
k2
))
CheckTrue
(
c
,
!
NewKey
(
"/A"
)
.
IsDescendantOf
(
k1
))
CheckTrue
(
c
,
k2
.
IsDescendantOf
(
NewKey
(
"/A"
)))
CheckTrue
(
c
,
k1
.
IsDescendantOf
(
NewKey
(
"/A"
)))
CheckTrue
(
c
,
!
k2
.
IsAncestorOf
(
NewKey
(
"/A"
)))
CheckTrue
(
c
,
!
k1
.
IsAncestorOf
(
NewKey
(
"/A"
)))
CheckTrue
(
c
,
!
k2
.
IsAncestorOf
(
k2
))
CheckTrue
(
c
,
!
k1
.
IsAncestorOf
(
k1
))
c
.
Check
(
k1
.
Child
(
"D"
)
.
String
(),
Equals
,
k2
.
String
())
c
.
Check
(
k1
.
String
(),
Equals
,
k2
.
Parent
()
.
String
())
c
.
Check
(
k1
.
Path
()
.
String
(),
Equals
,
k2
.
Parent
()
.
Path
()
.
String
())
}
func
(
ks
*
KeySuite
)
TestType
(
c
*
C
)
{
k1
:=
NewKey
(
"/A/B/C:c"
)
k2
:=
NewKey
(
"/A/B/C:c/D:d"
)
CheckTrue
(
c
,
k1
.
IsAncestorOf
(
k2
))
CheckTrue
(
c
,
k2
.
IsDescendantOf
(
k1
))
c
.
Check
(
k1
.
Type
(),
Equals
,
"C"
)
c
.
Check
(
k2
.
Type
(),
Equals
,
"D"
)
c
.
Check
(
k1
.
Type
(),
Equals
,
k2
.
Parent
()
.
Type
())
}
func
(
ks
*
KeySuite
)
TestRandom
(
c
*
C
)
{
keys
:=
map
[
Key
]
bool
{}
for
i
:=
0
;
i
<
1000
;
i
++
{
r
:=
RandomKey
()
_
,
found
:=
keys
[
r
]
CheckTrue
(
c
,
!
found
)
keys
[
r
]
=
true
}
CheckTrue
(
c
,
len
(
keys
)
==
1000
)
}
Write
Preview
Markdown
is supported
0%
Try again
or
attach a new file
.
Attach a file
Cancel
You are about to add
0
people
to the discussion. Proceed with caution.
Finish editing this message first!
Cancel
Please
register
or
sign in
to comment