plain_record 0.1 → 0.2
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/.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
|