plain_record 0.1 → 0.2
Sign up to get free protection for your applications and to get access to all the features.
- data/.gitignore +8 -0
- data/.rspec +1 -0
- data/.travis.yml +6 -0
- data/.yardopts +4 -0
- data/ChangeLog +9 -0
- data/Gemfile +6 -0
- data/Gemfile.lock +24 -0
- data/LICENSE +3 -4
- data/README.md +124 -0
- data/Rakefile +27 -50
- data/lib/plain_record/association_proxy.rb +59 -0
- data/lib/plain_record/associations.rb +271 -0
- data/lib/plain_record/callbacks.rb +146 -0
- data/lib/plain_record/filepath.rb +141 -0
- data/lib/plain_record/model/entry.rb +17 -9
- data/lib/plain_record/model/list.rb +22 -9
- data/lib/plain_record/model.rb +119 -64
- data/lib/plain_record/resource.rb +72 -19
- data/lib/plain_record/version.rb +1 -1
- data/lib/plain_record.rb +21 -2
- data/plain_record.gemspec +30 -0
- data/spec/associations_spec.rb +142 -0
- data/spec/callbacks_spec.rb +59 -0
- data/spec/data/1/comments.yml +5 -0
- data/spec/data/1/{post.m → post.md} +0 -0
- data/spec/data/2/{post.m → post.md} +1 -1
- data/spec/data/3/post.md +4 -0
- data/spec/data/authors/extern.yml +2 -2
- data/spec/data/authors/intern.yml +4 -4
- data/spec/data/best/4/post.md +1 -0
- data/spec/filepath_spec.rb +53 -0
- data/spec/model_spec.rb +90 -42
- data/spec/resource_spec.rb +70 -27
- data/spec/spec_helper.rb +33 -14
- metadata +122 -70
- data/README.rdoc +0 -96
- data/spec/data/3/post.m +0 -1
data/.gitignore
ADDED
data/.rspec
ADDED
@@ -0,0 +1 @@
|
|
1
|
+
--format documentation --colour
|
data/.travis.yml
ADDED
data/.yardopts
ADDED
data/ChangeLog
ADDED
data/Gemfile
ADDED
data/Gemfile.lock
ADDED
@@ -0,0 +1,24 @@
|
|
1
|
+
GEM
|
2
|
+
remote: http://rubygems.org/
|
3
|
+
specs:
|
4
|
+
diff-lcs (1.1.3)
|
5
|
+
rake (0.9.2.2)
|
6
|
+
redcarpet (2.1.1)
|
7
|
+
rspec (2.10.0)
|
8
|
+
rspec-core (~> 2.10.0)
|
9
|
+
rspec-expectations (~> 2.10.0)
|
10
|
+
rspec-mocks (~> 2.10.0)
|
11
|
+
rspec-core (2.10.0)
|
12
|
+
rspec-expectations (2.10.0)
|
13
|
+
diff-lcs (~> 1.1.3)
|
14
|
+
rspec-mocks (2.10.1)
|
15
|
+
yard (0.8.1)
|
16
|
+
|
17
|
+
PLATFORMS
|
18
|
+
ruby
|
19
|
+
|
20
|
+
DEPENDENCIES
|
21
|
+
rake
|
22
|
+
redcarpet
|
23
|
+
rspec
|
24
|
+
yard
|
data/LICENSE
CHANGED
@@ -1,4 +1,4 @@
|
|
1
|
-
|
1
|
+
GNU LESSER GENERAL PUBLIC LICENSE
|
2
2
|
Version 3, 29 June 2007
|
3
3
|
|
4
4
|
Copyright (C) 2007 Free Software Foundation, Inc. <http://fsf.org/>
|
@@ -10,7 +10,7 @@
|
|
10
10
|
the terms and conditions of version 3 of the GNU General Public
|
11
11
|
License, supplemented by the additional permissions listed below.
|
12
12
|
|
13
|
-
0. Additional Definitions.
|
13
|
+
0. Additional Definitions.
|
14
14
|
|
15
15
|
As used herein, "this License" refers to version 3 of the GNU Lesser
|
16
16
|
General Public License, and the "GNU GPL" refers to version 3 of the GNU
|
@@ -111,7 +111,7 @@ the following:
|
|
111
111
|
a copy of the Library already present on the user's computer
|
112
112
|
system, and (b) will operate properly with a modified version
|
113
113
|
of the Library that is interface-compatible with the Linked
|
114
|
-
Version.
|
114
|
+
Version.
|
115
115
|
|
116
116
|
e) Provide Installation Information, but only if you would otherwise
|
117
117
|
be required to provide such information under section 6 of the
|
@@ -163,4 +163,3 @@ whether future versions of the GNU Lesser General Public License shall
|
|
163
163
|
apply, that proxy's public statement of acceptance of any version is
|
164
164
|
permanent authorization for you to choose that version for the
|
165
165
|
Library.
|
166
|
-
|
data/README.md
ADDED
@@ -0,0 +1,124 @@
|
|
1
|
+
# Plain Record
|
2
|
+
|
3
|
+
Plaint Record is a data persistence, which use human editable and readable plain
|
4
|
+
text files. It’s ideal for static generated sites, like blog or homepage.
|
5
|
+
|
6
|
+
If you want to write another static website generator, you don’t need to write
|
7
|
+
another file parser – you can use Plain Record.
|
8
|
+
|
9
|
+
## How To
|
10
|
+
|
11
|
+
For example we will create simple blog storage with posts and comments.
|
12
|
+
|
13
|
+
1. Add Plain Record to your application Gemfile:
|
14
|
+
|
15
|
+
```ruby
|
16
|
+
gem "plain_record"
|
17
|
+
```
|
18
|
+
|
19
|
+
2. Set storage root – dir, which will contain all data files:
|
20
|
+
|
21
|
+
```ruby
|
22
|
+
PlainRecord.root = 'data/'
|
23
|
+
```
|
24
|
+
|
25
|
+
3. Create Post class, include `Plain::Resource` module, set glob pattern
|
26
|
+
to posts files and define properties:
|
27
|
+
|
28
|
+
```ruby
|
29
|
+
class Post
|
30
|
+
include Plain::Resource
|
31
|
+
|
32
|
+
entry_in '*/post.md'
|
33
|
+
|
34
|
+
virtual :name, in_filepath(1)
|
35
|
+
virtual :comments, many(Comment)
|
36
|
+
property :title
|
37
|
+
property :tags
|
38
|
+
text :summary
|
39
|
+
text :content
|
40
|
+
end
|
41
|
+
```
|
42
|
+
|
43
|
+
4. Create new post file `data/first/post.md`. Properties will be saved as
|
44
|
+
YAML and text will be placed as plain text, which is separated by 3 dashes:
|
45
|
+
|
46
|
+
```
|
47
|
+
title: My first post
|
48
|
+
tags: test, first
|
49
|
+
---
|
50
|
+
It is short post summary.
|
51
|
+
---
|
52
|
+
And this is big big post text.
|
53
|
+
In several lines.
|
54
|
+
```
|
55
|
+
|
56
|
+
5. Also you can use files with list of entries. For example, comments:
|
57
|
+
|
58
|
+
```ruby
|
59
|
+
class Comment
|
60
|
+
include Plain::Resource
|
61
|
+
|
62
|
+
list_in '*/comments.yml'
|
63
|
+
|
64
|
+
virtual :post_name, in_filepath(1)
|
65
|
+
virtual :post, one(Post)
|
66
|
+
property :author
|
67
|
+
property :comment
|
68
|
+
end
|
69
|
+
```
|
70
|
+
You can’t use text fields in list files.
|
71
|
+
6. List files is a just YAML array. For example, `data/first/comments.yml`:
|
72
|
+
|
73
|
+
<pre><code>\- author: Anonymous
|
74
|
+
comment: I like it!
|
75
|
+
\- author: Friend
|
76
|
+
comment: You first post it shit.</pre></code>
|
77
|
+
|
78
|
+
7. Get all post:
|
79
|
+
|
80
|
+
```ruby
|
81
|
+
Post.all # will return array with our first post
|
82
|
+
```
|
83
|
+
|
84
|
+
8. Get specify enrties:
|
85
|
+
|
86
|
+
```ruby
|
87
|
+
Comment.all(author: 'Anonymous')
|
88
|
+
Post.all(title: /first/)
|
89
|
+
Post.all { |i| i.tags.length == 2 }
|
90
|
+
```
|
91
|
+
|
92
|
+
9. To get one entry use `first` method, which also can take matchers. You can
|
93
|
+
access for properties and text by methods with same name:
|
94
|
+
|
95
|
+
```ruby
|
96
|
+
post = Post.first(title: /first/)
|
97
|
+
post.file #=> "data/first/post.md"
|
98
|
+
post.name #=> "first"
|
99
|
+
post.title #=> "My first post"
|
100
|
+
post.tags #=> ["test", "first"]
|
101
|
+
post.summary #=> "It is short post summary."
|
102
|
+
```
|
103
|
+
|
104
|
+
10. You can also change and save entries:
|
105
|
+
|
106
|
+
```ruby
|
107
|
+
post.title = 'First post'
|
108
|
+
post.save
|
109
|
+
```
|
110
|
+
|
111
|
+
11. And delete it (with empty dirs in it file path):
|
112
|
+
|
113
|
+
```ruby
|
114
|
+
post.destroy
|
115
|
+
```
|
116
|
+
|
117
|
+
## License
|
118
|
+
|
119
|
+
Plain Record is licensed under the GNU Lesser General Public License version 3.
|
120
|
+
See the LICENSE file or http://www.gnu.org/licenses/lgpl.html.
|
121
|
+
|
122
|
+
## Author
|
123
|
+
|
124
|
+
Andrey “A.I.” Sitnik <andrey@sitnik.ru>
|
data/Rakefile
CHANGED
@@ -1,57 +1,34 @@
|
|
1
1
|
# encoding: utf-8
|
2
|
-
gem 'rspec'
|
3
|
-
require 'spec/rake/spectask'
|
4
|
-
require 'rake/rdoctask'
|
5
|
-
require 'rake/gempackagetask'
|
6
|
-
|
7
|
-
Spec::Rake::SpecTask.new('spec') do |t|
|
8
|
-
t.spec_opts = ['--format', 'specdoc', '--colour']
|
9
|
-
t.spec_files = Dir['spec/**/*_spec.rb'].sort
|
10
|
-
end
|
11
2
|
|
12
|
-
|
13
|
-
|
14
|
-
|
15
|
-
|
16
|
-
|
17
|
-
|
18
|
-
|
19
|
-
rdoc.options << '--inline-source'
|
3
|
+
require 'rubygems'
|
4
|
+
|
5
|
+
begin
|
6
|
+
require 'bundler/setup'
|
7
|
+
Bundler::GemHelper.install_tasks
|
8
|
+
rescue LoadError
|
9
|
+
puts "Bundler not available. Install it with: gem install bundler"
|
20
10
|
end
|
21
11
|
|
22
|
-
require File.join(File.dirname(__FILE__), 'lib
|
23
|
-
|
24
|
-
|
25
|
-
|
26
|
-
|
27
|
-
|
28
|
-
|
29
|
-
|
30
|
-
|
31
|
-
Plain Record is a data persistence, which use human editable and readable
|
32
|
-
plain text files. It’s ideal for static generated sites, like blog or
|
33
|
-
homepage.
|
34
|
-
DESC
|
35
|
-
|
36
|
-
s.files = FileList[
|
37
|
-
'lib/**/*',
|
38
|
-
'spec/**/*',
|
39
|
-
'Rakefile',
|
40
|
-
'LICENSE',
|
41
|
-
'README.rdoc']
|
42
|
-
s.test_files = FileList['spec/**/*']
|
43
|
-
s.extra_rdoc_files = ['README.rdoc', 'LICENSE']
|
44
|
-
s.require_path = 'lib'
|
45
|
-
s.has_rdoc = true
|
46
|
-
s.rdoc_options << '--title "Plain Record"' << '--main README.rdoc' <<
|
47
|
-
'--charset=utf-8' << '--all' << '--inline-source'
|
48
|
-
|
49
|
-
s.author = 'Andrey "A.I." Sitnik'
|
50
|
-
s.email = 'andrey@sitnik.ru'
|
51
|
-
s.homepage = 'http://github.com/ai/plain_record'
|
52
|
-
s.rubyforge_project = 'plainrecord'
|
12
|
+
require File.join(File.dirname(__FILE__), 'lib/plain_record/version')
|
13
|
+
|
14
|
+
require 'rspec/core/rake_task'
|
15
|
+
|
16
|
+
RSpec::Core::RakeTask.new
|
17
|
+
|
18
|
+
require 'yard'
|
19
|
+
YARD::Rake::YardocTask.new do |yard|
|
20
|
+
yard.options << "--title='Plain Record #{PlainRecord::VERSION}'"
|
53
21
|
end
|
54
22
|
|
55
|
-
|
56
|
-
|
23
|
+
task :clobber_doc do
|
24
|
+
rm_r 'doc' rescue nil
|
25
|
+
rm_r '.yardoc' rescue nil
|
26
|
+
end
|
27
|
+
task :clobber_package do
|
28
|
+
rm_r 'pkg' rescue nil
|
57
29
|
end
|
30
|
+
|
31
|
+
desc 'Delete all generated files'
|
32
|
+
task :clobber => [:clobber_package, :clobber_doc]
|
33
|
+
|
34
|
+
task :default => :spec
|
@@ -0,0 +1,59 @@
|
|
1
|
+
=begin
|
2
|
+
Storage for one-to-many virtual associations.
|
3
|
+
|
4
|
+
Copyright (C) 2009 Andrey “A.I.” Sitnik <andrey@sitnik.ru>
|
5
|
+
|
6
|
+
This program is free software: you can redistribute it and/or modify
|
7
|
+
it under the terms of the GNU Lesser General Public License as published by
|
8
|
+
the Free Software Foundation, either version 3 of the License, or
|
9
|
+
(at your option) any later version.
|
10
|
+
|
11
|
+
This program is distributed in the hope that it will be useful,
|
12
|
+
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
13
|
+
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
14
|
+
GNU Lesser General Public License for more details.
|
15
|
+
|
16
|
+
You should have received a copy of the GNU Lesser General Public License
|
17
|
+
along with this program. If not, see <http://www.gnu.org/licenses/>.
|
18
|
+
=end
|
19
|
+
|
20
|
+
module PlainRecord
|
21
|
+
# Storage for one-to-many virtual associations. When new object will pushed,
|
22
|
+
# proxy change it mapped properties.
|
23
|
+
class AssociationProxy < Array
|
24
|
+
# Model with association.
|
25
|
+
attr_accessor :owner
|
26
|
+
|
27
|
+
# Associations property name.
|
28
|
+
attr_accessor :property
|
29
|
+
|
30
|
+
# Create proxy for one-to-many virtual associations +property+ in +owner+
|
31
|
+
# and put +array+ into it.
|
32
|
+
def self.link(array, owner, property)
|
33
|
+
proxy = new(array, owner, property)
|
34
|
+
proxy.each { |i| proxy.link(i) }
|
35
|
+
proxy
|
36
|
+
end
|
37
|
+
|
38
|
+
# Create proxy for one-to-many virtual associations +property+ in +owner+
|
39
|
+
# with +array+ in value.
|
40
|
+
def initialize(array, owner, property)
|
41
|
+
@owner = owner
|
42
|
+
@property = property
|
43
|
+
super(array)
|
44
|
+
end
|
45
|
+
|
46
|
+
# Push new item in association and change it property by association map.
|
47
|
+
def <<(obj)
|
48
|
+
link(obj)
|
49
|
+
super(obj)
|
50
|
+
end
|
51
|
+
|
52
|
+
# Change properties in +obj+ by association map.
|
53
|
+
def link(obj)
|
54
|
+
@owner.class.association_maps[@property].each do |from, to|
|
55
|
+
obj.send(from.to_s + '=', @owner.send(to))
|
56
|
+
end
|
57
|
+
end
|
58
|
+
end
|
59
|
+
end
|
@@ -0,0 +1,271 @@
|
|
1
|
+
=begin
|
2
|
+
Extention to store or have link to another model.
|
3
|
+
|
4
|
+
Copyright (C) 2009 Andrey “A.I.” Sitnik <andrey@sitnik.ru>
|
5
|
+
|
6
|
+
This program is free software: you can redistribute it and/or modify
|
7
|
+
it under the terms of the GNU Lesser General Public License as published by
|
8
|
+
the Free Software Foundation, either version 3 of the License, or
|
9
|
+
(at your option) any later version.
|
10
|
+
|
11
|
+
This program is distributed in the hope that it will be useful,
|
12
|
+
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
13
|
+
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
14
|
+
GNU Lesser General Public License for more details.
|
15
|
+
|
16
|
+
You should have received a copy of the GNU Lesser General Public License
|
17
|
+
along with this program. If not, see <http://www.gnu.org/licenses/>.
|
18
|
+
=end
|
19
|
+
|
20
|
+
module PlainRecord
|
21
|
+
# Extention for model to store or have link to another model. There is two
|
22
|
+
# type of association.
|
23
|
+
#
|
24
|
+
# == Virtual property
|
25
|
+
# In +virtual+ method this definer create only _link_ to another model. When
|
26
|
+
# you try to use this virtual property, model will find association object by
|
27
|
+
# rules in +map+.
|
28
|
+
#
|
29
|
+
# Rules in +map+ is only Hash with model properties in key and association
|
30
|
+
# properties in value. For example, if model contain +name+ property and
|
31
|
+
# association must have +post_name+ with same value, +map+ will be
|
32
|
+
# <tt>{ :name => :post_name }</tt>.
|
33
|
+
#
|
34
|
+
# If you didn’t set +map+ definer will try to find it automatically:
|
35
|
+
# it will find in model and association class all property pairs, what have
|
36
|
+
# name like +property+ → <tt>model</tt>_<tt>property</tt>. For example,
|
37
|
+
# if model +Post+ have property +name+ and +Comment+ have +post_name+, you
|
38
|
+
# may not set +map+ – definer will find it automatically.
|
39
|
+
#
|
40
|
+
# class Review
|
41
|
+
# include PlainRecord::Resource
|
42
|
+
#
|
43
|
+
# entry_in 'reviews/*.md'
|
44
|
+
#
|
45
|
+
# virtual :author, one(Author)
|
46
|
+
# property :author_login
|
47
|
+
# text :review
|
48
|
+
# end
|
49
|
+
#
|
50
|
+
# class Author
|
51
|
+
# include PlainRecord::Resource
|
52
|
+
#
|
53
|
+
# list_in 'authors.yml'
|
54
|
+
#
|
55
|
+
# virtual :reviews, many(Review)
|
56
|
+
# property :login
|
57
|
+
# property :name
|
58
|
+
# end
|
59
|
+
#
|
60
|
+
# == Real property
|
61
|
+
# If you will use this definer in +property+ method, association object data
|
62
|
+
# will store in you model file. For example model:
|
63
|
+
#
|
64
|
+
# class Movie
|
65
|
+
# include PlainRecord::Resource
|
66
|
+
#
|
67
|
+
# property :title
|
68
|
+
# property :genre
|
69
|
+
# property :release_year
|
70
|
+
# end
|
71
|
+
#
|
72
|
+
# class Tag
|
73
|
+
# include PlainRecord::Resource
|
74
|
+
# property :name
|
75
|
+
# end
|
76
|
+
#
|
77
|
+
# class Review
|
78
|
+
# include PlainRecord::Resource
|
79
|
+
#
|
80
|
+
# entry_in 'reviews/*.md'
|
81
|
+
#
|
82
|
+
# property :author
|
83
|
+
# property :movie, one(Movie)
|
84
|
+
# property :tags, many(Tag)
|
85
|
+
# text :review
|
86
|
+
# end
|
87
|
+
#
|
88
|
+
# will be store as:
|
89
|
+
#
|
90
|
+
# author: John Smith
|
91
|
+
# movie:
|
92
|
+
# title: Watchmen
|
93
|
+
# genre: action
|
94
|
+
# release_year: 2009
|
95
|
+
# tags:
|
96
|
+
# - name: Great movies
|
97
|
+
# - name: Comics
|
98
|
+
# ---
|
99
|
+
# Movie is great!
|
100
|
+
module Associations
|
101
|
+
# Hash with map for virtual associations.
|
102
|
+
attr_accessor :association_maps
|
103
|
+
|
104
|
+
# Hash with cached values for virtual associations.
|
105
|
+
attr_accessor :association_cache
|
106
|
+
|
107
|
+
private
|
108
|
+
|
109
|
+
# Return definer for one-to-one association with +klass+. Have different
|
110
|
+
# logic in +property+ and +virtual+ methods.
|
111
|
+
def one(klass, map = { })
|
112
|
+
proc do |property, caller|
|
113
|
+
if :property == caller
|
114
|
+
Associations.define_real_one(self, property, klass)
|
115
|
+
:accessor
|
116
|
+
elsif :virtual == caller
|
117
|
+
map = Associations.map(self, klass, "#{property}_") if map.empty?
|
118
|
+
Associations.define_link_one(self, klass, property, map)
|
119
|
+
nil
|
120
|
+
else
|
121
|
+
raise ArgumentError, "You couldn't create association property" +
|
122
|
+
" #{property} by text creator"
|
123
|
+
end
|
124
|
+
end
|
125
|
+
end
|
126
|
+
|
127
|
+
# Return definer for one-to-many or many-to-many association with +klass+.
|
128
|
+
# Have different login in +property+ and +virtual+ methods.
|
129
|
+
def many(klass, prefix = nil, map = { })
|
130
|
+
proc do |property, caller|
|
131
|
+
if :property == caller
|
132
|
+
Associations.define_real_many(self, property, klass)
|
133
|
+
:accessor
|
134
|
+
elsif :virtual == caller
|
135
|
+
unless prefix
|
136
|
+
prefix = self.to_s.gsub!(/[A-Z]/, '_\0')[1..-1].downcase + '_'
|
137
|
+
end
|
138
|
+
map = Associations.map(klass, self, prefix) if map.empty?
|
139
|
+
Associations.define_link_many(self, klass, property, map)
|
140
|
+
nil
|
141
|
+
else
|
142
|
+
raise ArgumentError, "You couldn't create association property" +
|
143
|
+
" #{property} by text creator"
|
144
|
+
end
|
145
|
+
end
|
146
|
+
end
|
147
|
+
|
148
|
+
class << self
|
149
|
+
# Define, that +property+ in +klass+ contain in file data from +model+.
|
150
|
+
def define_real_one(klass, property, model)
|
151
|
+
name = property.to_s
|
152
|
+
klass.after :load do |result, entry|
|
153
|
+
entry.data[name] = model.new(entry.file, entry.data[name])
|
154
|
+
result
|
155
|
+
end
|
156
|
+
klass.before :save do |entry|
|
157
|
+
model.call_before_callbacks(:save, [entry.data[name]])
|
158
|
+
entry.data[name] = entry.data[name]
|
159
|
+
end
|
160
|
+
klass.after :save do |result, entry|
|
161
|
+
entry.data[name] = model.new(entry.file, entry.data[name])
|
162
|
+
model.call_after_callbacks(:save, nil, [entry.data[name]])
|
163
|
+
result
|
164
|
+
end
|
165
|
+
end
|
166
|
+
|
167
|
+
# Define, that +property+ in +klass+ contain in file array of data from
|
168
|
+
# +model+ objects.
|
169
|
+
def define_real_many(klass, property, model)
|
170
|
+
name = property.to_s
|
171
|
+
klass.after :load do |result, entry|
|
172
|
+
if entry.data[name].is_a? Enumerable
|
173
|
+
entry.data[name].map! { |i| model.new(entry.file, i) }
|
174
|
+
else
|
175
|
+
entry.data[name] = []
|
176
|
+
end
|
177
|
+
result
|
178
|
+
end
|
179
|
+
klass.before :save do |entry|
|
180
|
+
if entry.data[name].empty?
|
181
|
+
entry.data.delete(name)
|
182
|
+
else
|
183
|
+
entry.data[name].map! do |obj|
|
184
|
+
model.call_before_callbacks(:save, [obj])
|
185
|
+
obj.data
|
186
|
+
end
|
187
|
+
end
|
188
|
+
end
|
189
|
+
klass.after :save do |result, entry|
|
190
|
+
entry.data[name].map! { |i| model.new(entry.file, i) }
|
191
|
+
entry.data[name].each do |i|
|
192
|
+
model.call_after_callbacks(:save, nil, [i])
|
193
|
+
end
|
194
|
+
result
|
195
|
+
end
|
196
|
+
end
|
197
|
+
|
198
|
+
# Find properties pairs in +from+ and +to+ models, witch is like
|
199
|
+
# <tt>prefix</tt>_<tt>from</tt> → +to+.
|
200
|
+
#
|
201
|
+
# For example, if Comment contain +post_name+ property and Post contain
|
202
|
+
# +name+:
|
203
|
+
#
|
204
|
+
# Associations.map(Comment, Post, :post) #=> { :post_name => :name }
|
205
|
+
def map(from, to, prefix)
|
206
|
+
from_fields = (from.properties + from.virtuals).map { |i| i.to_s }
|
207
|
+
mapped = { }
|
208
|
+
(to.properties + to.virtuals).each do |to_field|
|
209
|
+
from_field = prefix + to_field.to_s
|
210
|
+
if from_fields.include? from_field
|
211
|
+
mapped[from_field.to_sym] = to_field
|
212
|
+
end
|
213
|
+
end
|
214
|
+
mapped
|
215
|
+
end
|
216
|
+
|
217
|
+
# Define that virtual property +name+ in +klass+ contain link to +model+
|
218
|
+
# witch is finded by +map+.
|
219
|
+
def define_link_one(klass, model, name, map)
|
220
|
+
klass.association_cache ||= { }
|
221
|
+
klass.association_maps ||= { }
|
222
|
+
klass.association_maps[name] = map
|
223
|
+
|
224
|
+
klass.class_eval <<-EOS, __FILE__, __LINE__
|
225
|
+
def #{name}
|
226
|
+
unless self.class.association_cache[:#{name}]
|
227
|
+
search = Hash[
|
228
|
+
self.class.association_maps[:#{name}].map do |from, to|
|
229
|
+
[to, send(from)]
|
230
|
+
end]
|
231
|
+
self.class.association_cache[:#{name}] = #{model}.first(search)
|
232
|
+
end
|
233
|
+
self.class.association_cache[:#{name}]
|
234
|
+
end
|
235
|
+
def #{name}=(value)
|
236
|
+
self.class.association_maps[:#{name}].each do |from, to|
|
237
|
+
value.send(to.to_s + '=', send(from))
|
238
|
+
end
|
239
|
+
self.class.association_cache[:#{name}] = value
|
240
|
+
end
|
241
|
+
EOS
|
242
|
+
end
|
243
|
+
|
244
|
+
# Define that virtual property +name+ in +klass+ contain links to +model+
|
245
|
+
# witch are finded by +map+.
|
246
|
+
def define_link_many(klass, model, name, map)
|
247
|
+
klass.association_cache ||= { }
|
248
|
+
klass.association_maps ||= { }
|
249
|
+
klass.association_maps[name] = map
|
250
|
+
|
251
|
+
klass.class_eval <<-EOS, __FILE__, __LINE__
|
252
|
+
def #{name}
|
253
|
+
unless self.class.association_cache[:#{name}]
|
254
|
+
search = Hash[
|
255
|
+
self.class.association_maps[:#{name}].map do |from, to|
|
256
|
+
[from, send(to)]
|
257
|
+
end]
|
258
|
+
self.class.association_cache[:#{name}] = AssociationProxy.new(
|
259
|
+
#{model}.all(search), self, :#{name})
|
260
|
+
end
|
261
|
+
self.class.association_cache[:#{name}]
|
262
|
+
end
|
263
|
+
def #{name}=(values)
|
264
|
+
self.class.association_cache[:#{name}] = AssociationProxy.link(
|
265
|
+
values, self, :#{name})
|
266
|
+
end
|
267
|
+
EOS
|
268
|
+
end
|
269
|
+
end
|
270
|
+
end
|
271
|
+
end
|