tyrion 0.2.0 → 0.3.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.markdown +50 -12
- data/lib/tyrion.rb +1 -1
- data/lib/tyrion/attributes.rb +23 -11
- data/lib/tyrion/connection.rb +2 -2
- data/lib/tyrion/criteria.rb +94 -0
- data/lib/tyrion/document.rb +12 -23
- data/lib/tyrion/persistence.rb +19 -23
- data/lib/tyrion/querying.rb +16 -40
- data/lib/tyrion/storage.rb +11 -11
- data/lib/tyrion/version.rb +1 -1
- metadata +24 -13
- data/lib/tyrion/components.rb +0 -13
data/README.markdown
CHANGED
@@ -16,6 +16,8 @@ Each file is an array of homogeneous documents (much like collections).
|
|
16
16
|
```
|
17
17
|
|
18
18
|
### Document
|
19
|
+
It supports validations from ActiveModel::Validations.
|
20
|
+
|
19
21
|
``` ruby
|
20
22
|
class Post
|
21
23
|
include Tyrion::Document
|
@@ -23,18 +25,19 @@ Each file is an array of homogeneous documents (much like collections).
|
|
23
25
|
field :title
|
24
26
|
field :body
|
25
27
|
field :rank
|
28
|
+
|
29
|
+
validates_presence_of :title, :body, :rank
|
26
30
|
end
|
27
31
|
```
|
28
32
|
|
29
33
|
### Persistence
|
30
34
|
#### Save
|
31
35
|
``` ruby
|
32
|
-
post = Post.
|
36
|
+
post = Post.new :title => "Hello", :body => "Hi there, ..."
|
33
37
|
post.save
|
34
38
|
```
|
35
39
|
``` ruby
|
36
|
-
|
37
|
-
Post.create! :title => "Hello", :body => "Hi there, ..."
|
40
|
+
Post.create :title => "Hello", :body => "Hi there, ..."
|
38
41
|
```
|
39
42
|
|
40
43
|
#### Delete
|
@@ -43,22 +46,57 @@ Each file is an array of homogeneous documents (much like collections).
|
|
43
46
|
post.delete
|
44
47
|
```
|
45
48
|
``` ruby
|
46
|
-
Post.delete_all
|
49
|
+
Post.delete_all
|
47
50
|
```
|
51
|
+
|
52
|
+
### Querying
|
53
|
+
Chainable querying is allowed.
|
54
|
+
First called is first executed and an enumerable is returned.
|
55
|
+
|
56
|
+
`where`: all matching documents
|
57
|
+
|
48
58
|
``` ruby
|
49
|
-
Post.
|
59
|
+
Post.where :title => 'Hello', :rank => 3
|
50
60
|
```
|
51
61
|
|
52
|
-
|
53
|
-
`find_by_attribute`: just the first match
|
62
|
+
`limit`: limits returned documents
|
54
63
|
|
55
64
|
``` ruby
|
56
|
-
Post.
|
57
|
-
Post.find_by_body /^Hi there/i
|
65
|
+
Post.limit(5)
|
58
66
|
```
|
59
67
|
|
60
|
-
`
|
68
|
+
`skip`: offsets returned documents
|
69
|
+
|
70
|
+
``` ruby
|
71
|
+
Post.skip(5)
|
72
|
+
```
|
73
|
+
|
74
|
+
`asc`: sorts ascendingly according to passed keys
|
61
75
|
|
62
76
|
``` ruby
|
63
|
-
Post.
|
64
|
-
```
|
77
|
+
Post.asc(:rank, :title)
|
78
|
+
```
|
79
|
+
|
80
|
+
`desc`: sorts discendingly according to passed keys
|
81
|
+
|
82
|
+
``` ruby
|
83
|
+
Post.desc(:rank, :title)
|
84
|
+
```
|
85
|
+
|
86
|
+
#### Fancy chains
|
87
|
+
``` ruby
|
88
|
+
Post.where(:rank => 5).desc(:title, :body).skip(5).limit(5)
|
89
|
+
```
|
90
|
+
And since it delegates to an enumerable...
|
91
|
+
|
92
|
+
``` ruby
|
93
|
+
Post.where(:rank => 5).count
|
94
|
+
Post.where(:rank => 10).each{ |doc| ... }
|
95
|
+
```
|
96
|
+
|
97
|
+
## ToDos
|
98
|
+
|
99
|
+
* Modifiers on criterias (delete, update, ...)
|
100
|
+
* Default values for attributes
|
101
|
+
* Embedded documents
|
102
|
+
* Keys (_id?)
|
data/lib/tyrion.rb
CHANGED
@@ -6,9 +6,9 @@ require 'tyrion/connection'
|
|
6
6
|
|
7
7
|
require 'tyrion/attributes'
|
8
8
|
require 'tyrion/persistence'
|
9
|
+
require 'tyrion/criteria'
|
9
10
|
require 'tyrion/querying'
|
10
11
|
require 'tyrion/storage'
|
11
|
-
require 'tyrion/components'
|
12
12
|
require 'tyrion/validations'
|
13
13
|
|
14
14
|
require 'tyrion/document'
|
data/lib/tyrion/attributes.rb
CHANGED
@@ -1,42 +1,54 @@
|
|
1
1
|
module Tyrion
|
2
|
-
module Attributes
|
2
|
+
module Attributes
|
3
3
|
def self.included(receiver)
|
4
4
|
receiver.extend ClassMethods
|
5
5
|
receiver.send :include, InstanceMethods
|
6
6
|
end
|
7
|
-
|
7
|
+
|
8
8
|
module ClassMethods
|
9
9
|
def field(name, type = String)
|
10
10
|
name = name.to_s
|
11
|
-
|
11
|
+
|
12
12
|
define_method("#{name}"){ attributes[name] }
|
13
13
|
define_method("#{name}="){ |value| attributes[name] = value }
|
14
14
|
end
|
15
15
|
end
|
16
|
-
|
16
|
+
|
17
17
|
module InstanceMethods
|
18
18
|
attr_reader :attributes
|
19
|
-
|
19
|
+
|
20
|
+
def initialize(attrs = {})
|
21
|
+
@attributes = attrs.stringify_keys
|
22
|
+
end
|
23
|
+
|
20
24
|
def method_missing(name, *args)
|
21
25
|
attr = name.to_s
|
22
26
|
return super unless attributes.has_key? attr.gsub("=", "")
|
23
|
-
|
27
|
+
|
24
28
|
if attr =~ /=$/
|
25
29
|
write_attribute(attr[0..-2], args.shift || raise("BOOM"))
|
26
30
|
else
|
27
31
|
read_attribute(attr)
|
28
32
|
end
|
29
33
|
end
|
30
|
-
|
31
|
-
|
32
|
-
|
34
|
+
|
35
|
+
# Checks for equality.
|
36
|
+
# @example Compare two objects
|
37
|
+
# object == other
|
38
|
+
# @param [Object] The other object to compare with
|
39
|
+
# @return [true, false] True if objects' attributes are the same, false otherwise.
|
40
|
+
def == other
|
41
|
+
return false unless other.is_a?(Tyrion::Document)
|
42
|
+
other.attributes == attributes
|
43
|
+
end
|
44
|
+
|
33
45
|
def read_attribute(name)
|
34
46
|
attributes[name.to_s]
|
35
47
|
end
|
36
|
-
|
48
|
+
|
37
49
|
def write_attribute(name, arg)
|
38
50
|
attributes[name.to_s] = arg
|
39
51
|
end
|
40
52
|
end
|
41
53
|
end
|
42
|
-
end
|
54
|
+
end
|
data/lib/tyrion/connection.rb
CHANGED
@@ -0,0 +1,94 @@
|
|
1
|
+
module Tyrion
|
2
|
+
class Criteria
|
3
|
+
include Enumerable
|
4
|
+
|
5
|
+
attr_reader :klass
|
6
|
+
|
7
|
+
def initialize(storage, klass)
|
8
|
+
@storage = storage
|
9
|
+
@klass = klass
|
10
|
+
@constraints = []
|
11
|
+
end
|
12
|
+
|
13
|
+
def all
|
14
|
+
self
|
15
|
+
end
|
16
|
+
|
17
|
+
def where(params = {})
|
18
|
+
@constraints << [:where, params]
|
19
|
+
self
|
20
|
+
end
|
21
|
+
|
22
|
+
def limit(n)
|
23
|
+
@constraints << [:limit, n]
|
24
|
+
self
|
25
|
+
end
|
26
|
+
|
27
|
+
def skip(n)
|
28
|
+
@constraints << [:skip, n]
|
29
|
+
self
|
30
|
+
end
|
31
|
+
|
32
|
+
def asc(*keys)
|
33
|
+
@constraints << [:sort, [keys, true]]
|
34
|
+
self
|
35
|
+
end
|
36
|
+
|
37
|
+
def desc(*keys)
|
38
|
+
@constraints << [:sort, [keys, false]]
|
39
|
+
self
|
40
|
+
end
|
41
|
+
|
42
|
+
def inspect
|
43
|
+
"<Criteria:0x#{__id__.to_s(16)}\n" +
|
44
|
+
" Model:#{@klass.capitalize},\n" +
|
45
|
+
" Constraints: " + @constraints.inspect[1..-2] + ">"
|
46
|
+
end
|
47
|
+
|
48
|
+
def each(&block)
|
49
|
+
fetch_documents if not @data
|
50
|
+
@data.each{ |c| block.call(c) }
|
51
|
+
end
|
52
|
+
|
53
|
+
def last
|
54
|
+
to_a.last
|
55
|
+
end
|
56
|
+
|
57
|
+
private
|
58
|
+
|
59
|
+
def fetch_documents
|
60
|
+
@data = @storage.dup
|
61
|
+
|
62
|
+
@constraints.each do |con|
|
63
|
+
type, args = con
|
64
|
+
|
65
|
+
case type
|
66
|
+
when :where
|
67
|
+
@data = @data.select do |doc|
|
68
|
+
args.each_pair.map do |k, v|
|
69
|
+
doc.read_attribute(k) == v
|
70
|
+
end.inject(&:&)
|
71
|
+
end
|
72
|
+
when :limit
|
73
|
+
@data = @data.take(args)
|
74
|
+
when :skip
|
75
|
+
@data = Array(@data[args..-1])
|
76
|
+
when :sort
|
77
|
+
keys, dir = args
|
78
|
+
if @data.respond_to? :sort_by!
|
79
|
+
@data.sort_by! do |doc|
|
80
|
+
keys.map{|s| doc.read_attribute(s) }
|
81
|
+
end
|
82
|
+
else
|
83
|
+
@data.sort! do |a, b|
|
84
|
+
first = keys.map{|s| a.read_attribute(s) }
|
85
|
+
second = keys.map{|s| b.read_attribute(s) }
|
86
|
+
first <=> second
|
87
|
+
end
|
88
|
+
end
|
89
|
+
@data.reverse! unless dir
|
90
|
+
end
|
91
|
+
end
|
92
|
+
end
|
93
|
+
end
|
94
|
+
end
|
data/lib/tyrion/document.rb
CHANGED
@@ -1,42 +1,31 @@
|
|
1
1
|
module Tyrion
|
2
|
-
|
2
|
+
|
3
3
|
# Base module to persist objects.
|
4
4
|
module Document
|
5
5
|
def self.included(receiver)
|
6
6
|
receiver.extend ClassMethods
|
7
|
-
|
7
|
+
|
8
8
|
receiver.class_eval do
|
9
|
-
include Tyrion::
|
9
|
+
include Tyrion::Attributes
|
10
|
+
include Tyrion::Querying
|
11
|
+
include Tyrion::Validations
|
12
|
+
include Tyrion::Persistence
|
13
|
+
include Tyrion::Storage
|
10
14
|
include InstanceMethods
|
11
15
|
end
|
12
16
|
end
|
13
|
-
|
17
|
+
|
14
18
|
module ClassMethods
|
15
|
-
|
16
|
-
|
19
|
+
private
|
20
|
+
|
17
21
|
def klass_name
|
18
22
|
to_s.downcase
|
19
23
|
end
|
20
24
|
end
|
21
|
-
|
25
|
+
|
22
26
|
module InstanceMethods
|
23
|
-
|
24
|
-
# Checks for equality.
|
25
|
-
# @example Compare two objects
|
26
|
-
# object == other
|
27
|
-
# @param [Object] The other object to compare with
|
28
|
-
# @return [true, false] True if objects' attributes are the same, false otherwise.
|
29
|
-
def == other
|
30
|
-
other.attributes == attributes
|
31
|
-
end
|
32
|
-
|
33
|
-
def initialize(attrs = {})
|
34
|
-
@attributes = attrs.stringify_keys
|
35
|
-
@new_document = true
|
36
|
-
end
|
37
|
-
|
38
27
|
private
|
39
|
-
|
28
|
+
|
40
29
|
def klass_name
|
41
30
|
self.class.to_s.downcase
|
42
31
|
end
|
data/lib/tyrion/persistence.rb
CHANGED
@@ -4,47 +4,43 @@ module Tyrion
|
|
4
4
|
receiver.extend ClassMethods
|
5
5
|
receiver.send :include, InstanceMethods
|
6
6
|
end
|
7
|
-
|
7
|
+
|
8
8
|
module ClassMethods
|
9
|
-
def create
|
10
|
-
new(
|
11
|
-
attributes.each_pair{ |k, v| n.send(:write_attribute, k.to_s, v) }
|
12
|
-
end
|
13
|
-
end
|
14
|
-
|
15
|
-
def create! attributes = {}
|
16
|
-
create(attributes).tap{ |doc| doc.save }
|
9
|
+
def create(*args)
|
10
|
+
new(*args).tap{ |doc| doc.save }
|
17
11
|
end
|
18
|
-
|
12
|
+
|
19
13
|
def delete_all
|
20
14
|
storage[klass_name].clear
|
21
|
-
|
22
|
-
|
23
|
-
def delete attributes = {}
|
24
|
-
where(attributes).each(&:delete)
|
15
|
+
save_storage(klass_name)
|
25
16
|
end
|
26
17
|
end
|
27
|
-
|
28
|
-
module InstanceMethods
|
18
|
+
|
19
|
+
module InstanceMethods
|
20
|
+
def initialize(*args)
|
21
|
+
super(*args)
|
22
|
+
@persisted = false
|
23
|
+
end
|
24
|
+
|
29
25
|
def save
|
30
26
|
if valid?
|
31
27
|
self.class.storage[klass_name] << self
|
32
28
|
self.class.save_storage klass_name
|
33
|
-
@
|
34
|
-
|
29
|
+
@persisted = true
|
30
|
+
|
35
31
|
true
|
36
32
|
else
|
37
33
|
false
|
38
34
|
end
|
39
35
|
end
|
40
|
-
|
36
|
+
|
41
37
|
def delete
|
42
38
|
self.class.storage[klass_name].delete_if{ |doc| self == doc }
|
43
|
-
self.class.save_storage
|
39
|
+
self.class.save_storage(klass_name)
|
44
40
|
end
|
45
|
-
|
46
|
-
def
|
47
|
-
@
|
41
|
+
|
42
|
+
def persisted?
|
43
|
+
@persisted
|
48
44
|
end
|
49
45
|
end
|
50
46
|
end
|
data/lib/tyrion/querying.rb
CHANGED
@@ -1,50 +1,26 @@
|
|
1
|
+
require 'forwardable'
|
2
|
+
|
1
3
|
module Tyrion
|
2
4
|
module Querying
|
3
5
|
def self.included(receiver)
|
4
6
|
receiver.extend ClassMethods
|
5
7
|
end
|
6
|
-
|
8
|
+
|
7
9
|
module ClassMethods
|
8
|
-
|
9
|
-
|
10
|
+
extend Forwardable
|
11
|
+
|
12
|
+
def_delegators :new_criteria, :all, :where, :first, :last, :limit,
|
13
|
+
:skip, :asc, :desc
|
14
|
+
|
15
|
+
def count
|
16
|
+
storage[klass_name].count
|
10
17
|
end
|
11
|
-
|
12
|
-
|
13
|
-
|
14
|
-
|
15
|
-
|
16
|
-
:=~
|
17
|
-
else
|
18
|
-
:==
|
19
|
-
end
|
20
|
-
|
21
|
-
true if doc.send(:read_attribute, k.to_s).send(operator, v)
|
22
|
-
end.compact
|
23
|
-
|
24
|
-
doc if match.count == attributes.count
|
25
|
-
end.compact
|
26
|
-
end
|
27
|
-
|
28
|
-
def method_missing(method, *args)
|
29
|
-
if method.to_s =~ /^find_by_(.+)$/
|
30
|
-
attr = $1
|
31
|
-
arg = args.shift || raise("Provide at least one argument!")
|
32
|
-
|
33
|
-
all.each do |doc|
|
34
|
-
operator = if arg.is_a? Regexp
|
35
|
-
:=~
|
36
|
-
else
|
37
|
-
:==
|
38
|
-
end
|
39
|
-
|
40
|
-
return doc if doc.attributes[attr].send(operator, arg)
|
41
|
-
end
|
42
|
-
|
43
|
-
nil
|
44
|
-
else
|
45
|
-
super
|
46
|
-
end
|
18
|
+
|
19
|
+
private
|
20
|
+
|
21
|
+
def new_criteria
|
22
|
+
Criteria.new(storage[klass_name], klass_name)
|
47
23
|
end
|
48
24
|
end
|
49
25
|
end
|
50
|
-
end
|
26
|
+
end
|
data/lib/tyrion/storage.rb
CHANGED
@@ -1,38 +1,38 @@
|
|
1
1
|
module Tyrion
|
2
|
-
module Storage
|
2
|
+
module Storage
|
3
3
|
def self.included(receiver)
|
4
4
|
receiver.extend ClassMethods
|
5
|
-
|
6
|
-
receiver.reload unless receiver == Tyrion::
|
5
|
+
|
6
|
+
receiver.reload unless receiver == Tyrion::Document
|
7
7
|
end
|
8
|
-
|
8
|
+
|
9
9
|
module ClassMethods
|
10
10
|
def storage
|
11
11
|
@storage ||= {}
|
12
12
|
end
|
13
|
-
|
13
|
+
|
14
14
|
def reload
|
15
15
|
klass_name = to_s.downcase
|
16
16
|
path = klass_filepath klass_name
|
17
|
-
|
17
|
+
|
18
18
|
if File.exists?(path)
|
19
19
|
raw_file = File.read(path)
|
20
|
-
storage[klass_name] = MultiJson.decode(raw_file).map{ |doc|
|
20
|
+
storage[klass_name] = MultiJson.decode(raw_file).map{ |doc| new(doc) }
|
21
21
|
else
|
22
22
|
storage[klass_name] = []
|
23
23
|
end
|
24
24
|
end
|
25
|
-
|
25
|
+
|
26
26
|
def save_storage klass_name
|
27
27
|
path = klass_filepath klass_name
|
28
|
-
|
28
|
+
|
29
29
|
File.open(path, 'w') do |f|
|
30
30
|
f.puts MultiJson.encode(storage[klass_name].map &:attributes)
|
31
31
|
end
|
32
32
|
end
|
33
|
-
|
33
|
+
|
34
34
|
protected
|
35
|
-
|
35
|
+
|
36
36
|
def klass_filepath klass_name
|
37
37
|
File.join(Connection.path, klass_name + ".json")
|
38
38
|
end
|
data/lib/tyrion/version.rb
CHANGED
metadata
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: tyrion
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 0.
|
4
|
+
version: 0.3.0
|
5
5
|
prerelease:
|
6
6
|
platform: ruby
|
7
7
|
authors:
|
@@ -9,11 +9,11 @@ authors:
|
|
9
9
|
autorequire:
|
10
10
|
bindir: bin
|
11
11
|
cert_chain: []
|
12
|
-
date:
|
12
|
+
date: 2012-01-01 00:00:00.000000000 Z
|
13
13
|
dependencies:
|
14
14
|
- !ruby/object:Gem::Dependency
|
15
15
|
name: multi_json
|
16
|
-
requirement: &
|
16
|
+
requirement: &70231390612120 !ruby/object:Gem::Requirement
|
17
17
|
none: false
|
18
18
|
requirements:
|
19
19
|
- - ! '>='
|
@@ -21,10 +21,10 @@ dependencies:
|
|
21
21
|
version: '0'
|
22
22
|
type: :runtime
|
23
23
|
prerelease: false
|
24
|
-
version_requirements: *
|
24
|
+
version_requirements: *70231390612120
|
25
25
|
- !ruby/object:Gem::Dependency
|
26
26
|
name: json_pure
|
27
|
-
requirement: &
|
27
|
+
requirement: &70231390611420 !ruby/object:Gem::Requirement
|
28
28
|
none: false
|
29
29
|
requirements:
|
30
30
|
- - ! '>='
|
@@ -32,10 +32,10 @@ dependencies:
|
|
32
32
|
version: '0'
|
33
33
|
type: :runtime
|
34
34
|
prerelease: false
|
35
|
-
version_requirements: *
|
35
|
+
version_requirements: *70231390611420
|
36
36
|
- !ruby/object:Gem::Dependency
|
37
37
|
name: activemodel
|
38
|
-
requirement: &
|
38
|
+
requirement: &70231390610660 !ruby/object:Gem::Requirement
|
39
39
|
none: false
|
40
40
|
requirements:
|
41
41
|
- - ~>
|
@@ -43,10 +43,10 @@ dependencies:
|
|
43
43
|
version: 3.1.0
|
44
44
|
type: :runtime
|
45
45
|
prerelease: false
|
46
|
-
version_requirements: *
|
46
|
+
version_requirements: *70231390610660
|
47
47
|
- !ruby/object:Gem::Dependency
|
48
48
|
name: rspec
|
49
|
-
requirement: &
|
49
|
+
requirement: &70231390626300 !ruby/object:Gem::Requirement
|
50
50
|
none: false
|
51
51
|
requirements:
|
52
52
|
- - ! '>='
|
@@ -54,10 +54,21 @@ dependencies:
|
|
54
54
|
version: '0'
|
55
55
|
type: :development
|
56
56
|
prerelease: false
|
57
|
-
version_requirements: *
|
57
|
+
version_requirements: *70231390626300
|
58
|
+
- !ruby/object:Gem::Dependency
|
59
|
+
name: pry
|
60
|
+
requirement: &70231390625480 !ruby/object:Gem::Requirement
|
61
|
+
none: false
|
62
|
+
requirements:
|
63
|
+
- - ! '>='
|
64
|
+
- !ruby/object:Gem::Version
|
65
|
+
version: '0'
|
66
|
+
type: :development
|
67
|
+
prerelease: false
|
68
|
+
version_requirements: *70231390625480
|
58
69
|
- !ruby/object:Gem::Dependency
|
59
70
|
name: simplecov
|
60
|
-
requirement: &
|
71
|
+
requirement: &70231390624840 !ruby/object:Gem::Requirement
|
61
72
|
none: false
|
62
73
|
requirements:
|
63
74
|
- - ! '>='
|
@@ -65,7 +76,7 @@ dependencies:
|
|
65
76
|
version: '0'
|
66
77
|
type: :development
|
67
78
|
prerelease: false
|
68
|
-
version_requirements: *
|
79
|
+
version_requirements: *70231390624840
|
69
80
|
description: Tyrion's goal is to provide a fast (as in _easy to setup_) and dirty
|
70
81
|
unstructured document store.
|
71
82
|
email:
|
@@ -75,8 +86,8 @@ extensions: []
|
|
75
86
|
extra_rdoc_files: []
|
76
87
|
files:
|
77
88
|
- lib/tyrion/attributes.rb
|
78
|
-
- lib/tyrion/components.rb
|
79
89
|
- lib/tyrion/connection.rb
|
90
|
+
- lib/tyrion/criteria.rb
|
80
91
|
- lib/tyrion/document.rb
|
81
92
|
- lib/tyrion/persistence.rb
|
82
93
|
- lib/tyrion/querying.rb
|
data/lib/tyrion/components.rb
DELETED
@@ -1,13 +0,0 @@
|
|
1
|
-
module Tyrion
|
2
|
-
module Components
|
3
|
-
def self.included(receiver)
|
4
|
-
receiver.class_eval do
|
5
|
-
include Tyrion::Attributes
|
6
|
-
include Tyrion::Validations
|
7
|
-
include Tyrion::Persistence
|
8
|
-
include Tyrion::Querying
|
9
|
-
include Tyrion::Storage
|
10
|
-
end
|
11
|
-
end
|
12
|
-
end
|
13
|
-
end
|