plain_record 0.2 → 0.3
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/.travis.yml +0 -1
- data/ChangeLog +11 -0
- data/Gemfile +2 -0
- data/Gemfile.lock +10 -1
- data/README.md +18 -13
- data/lib/plain_record/association_proxy.rb +14 -13
- data/lib/plain_record/associations.rb +80 -80
- data/lib/plain_record/callbacks.rb +3 -2
- data/lib/plain_record/default.rb +56 -0
- data/lib/plain_record/extra/git.rb +97 -0
- data/lib/plain_record/extra/i18n.rb +157 -0
- data/lib/plain_record/filepath.rb +31 -39
- data/lib/plain_record/model/entry.rb +2 -1
- data/lib/plain_record/model/list.rb +2 -1
- data/lib/plain_record/model.rb +117 -88
- data/lib/plain_record/resource.rb +10 -9
- data/lib/plain_record/type.rb +86 -0
- data/lib/plain_record/version.rb +1 -1
- data/lib/plain_record.rb +17 -2
- data/plain_record.gemspec +3 -1
- data/spec/associations_spec.rb +12 -8
- data/spec/data/2/comments.yml +2 -0
- data/spec/data/2/post.md +1 -1
- data/spec/default_spec.rb +17 -0
- data/spec/filepath_spec.rb +10 -10
- data/spec/git_spec.rb +55 -0
- data/spec/i18n_spec.rb +167 -0
- data/spec/model_spec.rb +71 -30
- data/spec/plain_record_spec.rb +28 -0
- data/spec/resource_spec.rb +3 -3
- data/spec/spec_helper.rb +10 -24
- data/spec/type_spec.rb +129 -0
- metadata +47 -15
data/.travis.yml
CHANGED
data/ChangeLog
CHANGED
@@ -1,3 +1,14 @@
|
|
1
|
+
== 0.3 (Chlorine)
|
2
|
+
* Use modules to use super in filters.
|
3
|
+
* Add filter to i18n support.
|
4
|
+
* Add filters to get updated and created time from git.
|
5
|
+
* Add filters to set field type.
|
6
|
+
* Add filter to set default field value.
|
7
|
+
* Add alternate hash syntax to define filters.
|
8
|
+
* Set deault data root in Rails project.
|
9
|
+
* Fix association cache.
|
10
|
+
* Allow to set Pathname in data root.
|
11
|
+
|
1
12
|
== 0.2 (Smallpox)
|
2
13
|
* Add associations.
|
3
14
|
* Add special syntax for virtual properties.
|
data/Gemfile
CHANGED
data/Gemfile.lock
CHANGED
@@ -1,14 +1,21 @@
|
|
1
|
+
GIT
|
2
|
+
remote: git://github.com/ai/r18n.git
|
3
|
+
revision: ca6933926eb955344eeb982de35f492c76a38b1a
|
4
|
+
specs:
|
5
|
+
r18n-core (0.4.14)
|
6
|
+
|
1
7
|
GEM
|
2
8
|
remote: http://rubygems.org/
|
3
9
|
specs:
|
4
10
|
diff-lcs (1.1.3)
|
11
|
+
i18n (0.6.0)
|
5
12
|
rake (0.9.2.2)
|
6
13
|
redcarpet (2.1.1)
|
7
14
|
rspec (2.10.0)
|
8
15
|
rspec-core (~> 2.10.0)
|
9
16
|
rspec-expectations (~> 2.10.0)
|
10
17
|
rspec-mocks (~> 2.10.0)
|
11
|
-
rspec-core (2.10.
|
18
|
+
rspec-core (2.10.1)
|
12
19
|
rspec-expectations (2.10.0)
|
13
20
|
diff-lcs (~> 1.1.3)
|
14
21
|
rspec-mocks (2.10.1)
|
@@ -18,6 +25,8 @@ PLATFORMS
|
|
18
25
|
ruby
|
19
26
|
|
20
27
|
DEPENDENCIES
|
28
|
+
i18n
|
29
|
+
r18n-core!
|
21
30
|
rake
|
22
31
|
redcarpet
|
23
32
|
rspec
|
data/README.md
CHANGED
@@ -6,6 +6,10 @@ text files. It’s ideal for static generated sites, like blog or homepage.
|
|
6
6
|
If you want to write another static website generator, you don’t need to write
|
7
7
|
another file parser – you can use Plain Record.
|
8
8
|
|
9
|
+
Sponsored by [Evil Martians].
|
10
|
+
|
11
|
+
[Evil Martians]: http://evilmartians.com/
|
12
|
+
|
9
13
|
## How To
|
10
14
|
|
11
15
|
For example we will create simple blog storage with posts and comments.
|
@@ -23,7 +27,7 @@ For example we will create simple blog storage with posts and comments.
|
|
23
27
|
```
|
24
28
|
|
25
29
|
3. Create Post class, include `Plain::Resource` module, set glob pattern
|
26
|
-
to posts files and define
|
30
|
+
to posts files and define fields:
|
27
31
|
|
28
32
|
```ruby
|
29
33
|
class Post
|
@@ -31,16 +35,17 @@ For example we will create simple blog storage with posts and comments.
|
|
31
35
|
|
32
36
|
entry_in '*/post.md'
|
33
37
|
|
34
|
-
virtual
|
35
|
-
virtual
|
36
|
-
|
37
|
-
|
38
|
-
|
39
|
-
text
|
38
|
+
virtual :name, in_filepath(1)
|
39
|
+
virtual :comments, many(Comment)
|
40
|
+
field :title default("Untitled")
|
41
|
+
field :tags default([])
|
42
|
+
field :created type(Time)
|
43
|
+
text :summary
|
44
|
+
text :content
|
40
45
|
end
|
41
46
|
```
|
42
47
|
|
43
|
-
4. Create new post file `data/first/post.md`.
|
48
|
+
4. Create new post file `data/first/post.md`. Fields will be saved as
|
44
49
|
YAML and text will be placed as plain text, which is separated by 3 dashes:
|
45
50
|
|
46
51
|
```
|
@@ -61,10 +66,10 @@ For example we will create simple blog storage with posts and comments.
|
|
61
66
|
|
62
67
|
list_in '*/comments.yml'
|
63
68
|
|
64
|
-
virtual
|
65
|
-
virtual
|
66
|
-
|
67
|
-
|
69
|
+
virtual :post_name, in_filepath(1)
|
70
|
+
virtual :post, one(Post)
|
71
|
+
field :author
|
72
|
+
field :comment
|
68
73
|
end
|
69
74
|
```
|
70
75
|
You can’t use text fields in list files.
|
@@ -90,7 +95,7 @@ For example we will create simple blog storage with posts and comments.
|
|
90
95
|
```
|
91
96
|
|
92
97
|
9. To get one entry use `first` method, which also can take matchers. You can
|
93
|
-
access for
|
98
|
+
access for fields and text by methods with same name:
|
94
99
|
|
95
100
|
```ruby
|
96
101
|
post = Post.first(title: /first/)
|
@@ -1,7 +1,8 @@
|
|
1
1
|
=begin
|
2
2
|
Storage for one-to-many virtual associations.
|
3
3
|
|
4
|
-
Copyright (C) 2009 Andrey “A.I.” Sitnik <andrey@sitnik.ru
|
4
|
+
Copyright (C) 2009 Andrey “A.I.” Sitnik <andrey@sitnik.ru>,
|
5
|
+
sponsored by Evil Martians.
|
5
6
|
|
6
7
|
This program is free software: you can redistribute it and/or modify
|
7
8
|
it under the terms of the GNU Lesser General Public License as published by
|
@@ -19,39 +20,39 @@ along with this program. If not, see <http://www.gnu.org/licenses/>.
|
|
19
20
|
|
20
21
|
module PlainRecord
|
21
22
|
# Storage for one-to-many virtual associations. When new object will pushed,
|
22
|
-
# proxy change it mapped
|
23
|
+
# proxy change it mapped fields.
|
23
24
|
class AssociationProxy < Array
|
24
25
|
# Model with association.
|
25
26
|
attr_accessor :owner
|
26
27
|
|
27
|
-
# Associations
|
28
|
-
attr_accessor :
|
28
|
+
# Associations field name.
|
29
|
+
attr_accessor :field
|
29
30
|
|
30
|
-
# Create proxy for one-to-many virtual associations +
|
31
|
+
# Create proxy for one-to-many virtual associations +field+ in +owner+
|
31
32
|
# and put +array+ into it.
|
32
|
-
def self.link(array, owner,
|
33
|
-
proxy = new(array, owner,
|
33
|
+
def self.link(array, owner, field)
|
34
|
+
proxy = new(array, owner, field)
|
34
35
|
proxy.each { |i| proxy.link(i) }
|
35
36
|
proxy
|
36
37
|
end
|
37
38
|
|
38
|
-
# Create proxy for one-to-many virtual associations +
|
39
|
+
# Create proxy for one-to-many virtual associations +field+ in +owner+
|
39
40
|
# with +array+ in value.
|
40
|
-
def initialize(array, owner,
|
41
|
+
def initialize(array, owner, field)
|
41
42
|
@owner = owner
|
42
|
-
@
|
43
|
+
@field = field
|
43
44
|
super(array)
|
44
45
|
end
|
45
46
|
|
46
|
-
# Push new item in association and change it
|
47
|
+
# Push new item in association and change it field by association map.
|
47
48
|
def <<(obj)
|
48
49
|
link(obj)
|
49
50
|
super(obj)
|
50
51
|
end
|
51
52
|
|
52
|
-
# Change
|
53
|
+
# Change fields in +obj+ by association map.
|
53
54
|
def link(obj)
|
54
|
-
@owner.class.association_maps[@
|
55
|
+
@owner.class.association_maps[@field].each do |from, to|
|
55
56
|
obj.send(from.to_s + '=', @owner.send(to))
|
56
57
|
end
|
57
58
|
end
|
@@ -1,7 +1,8 @@
|
|
1
1
|
=begin
|
2
2
|
Extention to store or have link to another model.
|
3
3
|
|
4
|
-
Copyright (C) 2009 Andrey “A.I.” Sitnik <andrey@sitnik.ru
|
4
|
+
Copyright (C) 2009 Andrey “A.I.” Sitnik <andrey@sitnik.ru>,
|
5
|
+
sponsored by Evil Martians.
|
5
6
|
|
6
7
|
This program is free software: you can redistribute it and/or modify
|
7
8
|
it under the terms of the GNU Lesser General Public License as published by
|
@@ -21,30 +22,30 @@ module PlainRecord
|
|
21
22
|
# Extention for model to store or have link to another model. There is two
|
22
23
|
# type of association.
|
23
24
|
#
|
24
|
-
# == Virtual
|
25
|
-
# In +virtual+ method this
|
26
|
-
# you try to use this virtual
|
25
|
+
# == Virtual field
|
26
|
+
# In +virtual+ method this filter create only _link_ to another model. When
|
27
|
+
# you try to use this virtual field, model will find association object by
|
27
28
|
# rules in +map+.
|
28
29
|
#
|
29
|
-
# Rules in +map+ is only Hash with model
|
30
|
-
#
|
30
|
+
# Rules in +map+ is only Hash with model fields in key and association
|
31
|
+
# fields in value. For example, if model contain +name+ field and
|
31
32
|
# association must have +post_name+ with same value, +map+ will be
|
32
33
|
# <tt>{ :name => :post_name }</tt>.
|
33
34
|
#
|
34
|
-
# If you didn’t set +map+
|
35
|
-
# it will find in model and association class all
|
36
|
-
# name like +
|
37
|
-
# if model +Post+ have
|
38
|
-
# may not set +map+ –
|
35
|
+
# If you didn’t set +map+ filter will try to find it automatically:
|
36
|
+
# it will find in model and association class all field pairs, what have
|
37
|
+
# name like +field+ → <tt>model</tt>_<tt>field</tt>. For example,
|
38
|
+
# if model +Post+ have field +name+ and +Comment+ have +post_name+, you
|
39
|
+
# may not set +map+ – filter will find it automatically.
|
39
40
|
#
|
40
41
|
# class Review
|
41
42
|
# include PlainRecord::Resource
|
42
43
|
#
|
43
44
|
# entry_in 'reviews/*.md'
|
44
45
|
#
|
45
|
-
# virtual
|
46
|
-
#
|
47
|
-
# text
|
46
|
+
# virtual :author, one(Author)
|
47
|
+
# field :author_login
|
48
|
+
# text :review
|
48
49
|
# end
|
49
50
|
#
|
50
51
|
# class Author
|
@@ -52,26 +53,26 @@ module PlainRecord
|
|
52
53
|
#
|
53
54
|
# list_in 'authors.yml'
|
54
55
|
#
|
55
|
-
# virtual
|
56
|
-
#
|
57
|
-
#
|
56
|
+
# virtual :reviews, many(Review)
|
57
|
+
# field :login
|
58
|
+
# field :name
|
58
59
|
# end
|
59
60
|
#
|
60
|
-
# == Real
|
61
|
-
# If you will use this
|
61
|
+
# == Real field
|
62
|
+
# If you will use this filter in +field+ method, association object data
|
62
63
|
# will store in you model file. For example model:
|
63
64
|
#
|
64
65
|
# class Movie
|
65
66
|
# include PlainRecord::Resource
|
66
67
|
#
|
67
|
-
#
|
68
|
-
#
|
69
|
-
#
|
68
|
+
# field :title
|
69
|
+
# field :genre
|
70
|
+
# field :release_year
|
70
71
|
# end
|
71
72
|
#
|
72
73
|
# class Tag
|
73
74
|
# include PlainRecord::Resource
|
74
|
-
#
|
75
|
+
# field :name
|
75
76
|
# end
|
76
77
|
#
|
77
78
|
# class Review
|
@@ -79,10 +80,10 @@ module PlainRecord
|
|
79
80
|
#
|
80
81
|
# entry_in 'reviews/*.md'
|
81
82
|
#
|
82
|
-
#
|
83
|
-
#
|
84
|
-
#
|
85
|
-
# text
|
83
|
+
# field :author
|
84
|
+
# field :movie, one(Movie)
|
85
|
+
# field :tags, many(Tag)
|
86
|
+
# text :review
|
86
87
|
# end
|
87
88
|
#
|
88
89
|
# will be store as:
|
@@ -101,54 +102,47 @@ module PlainRecord
|
|
101
102
|
# Hash with map for virtual associations.
|
102
103
|
attr_accessor :association_maps
|
103
104
|
|
104
|
-
# Hash with cached values for virtual associations.
|
105
|
-
attr_accessor :association_cache
|
106
|
-
|
107
105
|
private
|
108
106
|
|
109
|
-
# Return
|
110
|
-
# logic in +
|
107
|
+
# Return filter for one-to-one association with +klass+. Have different
|
108
|
+
# logic in +field+ and +virtual+ methods.
|
111
109
|
def one(klass, map = { })
|
112
|
-
proc do |
|
113
|
-
if :
|
114
|
-
Associations.define_real_one(
|
115
|
-
|
116
|
-
|
117
|
-
|
118
|
-
Associations.define_link_one(self, klass, property, map)
|
119
|
-
nil
|
110
|
+
proc do |model, field, type|
|
111
|
+
if :field == type
|
112
|
+
Associations.define_real_one(model, field, klass)
|
113
|
+
elsif :virtual == type
|
114
|
+
map = Associations.map(model, klass, "#{field}_") if map.empty?
|
115
|
+
Associations.define_link_one(model, klass, field, map)
|
120
116
|
else
|
121
|
-
raise ArgumentError, "You couldn't create association
|
122
|
-
" #{
|
117
|
+
raise ArgumentError, "You couldn't create association field" +
|
118
|
+
" #{field} by text creator"
|
123
119
|
end
|
124
120
|
end
|
125
121
|
end
|
126
122
|
|
127
|
-
# Return
|
128
|
-
# Have different login in +
|
123
|
+
# Return filter for one-to-many or many-to-many association with +klass+.
|
124
|
+
# Have different login in +field+ and +virtual+ methods.
|
129
125
|
def many(klass, prefix = nil, map = { })
|
130
|
-
proc do |
|
131
|
-
if :
|
132
|
-
Associations.define_real_many(
|
133
|
-
|
134
|
-
elsif :virtual == caller
|
126
|
+
proc do |model, field, type|
|
127
|
+
if :field == type
|
128
|
+
Associations.define_real_many(model, field, klass)
|
129
|
+
elsif :virtual == type
|
135
130
|
unless prefix
|
136
131
|
prefix = self.to_s.gsub!(/[A-Z]/, '_\0')[1..-1].downcase + '_'
|
137
132
|
end
|
138
|
-
map = Associations.map(klass,
|
139
|
-
Associations.define_link_many(
|
140
|
-
nil
|
133
|
+
map = Associations.map(klass, model, prefix) if map.empty?
|
134
|
+
Associations.define_link_many(model, klass, field, map)
|
141
135
|
else
|
142
|
-
raise ArgumentError, "You couldn't create association
|
143
|
-
" #{
|
136
|
+
raise ArgumentError, "You couldn't create association field" +
|
137
|
+
" #{field} by text creator"
|
144
138
|
end
|
145
139
|
end
|
146
140
|
end
|
147
141
|
|
148
142
|
class << self
|
149
|
-
# Define, that +
|
150
|
-
def define_real_one(klass,
|
151
|
-
name =
|
143
|
+
# Define, that +field+ in +klass+ contain in file data from +model+.
|
144
|
+
def define_real_one(klass, field, model)
|
145
|
+
name = field.to_s
|
152
146
|
klass.after :load do |result, entry|
|
153
147
|
entry.data[name] = model.new(entry.file, entry.data[name])
|
154
148
|
result
|
@@ -164,10 +158,10 @@ module PlainRecord
|
|
164
158
|
end
|
165
159
|
end
|
166
160
|
|
167
|
-
# Define, that +
|
161
|
+
# Define, that +field+ in +klass+ contain in file array of data from
|
168
162
|
# +model+ objects.
|
169
|
-
def define_real_many(klass,
|
170
|
-
name =
|
163
|
+
def define_real_many(klass, field, model)
|
164
|
+
name = field.to_s
|
171
165
|
klass.after :load do |result, entry|
|
172
166
|
if entry.data[name].is_a? Enumerable
|
173
167
|
entry.data[name].map! { |i| model.new(entry.file, i) }
|
@@ -195,17 +189,17 @@ module PlainRecord
|
|
195
189
|
end
|
196
190
|
end
|
197
191
|
|
198
|
-
# Find
|
192
|
+
# Find fields pairs in +from+ and +to+ models, witch is like
|
199
193
|
# <tt>prefix</tt>_<tt>from</tt> → +to+.
|
200
194
|
#
|
201
|
-
# For example, if Comment contain +post_name+
|
195
|
+
# For example, if Comment contain +post_name+ field and Post contain
|
202
196
|
# +name+:
|
203
197
|
#
|
204
198
|
# Associations.map(Comment, Post, :post) #=> { :post_name => :name }
|
205
199
|
def map(from, to, prefix)
|
206
|
-
from_fields = (from.
|
200
|
+
from_fields = (from.fields + from.virtuals).map { |i| i.to_s }
|
207
201
|
mapped = { }
|
208
|
-
(to.
|
202
|
+
(to.fields + to.virtuals).each do |to_field|
|
209
203
|
from_field = prefix + to_field.to_s
|
210
204
|
if from_fields.include? from_field
|
211
205
|
mapped[from_field.to_sym] = to_field
|
@@ -214,54 +208,60 @@ module PlainRecord
|
|
214
208
|
mapped
|
215
209
|
end
|
216
210
|
|
217
|
-
|
211
|
+
def init_association_cache(klass)
|
212
|
+
klass.after :load do |result, entry|
|
213
|
+
entry.instance_exec { @association_cache = { } }
|
214
|
+
end
|
215
|
+
end
|
216
|
+
|
217
|
+
# Define that virtual field +name+ in +klass+ contain link to +model+
|
218
218
|
# witch is finded by +map+.
|
219
219
|
def define_link_one(klass, model, name, map)
|
220
|
-
klass.
|
221
|
-
klass.association_maps ||= { }
|
220
|
+
klass.association_maps ||= { }
|
222
221
|
klass.association_maps[name] = map
|
222
|
+
init_association_cache(klass)
|
223
223
|
|
224
|
-
klass.
|
224
|
+
klass.add_accessors <<-EOS, __FILE__, __LINE__
|
225
225
|
def #{name}
|
226
|
-
unless
|
226
|
+
unless @association_cache.has_key? :#{name}
|
227
227
|
search = Hash[
|
228
228
|
self.class.association_maps[:#{name}].map do |from, to|
|
229
229
|
[to, send(from)]
|
230
230
|
end]
|
231
|
-
|
231
|
+
@association_cache[:#{name}] = #{model}.first(search)
|
232
232
|
end
|
233
|
-
|
233
|
+
@association_cache[:#{name}]
|
234
234
|
end
|
235
235
|
def #{name}=(value)
|
236
236
|
self.class.association_maps[:#{name}].each do |from, to|
|
237
237
|
value.send(to.to_s + '=', send(from))
|
238
238
|
end
|
239
|
-
|
239
|
+
@association_cache[:#{name}] = value
|
240
240
|
end
|
241
241
|
EOS
|
242
242
|
end
|
243
243
|
|
244
|
-
# Define that virtual
|
244
|
+
# Define that virtual field +name+ in +klass+ contain links to +model+
|
245
245
|
# witch are finded by +map+.
|
246
246
|
def define_link_many(klass, model, name, map)
|
247
|
-
klass.
|
248
|
-
klass.association_maps ||= { }
|
247
|
+
klass.association_maps ||= { }
|
249
248
|
klass.association_maps[name] = map
|
249
|
+
init_association_cache(klass)
|
250
250
|
|
251
|
-
klass.
|
251
|
+
klass.add_accessors <<-EOS, __FILE__, __LINE__
|
252
252
|
def #{name}
|
253
|
-
unless
|
253
|
+
unless @association_cache.has_key? :#{name}
|
254
254
|
search = Hash[
|
255
255
|
self.class.association_maps[:#{name}].map do |from, to|
|
256
256
|
[from, send(to)]
|
257
257
|
end]
|
258
|
-
|
258
|
+
@association_cache[:#{name}] = AssociationProxy.new(
|
259
259
|
#{model}.all(search), self, :#{name})
|
260
260
|
end
|
261
|
-
|
261
|
+
@association_cache[:#{name}]
|
262
262
|
end
|
263
263
|
def #{name}=(values)
|
264
|
-
|
264
|
+
@association_cache[:#{name}] = AssociationProxy.link(
|
265
265
|
values, self, :#{name})
|
266
266
|
end
|
267
267
|
EOS
|
@@ -1,7 +1,8 @@
|
|
1
1
|
=begin
|
2
2
|
Module to add before/after hooks.
|
3
3
|
|
4
|
-
Copyright (C) 2009 Andrey “A.I.” Sitnik <andrey@sitnik.ru
|
4
|
+
Copyright (C) 2009 Andrey “A.I.” Sitnik <andrey@sitnik.ru>,
|
5
|
+
sponsored by Evil Martians.
|
5
6
|
|
6
7
|
This program is free software: you can redistribute it and/or modify
|
7
8
|
it under the terms of the GNU Lesser General Public License as published by
|
@@ -21,7 +22,7 @@ module PlainRecord
|
|
21
22
|
# Callbacks are hooks that allow you to define methods to run before and
|
22
23
|
# after some method, to change it logic.
|
23
24
|
module Callbacks
|
24
|
-
# Hash of class callbacks with
|
25
|
+
# Hash of class callbacks with field.
|
25
26
|
attr_accessor :callbacks
|
26
27
|
|
27
28
|
# Set block as callback before +events+. Callback with less +priority+ will
|
@@ -0,0 +1,56 @@
|
|
1
|
+
=begin
|
2
|
+
Extention to set default value for field.
|
3
|
+
|
4
|
+
Copyright (C) 2012 Andrey “A.I.” Sitnik <andrey@sitnik.ru>,
|
5
|
+
sponsored by Evil Martians.
|
6
|
+
|
7
|
+
This program is free software: you can redistribute it and/or modify
|
8
|
+
it under the terms of the GNU Lesser General Public License as published by
|
9
|
+
the Free Software Foundation, either version 3 of the License, or
|
10
|
+
(at your option) any later version.
|
11
|
+
|
12
|
+
This program is distributed in the hope that it will be useful,
|
13
|
+
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
14
|
+
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
15
|
+
GNU Lesser General Public License for more details.
|
16
|
+
|
17
|
+
You should have received a copy of the GNU Lesser General Public License
|
18
|
+
along with this program. If not, see <http://www.gnu.org/licenses/>.
|
19
|
+
=end
|
20
|
+
|
21
|
+
module PlainRecord
|
22
|
+
# Extention to set default value for field.
|
23
|
+
#
|
24
|
+
# class Post
|
25
|
+
# include PlainRecord::Resource
|
26
|
+
#
|
27
|
+
# entry_in '*/*/post.md'
|
28
|
+
#
|
29
|
+
# virtual :category, default('uncategorized')
|
30
|
+
# …
|
31
|
+
# end
|
32
|
+
#
|
33
|
+
# post = Post.new
|
34
|
+
# post.category #=> "uncategorized"
|
35
|
+
# post.category = "a"
|
36
|
+
# post.category #=> "a"
|
37
|
+
module Default
|
38
|
+
attr_accessor :default_values
|
39
|
+
|
40
|
+
private
|
41
|
+
|
42
|
+
# Filter to set default field value.
|
43
|
+
def default(value)
|
44
|
+
proc do |model, field, type|
|
45
|
+
model.default_values ||= { }
|
46
|
+
model.default_values[field] = value
|
47
|
+
|
48
|
+
model.add_accessors <<-EOS, __FILE__, __LINE__
|
49
|
+
def #{field}
|
50
|
+
super || self.class.default_values[:#{field}]
|
51
|
+
end
|
52
|
+
EOS
|
53
|
+
end
|
54
|
+
end
|
55
|
+
end
|
56
|
+
end
|
@@ -0,0 +1,97 @@
|
|
1
|
+
=begin
|
2
|
+
Extention to get time from git commits of model file.
|
3
|
+
|
4
|
+
Copyright (C) 2012 Andrey “A.I.” Sitnik <andrey@sitnik.ru>,
|
5
|
+
sponsored by Evil Martians.
|
6
|
+
|
7
|
+
This program is free software: you can redistribute it and/or modify
|
8
|
+
it under the terms of the GNU Lesser General Public License as published by
|
9
|
+
the Free Software Foundation, either version 3 of the License, or
|
10
|
+
(at your option) any later version.
|
11
|
+
|
12
|
+
This program is distributed in the hope that it will be useful,
|
13
|
+
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
14
|
+
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
15
|
+
GNU Lesser General Public License for more details.
|
16
|
+
|
17
|
+
You should have received a copy of the GNU Lesser General Public License
|
18
|
+
along with this program. If not, see <http://www.gnu.org/licenses/>.
|
19
|
+
=end
|
20
|
+
|
21
|
+
module PlainRecord::Extra
|
22
|
+
# Extention to get time from git commits of model file.
|
23
|
+
#
|
24
|
+
# It make sense only with `entry_in` models. You can get created or modified
|
25
|
+
# time from first or last git commit time.
|
26
|
+
#
|
27
|
+
# It is additional extention, so you need to include `PlainRecord::Extra::Git`
|
28
|
+
# module to your model.
|
29
|
+
#
|
30
|
+
# class Post
|
31
|
+
# include PlainRecord::Resource
|
32
|
+
# include PlainRecord::Extra::Git
|
33
|
+
#
|
34
|
+
# virtual :created_at, git_created_time
|
35
|
+
# virtual :updated_at, git_modify_time
|
36
|
+
# end
|
37
|
+
module Git
|
38
|
+
class << self
|
39
|
+
def included(base)
|
40
|
+
base.send :extend, Model
|
41
|
+
end
|
42
|
+
end
|
43
|
+
|
44
|
+
# Return time of first commit of model file (created time).
|
45
|
+
#
|
46
|
+
# If file isn’t commited yet, it will return `Time.now`.
|
47
|
+
def first_git_commit
|
48
|
+
return Time.now unless file
|
49
|
+
times = `git log --reverse --date=iso --pretty=format:%cD #{file}`
|
50
|
+
time = times.split("\n").first
|
51
|
+
time ? Time.parse(time) : Time.now
|
52
|
+
end
|
53
|
+
|
54
|
+
# Return time of last commit of model file (modified time).
|
55
|
+
#
|
56
|
+
# If file isn’t commited yet, it will return `Time.now`.
|
57
|
+
def last_git_commit
|
58
|
+
return Time.now if file.nil? or git_uncommitted?
|
59
|
+
time = `git log -1 --date=iso --pretty=format:%cD #{file}`
|
60
|
+
time ? Time.parse(time) : Time.now
|
61
|
+
end
|
62
|
+
|
63
|
+
# If file have changes, that is not commited yet.
|
64
|
+
def git_uncommitted?
|
65
|
+
not `git status -s #{file}`.empty?
|
66
|
+
end
|
67
|
+
|
68
|
+
module Model
|
69
|
+
|
70
|
+
private
|
71
|
+
|
72
|
+
# Filter to set default value to time of last file git commit.
|
73
|
+
# If file is not commited or has changes, filter will return `Time.now`.
|
74
|
+
def git_modified_time
|
75
|
+
proc do |model, name, type|
|
76
|
+
model.add_accessors <<-EOS, __FILE__, __LINE__
|
77
|
+
def #{name}
|
78
|
+
super || last_git_commit
|
79
|
+
end
|
80
|
+
EOS
|
81
|
+
end
|
82
|
+
end
|
83
|
+
|
84
|
+
# Filter to set default value to time of first file git commit.
|
85
|
+
# If file is not commited or has changes, filter will return `Time.now`.
|
86
|
+
def git_created_time
|
87
|
+
proc do |model, name, type|
|
88
|
+
model.add_accessors <<-EOS, __FILE__, __LINE__
|
89
|
+
def #{name}
|
90
|
+
super || first_git_commit
|
91
|
+
end
|
92
|
+
EOS
|
93
|
+
end
|
94
|
+
end
|
95
|
+
end
|
96
|
+
end
|
97
|
+
end
|