ygg 0.0.1 → 0.0.2
Sign up to get free protection for your applications and to get access to all the features.
- data/README.rdoc +57 -0
- data/lib/ygg.rb +3 -222
- data/lib/ygg/base.rb +224 -0
- data/lib/ygg/version.rb +1 -1
- metadata +4 -2
data/README.rdoc
ADDED
@@ -0,0 +1,57 @@
|
|
1
|
+
= Ygg
|
2
|
+
|
3
|
+
Lightweight YAML persistence solution with sofisticated ORM-class models and relationships.
|
4
|
+
|
5
|
+
== Installation
|
6
|
+
|
7
|
+
gem install ygg
|
8
|
+
|
9
|
+
== Usage
|
10
|
+
|
11
|
+
require "ygg"
|
12
|
+
|
13
|
+
class User < Ygg::Model
|
14
|
+
attr_accessor :name, :pass
|
15
|
+
end
|
16
|
+
|
17
|
+
class Article < Ygg::Model
|
18
|
+
attr_accessor :title, :content
|
19
|
+
end
|
20
|
+
|
21
|
+
Ygg.load "data.yaml"
|
22
|
+
|
23
|
+
user = User.create :index => "johnr",
|
24
|
+
:name => "John Robertson",
|
25
|
+
:pass => "secret"
|
26
|
+
|
27
|
+
article = Article.create :index => "first-ygg"
|
28
|
+
article.title = "Using Ygg for the first time"
|
29
|
+
article.content = "The API is inspired in classic ORMs like ActiveRecord"
|
30
|
+
|
31
|
+
user >> article # This declaration creations a relationships between the user and the article
|
32
|
+
|
33
|
+
Ygg.save
|
34
|
+
|
35
|
+
And then for obtaining all articles belonging to "johnr"
|
36
|
+
|
37
|
+
# require and Model declarations here
|
38
|
+
|
39
|
+
Ygg.load "data.yaml"
|
40
|
+
|
41
|
+
puts User.get("johnr") >= Article
|
42
|
+
|
43
|
+
Further documentation on functions will be added when the core reaches some stability.
|
44
|
+
|
45
|
+
= License
|
46
|
+
|
47
|
+
(The MIT License)
|
48
|
+
|
49
|
+
Copyright © 2011:
|
50
|
+
|
51
|
+
* Xavier Via (http://germino.com.ar)
|
52
|
+
|
53
|
+
Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the ‘Software’), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions:
|
54
|
+
|
55
|
+
The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software.
|
56
|
+
|
57
|
+
THE SOFTWARE IS PROVIDED ‘AS IS’, WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
data/lib/ygg.rb
CHANGED
@@ -1,224 +1,5 @@
|
|
1
|
-
require "
|
2
|
-
require "singleton"
|
1
|
+
require "ygg/base"
|
3
2
|
|
4
|
-
|
3
|
+
include Ygg::Delegator
|
5
4
|
|
6
|
-
|
7
|
-
def self.load(file)
|
8
|
-
Ygg::DSL.instance.eagerly file
|
9
|
-
end
|
10
|
-
|
11
|
-
def self.save
|
12
|
-
Ygg::DSL.instance.save
|
13
|
-
end
|
14
|
-
|
15
|
-
def self.rel(*args)
|
16
|
-
Ygg::DSL.instance.rel *args
|
17
|
-
end
|
18
|
-
|
19
|
-
module Delegator
|
20
|
-
def delegate(target, *methods)
|
21
|
-
methods.each do |method|
|
22
|
-
self.class.send :define_method, method do |*args, &block|
|
23
|
-
target.send method, *args, &block
|
24
|
-
end
|
25
|
-
end
|
26
|
-
end
|
27
|
-
end
|
28
|
-
|
29
|
-
class Relationship
|
30
|
-
attr_accessor :source
|
31
|
-
attr_accessor :target
|
32
|
-
|
33
|
-
def initialize(source, target)
|
34
|
-
@source = source
|
35
|
-
@target = target
|
36
|
-
|
37
|
-
short = Ygg::DSL.instance.data
|
38
|
-
short[:relationship] ||= []
|
39
|
-
short[:relationship] << self
|
40
|
-
end
|
41
|
-
end
|
42
|
-
|
43
|
-
class DSL
|
44
|
-
include Singleton
|
45
|
-
|
46
|
-
attr_accessor :data
|
47
|
-
attr_accessor :source
|
48
|
-
|
49
|
-
def eagerly(source)
|
50
|
-
@source = source
|
51
|
-
if File.exists? @source
|
52
|
-
@data = YAML.load_file @source
|
53
|
-
else
|
54
|
-
@data = {}
|
55
|
-
end
|
56
|
-
end
|
57
|
-
|
58
|
-
def save # Maybe get it better
|
59
|
-
source = @source if @source
|
60
|
-
unless source
|
61
|
-
raise "There's already a file named 'ygg.yaml'." if File.exists? "ygg.yaml"
|
62
|
-
source = "ygg.yaml"
|
63
|
-
end
|
64
|
-
|
65
|
-
File.open source, "w" do |file|
|
66
|
-
file.write YAML.dump @data
|
67
|
-
end
|
68
|
-
end
|
69
|
-
|
70
|
-
def record(model, id)
|
71
|
-
model_sym = model.to_s.downcase.to_sym
|
72
|
-
@data[model_sym][id]
|
73
|
-
end
|
74
|
-
|
75
|
-
def add(resource, son)
|
76
|
-
Ygg::Relationship.new resource, son
|
77
|
-
end
|
78
|
-
|
79
|
-
def _source(relation, resource, type = nil)
|
80
|
-
if relation.source == resource
|
81
|
-
if type == :all
|
82
|
-
return relation.target
|
83
|
-
elsif relation.target.class == type
|
84
|
-
return relation.target
|
85
|
-
end
|
86
|
-
end
|
87
|
-
return false
|
88
|
-
end
|
89
|
-
|
90
|
-
def _target(relation, resource, type = nil)
|
91
|
-
if relation.target == resource
|
92
|
-
if type == :all
|
93
|
-
return relation.source
|
94
|
-
elsif relation.source.class == type
|
95
|
-
return relation.source
|
96
|
-
end
|
97
|
-
end
|
98
|
-
return false
|
99
|
-
end
|
100
|
-
|
101
|
-
def rel(resource, options = {})
|
102
|
-
options[:as] ||= :any
|
103
|
-
options[:type] ||= :all
|
104
|
-
result = []
|
105
|
-
@data[:relationship].each do |relation|
|
106
|
-
args = [relation, resource, options[:type]]
|
107
|
-
case options[:as]
|
108
|
-
when :any
|
109
|
-
if res = _source(*args)
|
110
|
-
result << res
|
111
|
-
elsif res = _target(*args)
|
112
|
-
result << res
|
113
|
-
end
|
114
|
-
when :source
|
115
|
-
if res = _source(*args)
|
116
|
-
result << res
|
117
|
-
end
|
118
|
-
when :target
|
119
|
-
if res = _target(*args)
|
120
|
-
result << res
|
121
|
-
end
|
122
|
-
end
|
123
|
-
end
|
124
|
-
result
|
125
|
-
end
|
126
|
-
end
|
127
|
-
|
128
|
-
class Model
|
129
|
-
# Define methods for retrieving relations and
|
130
|
-
# creating relations
|
131
|
-
|
132
|
-
attr_accessor :index
|
133
|
-
|
134
|
-
# Instance methods
|
135
|
-
def >>(resource)
|
136
|
-
Ygg::DSL.instance.add self, resource
|
137
|
-
end
|
138
|
-
|
139
|
-
def <<(resource)
|
140
|
-
Ygg::DSL.instance.add resource, self
|
141
|
-
end
|
142
|
-
|
143
|
-
def %(type)
|
144
|
-
if type == :all
|
145
|
-
Ygg::DSL.instance.rel self
|
146
|
-
elsif type.kind_of? Class
|
147
|
-
Ygg::DSL.instance.rel self, :type => type
|
148
|
-
end
|
149
|
-
end
|
150
|
-
|
151
|
-
def >=(type)
|
152
|
-
if type == :all
|
153
|
-
Ygg::DSL.instance.rel self, :as => :source
|
154
|
-
elsif type.kind_of? Class
|
155
|
-
Ygg::DSL.instance.rel self, :as => :source, :type => type
|
156
|
-
end
|
157
|
-
end
|
158
|
-
|
159
|
-
def <=(type)
|
160
|
-
if type == :all
|
161
|
-
Ygg::DSL.instance.rel self, :as => :target
|
162
|
-
elsif type.kind_of? Class
|
163
|
-
Ygg::DSL.instance.rel self, :as => :target, :type => type
|
164
|
-
end
|
165
|
-
end
|
166
|
-
|
167
|
-
# Register this datus in the @data
|
168
|
-
def initialize(index)
|
169
|
-
Ygg::DSL.instance.data ||= {}
|
170
|
-
Ygg::DSL.instance.data[Ygg.symbolize(self)] ||= {}
|
171
|
-
if index.kind_of? String
|
172
|
-
Ygg::DSL.instance.data[Ygg.symbolize(self)][index] = self
|
173
|
-
@index = index
|
174
|
-
elsif index.kind_of? Hash
|
175
|
-
raise "Index missing" unless index[:index]
|
176
|
-
index.each do |key, value|
|
177
|
-
self.send "#{key}=".to_sym, value
|
178
|
-
end
|
179
|
-
end
|
180
|
-
end
|
181
|
-
|
182
|
-
# Class methods
|
183
|
-
def self.create(data)
|
184
|
-
if data.kind_of? Hash
|
185
|
-
raise "Index missing" unless data[:index]
|
186
|
-
index = data[:index]
|
187
|
-
elsif data.kind_of? String
|
188
|
-
index = data
|
189
|
-
end
|
190
|
-
instance = self.new index
|
191
|
-
|
192
|
-
if data.kind_of? Hash
|
193
|
-
data.each do |key, value|
|
194
|
-
instance.send "#{key}=".to_sym, value
|
195
|
-
end
|
196
|
-
end
|
197
|
-
Ygg::DSL.instance.save
|
198
|
-
instance
|
199
|
-
end
|
200
|
-
|
201
|
-
def self.all
|
202
|
-
Ygg::DSL.instance.data[self.to_s.downcase.to_sym] ||= {}
|
203
|
-
end
|
204
|
-
|
205
|
-
def self.get(index)
|
206
|
-
Ygg::DSL.instance.record(self, index)
|
207
|
-
end
|
208
|
-
|
209
|
-
def self.related_to(datus, as = nil)
|
210
|
-
options = {}
|
211
|
-
options[:type] = self
|
212
|
-
options[:as] = as if as
|
213
|
-
Ygg::DSL.instance.rel datus, options
|
214
|
-
end
|
215
|
-
end
|
216
|
-
|
217
|
-
def self.symbolize(object)
|
218
|
-
object.class.to_s.downcase.to_sym
|
219
|
-
end
|
220
|
-
end
|
221
|
-
|
222
|
-
#include Ygg::Delegator
|
223
|
-
|
224
|
-
#delegate Ygg::DSL, :save, :record, :relateds
|
5
|
+
delegate Ygg, :save, :data, :load
|
data/lib/ygg/base.rb
ADDED
@@ -0,0 +1,224 @@
|
|
1
|
+
require "yaml"
|
2
|
+
|
3
|
+
module Ygg
|
4
|
+
@@data
|
5
|
+
@@source
|
6
|
+
|
7
|
+
def self.load(source = "ygg.yaml", &block)
|
8
|
+
|
9
|
+
if source.kind_of? Symbol
|
10
|
+
if File.exists? "#{source}.yml"
|
11
|
+
source = "#{source}.yml"
|
12
|
+
else
|
13
|
+
source = "#{source}.yaml"
|
14
|
+
end
|
15
|
+
end
|
16
|
+
@@source ||= source if source == "ygg.yaml"
|
17
|
+
@@source = source if source != "ygg.yaml"
|
18
|
+
|
19
|
+
@@data = {}
|
20
|
+
@@data = YAML.load_file @@source if File.exists? @@source
|
21
|
+
|
22
|
+
@@data.each do |key, model|
|
23
|
+
model.each do |resource|
|
24
|
+
resource.loading! if resource.respond_to? :loading!
|
25
|
+
end if model.kind_of? Array
|
26
|
+
end
|
27
|
+
|
28
|
+
if block; yield; self.save; self.close; end
|
29
|
+
end
|
30
|
+
|
31
|
+
def self.save
|
32
|
+
@@data ||= {}
|
33
|
+
@@data.each do |key, model|
|
34
|
+
model.each do |resource|
|
35
|
+
resource._finalize! if resource.respond_to? :_finalize!
|
36
|
+
end if model.kind_of? Array
|
37
|
+
end
|
38
|
+
|
39
|
+
File.open @@source, "w" do |file|
|
40
|
+
file.write YAML.dump(@@data)
|
41
|
+
end
|
42
|
+
end
|
43
|
+
|
44
|
+
def self.close; @@data = nil; @@source; end
|
45
|
+
|
46
|
+
def self.data; @@data ||= {}; end
|
47
|
+
|
48
|
+
def self.symbolize(res)
|
49
|
+
return res.to_s.downcase.to_sym if res.kind_of? Class
|
50
|
+
res.class.to_s.downcase.to_sym
|
51
|
+
end
|
52
|
+
|
53
|
+
class Model
|
54
|
+
@@validator = [/(.*)/, [:index]]
|
55
|
+
|
56
|
+
def initialize(*args)
|
57
|
+
Ygg.data[Ygg.symbolize(self)] ||= []
|
58
|
+
Ygg.data[Ygg.symbolize(self)] << self
|
59
|
+
@created_at ||= Time.now if respond_to? :created_at
|
60
|
+
@updated_at ||= Time.now if respond_to? :updated_at
|
61
|
+
|
62
|
+
if data = args.shift
|
63
|
+
if data.kind_of? Hash
|
64
|
+
data.each {|k,v| self.send(:"#{k}=", v) }
|
65
|
+
elsif data.kind_of? String
|
66
|
+
validator = self.class.validator
|
67
|
+
if matches = data.match(validator[0])
|
68
|
+
validator[1].each_index do |i|
|
69
|
+
self.send :"_#{validator[1][i]}=", matches[i+1]
|
70
|
+
end
|
71
|
+
else
|
72
|
+
raise Ygg::InvalidInitializor, "#{data} does not match the validation regexp for #{self.class}"
|
73
|
+
end
|
74
|
+
end
|
75
|
+
end
|
76
|
+
end
|
77
|
+
|
78
|
+
# Instance methods : Defaults
|
79
|
+
def _finalize!; finalize! if respond_to? :finalize!; end
|
80
|
+
def _loading!; loading! if respond_to? :loading!; end
|
81
|
+
def destroy
|
82
|
+
Ygg.data[Ygg.symbolize(self)].delete self
|
83
|
+
# Destroy all Links
|
84
|
+
end
|
85
|
+
def _id
|
86
|
+
Ygg.data[Ygg.symbolize(self)].index self
|
87
|
+
end
|
88
|
+
|
89
|
+
# Instance methods : Links
|
90
|
+
def &(res)
|
91
|
+
Link.new :source => self, :target => res
|
92
|
+
end
|
93
|
+
def >(arg); targets arg; end
|
94
|
+
def <(arg); sources arg; end
|
95
|
+
def *(arg); links arg; end
|
96
|
+
def links(arg = nil); targets(arg) + sources(arg); end
|
97
|
+
|
98
|
+
def targets(arg = nil) # Test
|
99
|
+
links = Link.all.by_source self
|
100
|
+
return [] if links.kind_of? Array and links.empty?
|
101
|
+
links = [links] unless links.kind_of? Array
|
102
|
+
result = []
|
103
|
+
links.each do |link|
|
104
|
+
if arg
|
105
|
+
if arg.kind_of? Class
|
106
|
+
result << link.target if link.target.class == arg
|
107
|
+
else
|
108
|
+
result << link.target if link.info == arg
|
109
|
+
end
|
110
|
+
else; result << link.target; end
|
111
|
+
end
|
112
|
+
result
|
113
|
+
end
|
114
|
+
|
115
|
+
def sources(arg = nil) # Test
|
116
|
+
links = Link.all.by_target self
|
117
|
+
return [] if links.kind_of? Array and links.empty?
|
118
|
+
links = [links] unless links.kind_of? Array
|
119
|
+
result = []
|
120
|
+
links.each do |link|
|
121
|
+
if arg
|
122
|
+
if arg.kind_of? Class
|
123
|
+
result << link.source if link.source.class == arg
|
124
|
+
else
|
125
|
+
result << link.source if link.info == arg
|
126
|
+
end
|
127
|
+
else; result << link.source; end
|
128
|
+
end
|
129
|
+
result
|
130
|
+
end
|
131
|
+
|
132
|
+
def link_with(resource) # Test
|
133
|
+
links = Link.all.by_source self
|
134
|
+
return nil if links.kind_of? Array and links.empty?
|
135
|
+
links = [links] unless links.kind_of? Array
|
136
|
+
result = []
|
137
|
+
links.each do |link|
|
138
|
+
result << link if link.target == resource
|
139
|
+
end
|
140
|
+
|
141
|
+
return false if result.length == 0
|
142
|
+
return result[0] if result.length == 1
|
143
|
+
return result
|
144
|
+
end
|
145
|
+
|
146
|
+
# Class methods : Shorthands
|
147
|
+
def self.all; Ygg.data[Ygg.symbolize(self)]; end
|
148
|
+
def self.validator; @@validator; end
|
149
|
+
|
150
|
+
# Class methods : Metamethods
|
151
|
+
def self.timestamps!; attr_accessor :created_at, :updated_at; end
|
152
|
+
|
153
|
+
def self.key(*args)
|
154
|
+
attr_reader *args
|
155
|
+
args.each do |arg|
|
156
|
+
class_eval <<-_EOE
|
157
|
+
def _#{arg}=(v)
|
158
|
+
@#{arg} = v
|
159
|
+
end
|
160
|
+
|
161
|
+
def _#{arg}_hook=(v)
|
162
|
+
@updated_at = Time.now if respond_to? :updated_at
|
163
|
+
end
|
164
|
+
|
165
|
+
def #{arg}=(v)
|
166
|
+
self._#{arg}_hook = v
|
167
|
+
self._#{arg} = v
|
168
|
+
end
|
169
|
+
_EOE
|
170
|
+
end
|
171
|
+
end
|
172
|
+
end
|
173
|
+
|
174
|
+
class Link < Model
|
175
|
+
attr_accessor :source, :target, :info
|
176
|
+
def &(data); @info = data; end
|
177
|
+
end
|
178
|
+
|
179
|
+
class InvalidInitializor < RuntimeError; end
|
180
|
+
|
181
|
+
module Delegator
|
182
|
+
def delegate(target, *methods)
|
183
|
+
methods.each do |method|
|
184
|
+
self.class.send :define_method, method do |*a, &b|
|
185
|
+
target.send method, *a, &b
|
186
|
+
end
|
187
|
+
end
|
188
|
+
end
|
189
|
+
end
|
190
|
+
end
|
191
|
+
|
192
|
+
class Array
|
193
|
+
def method_missing(x, *args)
|
194
|
+
if matches = x.to_s.match(/^sort_by_(.+)$/)
|
195
|
+
key = matches[1].to_sym
|
196
|
+
self.sort_by { |x| x.send(key) }
|
197
|
+
|
198
|
+
elsif matches = x.to_s.match(/^by_(.+)$/) # Extend with regexps
|
199
|
+
key = matches[1].to_sym
|
200
|
+
|
201
|
+
if to_find = args.shift
|
202
|
+
result = []
|
203
|
+
self.each do |res|
|
204
|
+
if res.respond_to? key
|
205
|
+
datus = res.send key
|
206
|
+
result << res if datus == to_find
|
207
|
+
end
|
208
|
+
end
|
209
|
+
return [] if result.length == 0
|
210
|
+
return result[0] if result.length == 1
|
211
|
+
return result
|
212
|
+
|
213
|
+
else
|
214
|
+
self.send(:"sort_by_#{key}")[0] unless empty?
|
215
|
+
end
|
216
|
+
|
217
|
+
return []
|
218
|
+
end
|
219
|
+
end
|
220
|
+
end
|
221
|
+
|
222
|
+
#include Ygg::Delegator
|
223
|
+
|
224
|
+
#delegate Ygg, :save, :data, :load
|
data/lib/ygg/version.rb
CHANGED
metadata
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: ygg
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 0.0.
|
4
|
+
version: 0.0.2
|
5
5
|
prerelease:
|
6
6
|
platform: ruby
|
7
7
|
authors:
|
@@ -9,7 +9,7 @@ authors:
|
|
9
9
|
autorequire:
|
10
10
|
bindir: bin
|
11
11
|
cert_chain: []
|
12
|
-
date: 2011-
|
12
|
+
date: 2011-06-03 00:00:00.000000000 -03:00
|
13
13
|
default_executable:
|
14
14
|
dependencies: []
|
15
15
|
description: Pure Ruby persistence solution
|
@@ -21,8 +21,10 @@ extra_rdoc_files: []
|
|
21
21
|
files:
|
22
22
|
- .gitignore
|
23
23
|
- Gemfile
|
24
|
+
- README.rdoc
|
24
25
|
- Rakefile
|
25
26
|
- lib/ygg.rb
|
27
|
+
- lib/ygg/base.rb
|
26
28
|
- lib/ygg/version.rb
|
27
29
|
- ygg.gemspec
|
28
30
|
has_rdoc: true
|