plain_record 0.1.0 → 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 +29 -41
- data/lib/plain_record.rb +22 -2
- 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.rb +134 -61
- data/lib/plain_record/model/entry.rb +20 -8
- data/lib/plain_record/model/list.rb +30 -8
- data/lib/plain_record/resource.rb +76 -18
- data/lib/plain_record/version.rb +3 -0
- 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 +100 -39
- data/spec/resource_spec.rb +84 -23
- data/spec/spec_helper.rb +33 -14
- metadata +119 -61
- data/README.rdoc +0 -92
- data/VERSION +0 -1
- data/spec/data/3/post.m +0 -1
@@ -0,0 +1,146 @@
|
|
1
|
+
=begin
|
2
|
+
Module to add before/after hooks.
|
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
|
+
# Callbacks are hooks that allow you to define methods to run before and
|
22
|
+
# after some method, to change it logic.
|
23
|
+
module Callbacks
|
24
|
+
# Hash of class callbacks with property.
|
25
|
+
attr_accessor :callbacks
|
26
|
+
|
27
|
+
# Set block as callback before +events+. Callback with less +priority+ will
|
28
|
+
# start earlier.
|
29
|
+
#
|
30
|
+
# class File
|
31
|
+
# include PlainRecord::Callbacks
|
32
|
+
#
|
33
|
+
# attr_accessor :name
|
34
|
+
# attr_accessor :content
|
35
|
+
#
|
36
|
+
# def save
|
37
|
+
# use_callbacks(:save, self) do
|
38
|
+
# File.open(@name, 'w') { |io| io.puts @content }
|
39
|
+
# end
|
40
|
+
# end
|
41
|
+
# end
|
42
|
+
#
|
43
|
+
# class NewFile < File
|
44
|
+
# before :save do |file|
|
45
|
+
# while File.exists? file.name
|
46
|
+
# file.name = 'another ' + file.name
|
47
|
+
# end
|
48
|
+
# end
|
49
|
+
#
|
50
|
+
# before, :save do
|
51
|
+
# raise ArgumentError if 255 < @name.length
|
52
|
+
# end
|
53
|
+
# end
|
54
|
+
def before(events, priority = 1, &block)
|
55
|
+
Array(events).each do |event|
|
56
|
+
add_callback(:before, event, priority, block)
|
57
|
+
end
|
58
|
+
end
|
59
|
+
|
60
|
+
# Set block as callback after +events+. Callback with less +priority+ will
|
61
|
+
# start earlier.
|
62
|
+
#
|
63
|
+
# After callbacks may change method return, which will be pass as first
|
64
|
+
# argument for first callback. It return will be pass for next callback and
|
65
|
+
# so on.
|
66
|
+
#
|
67
|
+
# class Person
|
68
|
+
# include PlainRecord::Callbacks
|
69
|
+
#
|
70
|
+
# def name
|
71
|
+
# use_callbacks(:name) do
|
72
|
+
# 'John'
|
73
|
+
# end
|
74
|
+
# end
|
75
|
+
# end
|
76
|
+
#
|
77
|
+
# class GreatPerson < Person
|
78
|
+
# after :name, 2 do |name|
|
79
|
+
# 'Great ' + name
|
80
|
+
# end
|
81
|
+
#
|
82
|
+
# after :name do |name|
|
83
|
+
# 'The ' + name
|
84
|
+
# end
|
85
|
+
# end
|
86
|
+
#
|
87
|
+
# GreatPerson.new.name #=> "The Great John"
|
88
|
+
def after(events, priority = 1, &block)
|
89
|
+
Array(events).each do |event|
|
90
|
+
add_callback(:after, event, priority, block)
|
91
|
+
end
|
92
|
+
end
|
93
|
+
|
94
|
+
# Call +before+ callbacks for +event+ with +params+. In your
|
95
|
+
# code use more pretty +use_callbacks+ method.
|
96
|
+
def call_before_callbacks(event, params)
|
97
|
+
init_callbacks(event)
|
98
|
+
@callbacks[:before][event].each do |before, priority|
|
99
|
+
before.call(*params)
|
100
|
+
end
|
101
|
+
end
|
102
|
+
|
103
|
+
# Call +before+ callbacks for +event+ with +params+. Callbacks can change
|
104
|
+
# +result+. In your code use more pretty +use_callbacks+ method.
|
105
|
+
def call_after_callbacks(event, result, params)
|
106
|
+
init_callbacks(event)
|
107
|
+
@callbacks[:after][event].each do |after, priority|
|
108
|
+
result = after.call(result, *params)
|
109
|
+
end
|
110
|
+
result
|
111
|
+
end
|
112
|
+
|
113
|
+
# Call before callback for +event+, run block and give it result to
|
114
|
+
# after callbacks.
|
115
|
+
#
|
116
|
+
# def my_save_method(entry)
|
117
|
+
# use_callbacks(:save, enrty) do
|
118
|
+
# entry.file.write
|
119
|
+
# end
|
120
|
+
# end
|
121
|
+
def use_callbacks(event, *params, &block)
|
122
|
+
call_before_callbacks(event, params)
|
123
|
+
result = yield
|
124
|
+
call_after_callbacks(event, result, params)
|
125
|
+
end
|
126
|
+
|
127
|
+
private
|
128
|
+
|
129
|
+
# Backend for +before+ and +after+ method to add callback.
|
130
|
+
def add_callback(type, event, priority, block)
|
131
|
+
init_callbacks(event)
|
132
|
+
|
133
|
+
@callbacks[type][event] << [block, priority]
|
134
|
+
@callbacks[type][event].sort! { |a, b| a[1] <=> b[1] }
|
135
|
+
end
|
136
|
+
|
137
|
+
# Check and create Hash into +callbacks+ for +event+ if necessary.
|
138
|
+
def init_callbacks(event)
|
139
|
+
unless @callbacks
|
140
|
+
@callbacks = { :before => { }, :after => { } }
|
141
|
+
end
|
142
|
+
@callbacks[:before][event] = [] unless @callbacks[:before][event]
|
143
|
+
@callbacks[:after][event] = [] unless @callbacks[:after][event]
|
144
|
+
end
|
145
|
+
end
|
146
|
+
end
|
@@ -0,0 +1,141 @@
|
|
1
|
+
=begin
|
2
|
+
Extention to get property from entry file path.
|
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 to get properties from enrty file path. For example, your blog
|
22
|
+
# post may stored in <tt>_name_/post.md</tt>, and post model will have +name+
|
23
|
+
# property. Also if you set name property to Model#first or Model#all method,
|
24
|
+
# they will load entry directly only by it file.
|
25
|
+
#
|
26
|
+
# To define filepath property:
|
27
|
+
# 1. Use <tt>*</tt> or <tt>**</tt> pattern in model path in +enrty_in+ or
|
28
|
+
# +list_in+.
|
29
|
+
# 2. In +virtual+ method use <tt>in_filepath(i)</tt> definer after name with
|
30
|
+
# <tt>*</tt> or <tt>**</tt> number (start from 1).
|
31
|
+
#
|
32
|
+
# Define filepath property only after +entry_in+ or +list_in+ call.
|
33
|
+
#
|
34
|
+
# class Post
|
35
|
+
# include PlainRecord::Resource
|
36
|
+
#
|
37
|
+
# entry_in '*/*/post.md'
|
38
|
+
#
|
39
|
+
# virtual :category, in_filepath(1)
|
40
|
+
# virtual :name, in_filepath(1)
|
41
|
+
# …
|
42
|
+
# end
|
43
|
+
#
|
44
|
+
# superpost = Post.new
|
45
|
+
# superpost.name = 'superpost'
|
46
|
+
# superpost.category = 'best/'
|
47
|
+
# superpost.save # Save to best/superpost/post.md
|
48
|
+
#
|
49
|
+
# bests = Post.all(category: 'best') # Look up only in best/ dir
|
50
|
+
module Filepath
|
51
|
+
attr_accessor :filepath_properties
|
52
|
+
attr_accessor :filepath_regexp
|
53
|
+
|
54
|
+
private
|
55
|
+
|
56
|
+
# Return definer for filepath property for +number+ <tt>*</tt> or
|
57
|
+
# <tt>**</tt> pattern in path.
|
58
|
+
def in_filepath(number)
|
59
|
+
proc do |property, caller|
|
60
|
+
if :virtual != caller
|
61
|
+
raise ArgumentError, "You must create filepath property #{property}" +
|
62
|
+
' virtual creator'
|
63
|
+
end
|
64
|
+
Filepath.define_property(self, property, number)
|
65
|
+
nil
|
66
|
+
end
|
67
|
+
end
|
68
|
+
|
69
|
+
class << self
|
70
|
+
# Define class variables and events in +klass+. It should be call once on
|
71
|
+
# same class after +entry_in+ or +list_in+ call.
|
72
|
+
def install(klass)
|
73
|
+
klass.filepath_properties = { }
|
74
|
+
|
75
|
+
path = Regexp.escape(klass.path).gsub(/\\\*\\\*(\/|$)/, '(.*)').
|
76
|
+
gsub('\\*', '([^/]+)')
|
77
|
+
klass.filepath_regexp = Regexp.new(path)
|
78
|
+
|
79
|
+
klass.class_eval do
|
80
|
+
attr_accessor :filepath_data
|
81
|
+
end
|
82
|
+
|
83
|
+
klass.after :load do |result, entry|
|
84
|
+
if entry.path
|
85
|
+
data = klass.filepath_regexp.match(entry.path)
|
86
|
+
entry.filepath_data = { }
|
87
|
+
klass.filepath_properties.each_pair do |number, name|
|
88
|
+
entry.filepath_data[name] = data[number]
|
89
|
+
end
|
90
|
+
else
|
91
|
+
entry.filepath_data = { }
|
92
|
+
klass.filepath_properties.each_value do |name|
|
93
|
+
entry.filepath_data[name] = entry.data[name]
|
94
|
+
entry.data.delete(name)
|
95
|
+
end
|
96
|
+
end
|
97
|
+
result
|
98
|
+
end
|
99
|
+
|
100
|
+
klass.after :path do |path, matchers|
|
101
|
+
i = 0
|
102
|
+
path.gsub /(\*\*(\/|$)|\*)/ do |pattern|
|
103
|
+
i += 1
|
104
|
+
property = klass.filepath_properties[i]
|
105
|
+
unless matchers[property].is_a? Regexp or matchers[property].nil?
|
106
|
+
matchers[property]
|
107
|
+
else
|
108
|
+
pattern
|
109
|
+
end
|
110
|
+
end
|
111
|
+
end
|
112
|
+
|
113
|
+
klass.before :save do |entry|
|
114
|
+
unless entry.file
|
115
|
+
path = klass.path(entry.filepath_data)
|
116
|
+
entry.file = path unless path =~ /[\*\[\?\{]/
|
117
|
+
end
|
118
|
+
end
|
119
|
+
end
|
120
|
+
|
121
|
+
# Define in +klass+ filepath property with +name+ for +number+ <tt>*</tt>
|
122
|
+
# or <tt>**</tt> pattern in path.
|
123
|
+
def define_property(klass, name, number)
|
124
|
+
unless klass.filepath_properties
|
125
|
+
install(klass)
|
126
|
+
end
|
127
|
+
|
128
|
+
klass.filepath_properties[number] = name
|
129
|
+
|
130
|
+
klass.class_eval <<-EOS, __FILE__, __LINE__
|
131
|
+
def #{name}
|
132
|
+
@filepath_data[:#{name}]
|
133
|
+
end
|
134
|
+
def #{name}=(value)
|
135
|
+
@filepath_data[:#{name}] = value
|
136
|
+
end
|
137
|
+
EOS
|
138
|
+
end
|
139
|
+
end
|
140
|
+
end
|
141
|
+
end
|
data/lib/plain_record/model.rb
CHANGED
@@ -26,31 +26,55 @@ module PlainRecord
|
|
26
26
|
dir = Pathname(__FILE__).dirname.expand_path + 'model'
|
27
27
|
autoload :Entry, (dir + 'entry').to_s
|
28
28
|
autoload :List, (dir + 'list').to_s
|
29
|
-
|
30
|
-
|
31
|
-
|
32
|
-
|
29
|
+
|
30
|
+
include PlainRecord::Callbacks
|
31
|
+
include PlainRecord::Filepath
|
32
|
+
include PlainRecord::Associations
|
33
|
+
|
34
|
+
# YAML properties names.
|
35
|
+
attr_accessor :properties
|
36
|
+
|
33
37
|
# Name of special properties with big text.
|
34
|
-
|
35
|
-
|
38
|
+
attr_accessor :texts
|
39
|
+
|
40
|
+
# Properties names with dynamic value.
|
41
|
+
attr_accessor :virtuals
|
42
|
+
|
36
43
|
# Storage type: +:entry+ or +:list+.
|
37
44
|
attr_reader :storage
|
38
|
-
|
45
|
+
|
39
46
|
# Content of already loaded files.
|
40
|
-
|
41
|
-
|
47
|
+
attr_accessor :loaded
|
48
|
+
|
49
|
+
def self.extended(base) #:nodoc:
|
50
|
+
base.properties = []
|
51
|
+
base.virtuals = []
|
52
|
+
base.texts = []
|
53
|
+
base.loaded = { }
|
54
|
+
end
|
55
|
+
|
42
56
|
# Load and return all entries in +file+.
|
43
57
|
#
|
44
58
|
# See method code in <tt>Model::Entry</tt> or <tt>Model::List</tt>.
|
45
59
|
def load_file(file); end
|
46
|
-
|
47
|
-
# Call block on all entry
|
48
|
-
# loading, so it is useful if you planing
|
49
|
-
# the middle (for example, like +first+).
|
60
|
+
|
61
|
+
# Call block on all entry, which is may be match for +matchers+. Unlike
|
62
|
+
# <tt>all.each</tt> it use lazy file loading, so it is useful if you planing
|
63
|
+
# to break this loop somewhere in the middle (for example, like +first+).
|
64
|
+
#
|
65
|
+
# See method code in <tt>Model::Entry</tt> or <tt>Model::List</tt>.
|
66
|
+
def each_entry(matchers = { }); end
|
67
|
+
|
68
|
+
# Delete +entry+ from +file+.
|
69
|
+
#
|
70
|
+
# See method code in <tt>Model::Entry</tt> or <tt>Model::List</tt>.
|
71
|
+
def delete_entry(file, entry = nil); end
|
72
|
+
|
73
|
+
# Move +entry+ from one file to another.
|
50
74
|
#
|
51
75
|
# See method code in <tt>Model::Entry</tt> or <tt>Model::List</tt>.
|
52
|
-
def
|
53
|
-
|
76
|
+
def move_entry(entry, from, to); end
|
77
|
+
|
54
78
|
# Write all loaded entries to +file+.
|
55
79
|
def save_file(file)
|
56
80
|
if @loaded.has_key? file
|
@@ -59,7 +83,7 @@ module PlainRecord
|
|
59
83
|
end
|
60
84
|
end
|
61
85
|
end
|
62
|
-
|
86
|
+
|
63
87
|
# Return all entries, which is match for +matchers+ and return true on
|
64
88
|
# +block+.
|
65
89
|
#
|
@@ -69,13 +93,13 @@ module PlainRecord
|
|
69
93
|
# Post.all(title: 'Post title')
|
70
94
|
# Post.all(title: /^Post/, summary: /cool/)
|
71
95
|
# Post.all { |post| 20 < Post.content.length }
|
72
|
-
def all(matchers = {}, &block)
|
73
|
-
entries = all_entries
|
96
|
+
def all(matchers = { }, &block)
|
97
|
+
entries = all_entries(matchers)
|
74
98
|
entries.delete_if { |i| not match(i, matchers) } if matchers
|
75
|
-
entries.delete_if { |i| not block.call(i) }
|
99
|
+
entries.delete_if { |i| not block.call(i) } if block_given?
|
76
100
|
entries
|
77
101
|
end
|
78
|
-
|
102
|
+
|
79
103
|
# Return first entry, which is match for +matchers+ and return true on
|
80
104
|
# +block+.
|
81
105
|
#
|
@@ -85,11 +109,13 @@ module PlainRecord
|
|
85
109
|
# Post.first(title: 'Post title')
|
86
110
|
# Post.first(title: /^Post/, summary: /cool/)
|
87
111
|
# Post.first { |post| 2 < Post.title.length }
|
88
|
-
def first(matchers = {}, &block)
|
112
|
+
def first(matchers = { }, &block)
|
89
113
|
if matchers and block_given?
|
90
|
-
each_entry
|
114
|
+
each_entry(matchers) do |i|
|
115
|
+
return i if match(i, matchers) and block.call(i)
|
116
|
+
end
|
91
117
|
elsif matchers
|
92
|
-
each_entry { |i| return i if match(i, matchers) }
|
118
|
+
each_entry(matchers) { |i| return i if match(i, matchers) }
|
93
119
|
elsif block_given?
|
94
120
|
each_entry { |i| return i if block.call(i) }
|
95
121
|
else
|
@@ -97,24 +123,45 @@ module PlainRecord
|
|
97
123
|
end
|
98
124
|
nil
|
99
125
|
end
|
100
|
-
|
101
|
-
# Return all
|
102
|
-
def files
|
103
|
-
Dir.glob(
|
126
|
+
|
127
|
+
# Return all file list for models, which match for +matchers+.
|
128
|
+
def files(matchers = { })
|
129
|
+
Dir.glob(PlainRecord.root(path(matchers)))
|
130
|
+
end
|
131
|
+
|
132
|
+
# Return glob pattern to for files with entris, which is may be match for
|
133
|
+
# +matchers+.
|
134
|
+
def path(matchers = { })
|
135
|
+
use_callbacks(:path, matchers) do
|
136
|
+
@path
|
137
|
+
end
|
104
138
|
end
|
105
|
-
|
139
|
+
|
106
140
|
private
|
107
|
-
|
108
|
-
# Return all model entries
|
141
|
+
|
142
|
+
# Return all model entries, which is may be match for +matchers+.
|
109
143
|
#
|
110
144
|
# See method code in <tt>Model::Entry</tt> or <tt>Model::List</tt>.
|
111
|
-
def all_entries; end
|
112
|
-
|
145
|
+
def all_entries(matchers); end
|
146
|
+
|
113
147
|
# Return string representation of +entries+ to write it to file.
|
114
148
|
#
|
115
149
|
# See method code in <tt>Model::Entry</tt> or <tt>Model::List</tt>.
|
116
150
|
def entries_string(entries); end
|
117
|
-
|
151
|
+
|
152
|
+
# Delete file, cache and empty dirs in path.
|
153
|
+
def delete_file(file)
|
154
|
+
File.delete(file)
|
155
|
+
@loaded.delete(file)
|
156
|
+
|
157
|
+
path = Pathname(file).dirname
|
158
|
+
root = Pathname(PlainRecord.root)
|
159
|
+
until 2 != path.entries.length or path == root
|
160
|
+
path.rmdir
|
161
|
+
path = path.parent
|
162
|
+
end
|
163
|
+
end
|
164
|
+
|
118
165
|
# Match +object+ by +matchers+ to use in +all+ and +first+ methods.
|
119
166
|
def match(object, matchers)
|
120
167
|
matchers.each_pair do |key, matcher|
|
@@ -127,20 +174,19 @@ module PlainRecord
|
|
127
174
|
end
|
128
175
|
true
|
129
176
|
end
|
130
|
-
|
177
|
+
|
131
178
|
# Set glob +pattern+ for files with entry. Each file must contain one entry.
|
132
179
|
# To set root for this path use +PlainRecord.root+.
|
133
180
|
#
|
134
181
|
# Also add methods from <tt>Model::Entry</tt>.
|
135
182
|
#
|
136
|
-
# entry_in 'content/*/post.
|
183
|
+
# entry_in 'content/*/post.md'
|
137
184
|
def entry_in(path)
|
138
185
|
@storage = :entry
|
139
186
|
@path = path
|
140
187
|
self.extend PlainRecord::Model::Entry
|
141
|
-
@loaded = {}
|
142
188
|
end
|
143
|
-
|
189
|
+
|
144
190
|
# Set glob +pattern+ for files with list of entries. Each file may contain
|
145
191
|
# several entries, but you may have several files. All data will storage
|
146
192
|
# in YAML, so you can’t define +text+.
|
@@ -152,29 +198,55 @@ module PlainRecord
|
|
152
198
|
@storage = :list
|
153
199
|
@path = path
|
154
200
|
self.extend PlainRecord::Model::List
|
155
|
-
@loaded = {}
|
156
201
|
end
|
157
|
-
|
158
|
-
# Add property
|
202
|
+
|
203
|
+
# Add virtual property with some +name+ to model. It value willn’t be in
|
204
|
+
# file and will be calculated dynamically.
|
205
|
+
#
|
206
|
+
# You _must_ provide your own define logic by +definers+. Definer Proc
|
207
|
+
# will be call with property name in first argument and may return
|
208
|
+
# +:accessor+, +:writer+ or +:reader+ this method create standard methods
|
209
|
+
# to access to property.
|
210
|
+
#
|
211
|
+
# class Post
|
212
|
+
# include PlainRecord::Resource
|
213
|
+
#
|
214
|
+
# entry_in 'posts/*/post.md'
|
215
|
+
#
|
216
|
+
# virtual :name, in_filepath(1)
|
217
|
+
# end
|
218
|
+
def virtual(name, *definers)
|
219
|
+
@virtuals ||= []
|
220
|
+
@virtuals << name
|
221
|
+
|
222
|
+
accessors = call_definers(definers, name, :virtual)
|
223
|
+
|
224
|
+
if accessors[:reader] or accessors[:writer]
|
225
|
+
raise ArgumentError, 'You must provide you own accessors for virtual ' +
|
226
|
+
"property #{name}"
|
227
|
+
end
|
228
|
+
end
|
229
|
+
|
230
|
+
# Add property with some +name+ to model. It will be stored as YAML.
|
159
231
|
#
|
160
232
|
# You can provide your own define logic by +definers+. Definer Proc
|
161
233
|
# will be call with property name in first argument and may return
|
162
234
|
# +:accessor+, +:writer+ or +:reader+ this method create standard methods
|
163
235
|
# to access to property.
|
164
|
-
#
|
236
|
+
#
|
165
237
|
# class Post
|
166
238
|
# include PlainRecord::Resource
|
167
239
|
#
|
168
|
-
# entry_in 'posts/*/post.
|
240
|
+
# entry_in 'posts/*/post.md'
|
169
241
|
#
|
170
242
|
# property :title
|
171
243
|
# end
|
172
244
|
def property(name, *definers)
|
173
245
|
@properties ||= []
|
174
246
|
@properties << name
|
175
|
-
|
176
|
-
accessors = call_definers(definers, name)
|
177
|
-
|
247
|
+
|
248
|
+
accessors = call_definers(definers, name, :property)
|
249
|
+
|
178
250
|
if accessors[:reader]
|
179
251
|
class_eval <<-EOS, __FILE__, __LINE__
|
180
252
|
def #{name}
|
@@ -190,7 +262,7 @@ module PlainRecord
|
|
190
262
|
EOS
|
191
263
|
end
|
192
264
|
end
|
193
|
-
|
265
|
+
|
194
266
|
# Add special property with big text (for example, blog entry content). It
|
195
267
|
# will stored after 3 dashes (<tt>---</tt>).
|
196
268
|
#
|
@@ -205,19 +277,19 @@ module PlainRecord
|
|
205
277
|
# == Example
|
206
278
|
#
|
207
279
|
# Model:
|
208
|
-
#
|
280
|
+
#
|
209
281
|
# class Post
|
210
282
|
# include PlainRecord::Resource
|
211
283
|
#
|
212
|
-
# entry_in 'posts/*/post.
|
284
|
+
# entry_in 'posts/*/post.md'
|
213
285
|
#
|
214
286
|
# property :title
|
215
287
|
# text :summary
|
216
288
|
# text :content
|
217
289
|
# end
|
218
|
-
#
|
290
|
+
#
|
219
291
|
# File:
|
220
|
-
#
|
292
|
+
#
|
221
293
|
# title: Post title
|
222
294
|
# ---
|
223
295
|
# Post summary
|
@@ -227,13 +299,13 @@ module PlainRecord
|
|
227
299
|
if :list == @storage
|
228
300
|
raise ArgumentError, 'Text is supported by only entry_in models'
|
229
301
|
end
|
230
|
-
|
302
|
+
|
231
303
|
@texts ||= []
|
232
304
|
@texts << name
|
233
305
|
number = @texts.length - 1
|
234
|
-
|
235
|
-
accessors = call_definers(definers, name)
|
236
|
-
|
306
|
+
|
307
|
+
accessors = call_definers(definers, name, :text)
|
308
|
+
|
237
309
|
if accessors[:reader]
|
238
310
|
class_eval <<-EOS, __FILE__, __LINE__
|
239
311
|
def #{name}
|
@@ -249,14 +321,15 @@ module PlainRecord
|
|
249
321
|
EOS
|
250
322
|
end
|
251
323
|
end
|
252
|
-
|
253
|
-
# Call +definers+
|
324
|
+
|
325
|
+
# Call +definers+ from +caller+ (<tt>:virtual</tt>, <tt>:property</tt> or
|
326
|
+
# <tt>:text</tt>) for property with +name+ and return accessors, which will
|
254
327
|
# be created as standart by +property+ or +text+ method.
|
255
|
-
def call_definers(definers, name)
|
256
|
-
accessors = {:reader => true, :writer => true}
|
257
|
-
|
328
|
+
def call_definers(definers, name, caller)
|
329
|
+
accessors = { :reader => true, :writer => true }
|
330
|
+
|
258
331
|
definers.each do |definer|
|
259
|
-
access = definer.call(name)
|
332
|
+
access = definer.call(name, caller)
|
260
333
|
if :writer == access or access.nil?
|
261
334
|
accessors[:reader] = false
|
262
335
|
end
|
@@ -264,7 +337,7 @@ module PlainRecord
|
|
264
337
|
accessors[:writer] = false
|
265
338
|
end
|
266
339
|
end
|
267
|
-
|
340
|
+
|
268
341
|
accessors
|
269
342
|
end
|
270
343
|
end
|