plain_record 0.3 → 0.4
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 +4 -1
- data/ChangeLog +5 -0
- data/Gemfile +8 -3
- data/Gemfile.lock +16 -18
- data/Rakefile +6 -4
- data/lib/plain_record/associations.rb +16 -10
- data/lib/plain_record/extra/git.rb +2 -6
- data/lib/plain_record/extra/i18n.rb +2 -4
- data/lib/plain_record/extra/image.rb +282 -0
- data/lib/plain_record/file.rb +91 -0
- data/lib/plain_record/filepath.rb +41 -42
- data/lib/plain_record/model.rb +4 -4
- data/lib/plain_record/rails.rb +38 -0
- data/lib/plain_record/resource.rb +7 -6
- data/lib/plain_record/type.rb +2 -0
- data/lib/plain_record/version.rb +1 -1
- data/lib/plain_record.rb +7 -9
- data/plain_record.gemspec +4 -1
- data/spec/associations_spec.rb +9 -4
- data/spec/file_spec.rb +88 -0
- data/spec/image_spec.rb +149 -0
- data/spec/spec_helper.rb +4 -0
- metadata +98 -29
data/.travis.yml
CHANGED
data/ChangeLog
CHANGED
data/Gemfile
CHANGED
@@ -1,8 +1,13 @@
|
|
1
1
|
source :rubygems
|
2
2
|
|
3
3
|
gem 'rake'
|
4
|
-
gem 'yard'
|
5
4
|
gem 'rspec'
|
6
|
-
gem '
|
7
|
-
gem 'r18n-core', :require => nil, :github => 'ai/r18n'
|
5
|
+
gem 'r18n-core', :require => nil
|
8
6
|
gem 'i18n', :require => nil
|
7
|
+
gem 'rmagick', :platforms => :ruby
|
8
|
+
gem 'rmagick4j', :platforms => :jruby
|
9
|
+
|
10
|
+
group :development do
|
11
|
+
gem 'redcarpet'
|
12
|
+
gem 'yard'
|
13
|
+
end
|
data/Gemfile.lock
CHANGED
@@ -1,33 +1,31 @@
|
|
1
|
-
GIT
|
2
|
-
remote: git://github.com/ai/r18n.git
|
3
|
-
revision: ca6933926eb955344eeb982de35f492c76a38b1a
|
4
|
-
specs:
|
5
|
-
r18n-core (0.4.14)
|
6
|
-
|
7
1
|
GEM
|
8
2
|
remote: http://rubygems.org/
|
9
3
|
specs:
|
10
4
|
diff-lcs (1.1.3)
|
11
|
-
i18n (0.6.
|
12
|
-
|
13
|
-
|
14
|
-
|
15
|
-
|
16
|
-
|
17
|
-
rspec-
|
18
|
-
|
19
|
-
|
5
|
+
i18n (0.6.1)
|
6
|
+
r18n-core (1.1.3)
|
7
|
+
rake (10.0.3)
|
8
|
+
redcarpet (2.2.2)
|
9
|
+
rmagick (2.13.1)
|
10
|
+
rspec (2.12.0)
|
11
|
+
rspec-core (~> 2.12.0)
|
12
|
+
rspec-expectations (~> 2.12.0)
|
13
|
+
rspec-mocks (~> 2.12.0)
|
14
|
+
rspec-core (2.12.2)
|
15
|
+
rspec-expectations (2.12.1)
|
20
16
|
diff-lcs (~> 1.1.3)
|
21
|
-
rspec-mocks (2.
|
22
|
-
yard (0.8.
|
17
|
+
rspec-mocks (2.12.1)
|
18
|
+
yard (0.8.3)
|
23
19
|
|
24
20
|
PLATFORMS
|
25
21
|
ruby
|
26
22
|
|
27
23
|
DEPENDENCIES
|
28
24
|
i18n
|
29
|
-
r18n-core
|
25
|
+
r18n-core
|
30
26
|
rake
|
31
27
|
redcarpet
|
28
|
+
rmagick
|
29
|
+
rmagick4j
|
32
30
|
rspec
|
33
31
|
yard
|
data/Rakefile
CHANGED
@@ -15,10 +15,12 @@ require 'rspec/core/rake_task'
|
|
15
15
|
|
16
16
|
RSpec::Core::RakeTask.new
|
17
17
|
|
18
|
-
|
19
|
-
|
20
|
-
|
21
|
-
|
18
|
+
begin
|
19
|
+
require 'yard'
|
20
|
+
YARD::Rake::YardocTask.new do |yard|
|
21
|
+
yard.options << "--title='Plain Record #{PlainRecord::VERSION}'"
|
22
|
+
end
|
23
|
+
rescue LoadError; end
|
22
24
|
|
23
25
|
task :clobber_doc do
|
24
26
|
rm_r 'doc' rescue nil
|
@@ -88,7 +88,7 @@ module PlainRecord
|
|
88
88
|
#
|
89
89
|
# will be store as:
|
90
90
|
#
|
91
|
-
# author: John Smith
|
91
|
+
# author: John Smith
|
92
92
|
# movie:
|
93
93
|
# title: Watchmen
|
94
94
|
# genre: action
|
@@ -114,8 +114,8 @@ module PlainRecord
|
|
114
114
|
map = Associations.map(model, klass, "#{field}_") if map.empty?
|
115
115
|
Associations.define_link_one(model, klass, field, map)
|
116
116
|
else
|
117
|
-
raise ArgumentError,
|
118
|
-
|
117
|
+
raise ArgumentError,
|
118
|
+
"You couldn't create association field #{field} by text creator"
|
119
119
|
end
|
120
120
|
end
|
121
121
|
end
|
@@ -133,8 +133,8 @@ module PlainRecord
|
|
133
133
|
map = Associations.map(klass, model, prefix) if map.empty?
|
134
134
|
Associations.define_link_many(model, klass, field, map)
|
135
135
|
else
|
136
|
-
raise ArgumentError,
|
137
|
-
|
136
|
+
raise ArgumentError,
|
137
|
+
"You couldn't create association field #{field} by text creator"
|
138
138
|
end
|
139
139
|
end
|
140
140
|
end
|
@@ -144,16 +144,22 @@ module PlainRecord
|
|
144
144
|
def define_real_one(klass, field, model)
|
145
145
|
name = field.to_s
|
146
146
|
klass.after :load do |result, entry|
|
147
|
-
|
147
|
+
if entry.data[name]
|
148
|
+
entry.data[name] = model.new(entry.file, entry.data[name])
|
149
|
+
end
|
148
150
|
result
|
149
151
|
end
|
150
152
|
klass.before :save do |entry|
|
151
|
-
|
152
|
-
|
153
|
+
if entry.data[name]
|
154
|
+
model.call_before_callbacks(:save, [entry.data[name]])
|
155
|
+
entry.data[name] = entry.data[name]
|
156
|
+
end
|
153
157
|
end
|
154
158
|
klass.after :save do |result, entry|
|
155
|
-
|
156
|
-
|
159
|
+
if entry.data[name]
|
160
|
+
entry.data[name] = model.new(entry.file, entry.data[name])
|
161
|
+
model.call_after_callbacks(:save, nil, [entry.data[name]])
|
162
|
+
end
|
157
163
|
result
|
158
164
|
end
|
159
165
|
end
|
@@ -35,10 +35,8 @@ module PlainRecord::Extra
|
|
35
35
|
# virtual :updated_at, git_modify_time
|
36
36
|
# end
|
37
37
|
module Git
|
38
|
-
|
39
|
-
|
40
|
-
base.send :extend, Model
|
41
|
-
end
|
38
|
+
def self.included(base)
|
39
|
+
base.send :extend, Model
|
42
40
|
end
|
43
41
|
|
44
42
|
# Return time of first commit of model file (created time).
|
@@ -67,8 +65,6 @@ module PlainRecord::Extra
|
|
67
65
|
|
68
66
|
module Model
|
69
67
|
|
70
|
-
private
|
71
|
-
|
72
68
|
# Filter to set default value to time of last file git commit.
|
73
69
|
# If file is not commited or has changes, filter will return `Time.now`.
|
74
70
|
def git_modified_time
|
@@ -47,10 +47,8 @@ module PlainRecord::Extra
|
|
47
47
|
# end
|
48
48
|
# end
|
49
49
|
module I18n
|
50
|
-
|
51
|
-
|
52
|
-
base.send :extend, Model
|
53
|
-
end
|
50
|
+
def self.included(base)
|
51
|
+
base.send :extend, Model
|
54
52
|
end
|
55
53
|
|
56
54
|
# Return default locale. By default it look in R18n or Rails I18n.
|
@@ -0,0 +1,282 @@
|
|
1
|
+
=begin
|
2
|
+
Extention to store images.
|
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
|
+
require 'fileutils'
|
22
|
+
|
23
|
+
module PlainRecord::Extra
|
24
|
+
# Extention to store images.
|
25
|
+
#
|
26
|
+
# It make sense only with `entry_in` models. You can get created or modified
|
27
|
+
# time from first or last git commit time.
|
28
|
+
#
|
29
|
+
# It is additional extention, so you need to include `PlainRecord::Extra::Git`
|
30
|
+
# module to your model.
|
31
|
+
#
|
32
|
+
# class User
|
33
|
+
# include PlainRecord::Resource
|
34
|
+
# include PlainRecord::Extra::Image
|
35
|
+
#
|
36
|
+
# entry_in "users/*.yml"
|
37
|
+
#
|
38
|
+
# image_from do |user, field|
|
39
|
+
# "users/#{field}/#{user.name}.png"
|
40
|
+
# end
|
41
|
+
# image_url do |user, field, size|
|
42
|
+
# if size
|
43
|
+
# "users/#{user.name}/#{field}.#{size}.png"
|
44
|
+
# else
|
45
|
+
# "users/#{user.name}/#{field}.png"
|
46
|
+
# end
|
47
|
+
# end
|
48
|
+
#
|
49
|
+
# virtual :name, in_filepath(1)
|
50
|
+
# virtual :avatar, image(small: '32x32', big: '64x64')
|
51
|
+
# virtual :photo, image
|
52
|
+
# end
|
53
|
+
#
|
54
|
+
# # There are images at `data/users/avatar/ai.png` and
|
55
|
+
# # `data/users/photo/ai.png`
|
56
|
+
#
|
57
|
+
# PlainRecord::Extra::Image.convert_images!
|
58
|
+
# user = User.first(name: 'ai')
|
59
|
+
#
|
60
|
+
# user.photo.url #=> "data/users/ai/photo.png"
|
61
|
+
# user.photo.file #=> "app/assets/images/data/users/ai/avatar.small.png"
|
62
|
+
# user.avatar(:small).url #=> "data/users/ai/avatar.small.png"
|
63
|
+
module Image
|
64
|
+
class << self
|
65
|
+
# Models, that used this extention.
|
66
|
+
attr_accessor :included_in
|
67
|
+
|
68
|
+
# Base of converted images.
|
69
|
+
# Set to <tt>app/aseets/images/data</tt> in Rails.
|
70
|
+
attr_accessor :dir
|
71
|
+
|
72
|
+
# Base of converted images URL. Set to <tt>data/</tt> in Rails.
|
73
|
+
attr_accessor :url
|
74
|
+
|
75
|
+
def included(base)
|
76
|
+
base.send :extend, Model
|
77
|
+
self.included_in ||= []
|
78
|
+
self.included_in << base
|
79
|
+
end
|
80
|
+
|
81
|
+
# Define class variables.
|
82
|
+
def install(klass)
|
83
|
+
klass.image_sizes = { }
|
84
|
+
end
|
85
|
+
|
86
|
+
def dir
|
87
|
+
unless @dir
|
88
|
+
raise ArgumentError,
|
89
|
+
'You need to set `PlainRecord::Extra::Image.dir`'
|
90
|
+
end
|
91
|
+
@dir
|
92
|
+
end
|
93
|
+
|
94
|
+
def url
|
95
|
+
unless @url
|
96
|
+
raise ArgumentError,
|
97
|
+
'You need to set `PlainRecord::Extra::Image.url`'
|
98
|
+
end
|
99
|
+
@url
|
100
|
+
end
|
101
|
+
|
102
|
+
# Delete all converted images.
|
103
|
+
def clean_images!
|
104
|
+
Dir.glob(File.join(self.dir, '**/*')) do |file|
|
105
|
+
FileUtils.rm_r(file) if File.exists? file
|
106
|
+
end
|
107
|
+
end
|
108
|
+
|
109
|
+
# Convert images in all models.
|
110
|
+
def convert_images!
|
111
|
+
clean_images!
|
112
|
+
return unless included_in
|
113
|
+
|
114
|
+
included_in.each do |model|
|
115
|
+
model.all.each { |i| i.convert_images! }
|
116
|
+
end
|
117
|
+
end
|
118
|
+
|
119
|
+
end
|
120
|
+
|
121
|
+
# Convert all images to public dir.
|
122
|
+
def convert_images!
|
123
|
+
self.class.image_sizes.each_pair do |field, sizes|
|
124
|
+
from = self.class.get_image_from(self, field)
|
125
|
+
next unless File.exists? from
|
126
|
+
|
127
|
+
if sizes.empty?
|
128
|
+
to = self.class.get_image_file(self, field, nil)
|
129
|
+
FileUtils.mkpath(File.dirname(to))
|
130
|
+
FileUtils.cp(from, to)
|
131
|
+
else
|
132
|
+
require 'RMagick'
|
133
|
+
source = ::Magick::Image.read(from).first
|
134
|
+
sizes.each_pair do |name, size|
|
135
|
+
to = self.class.get_image_file(self, field, name)
|
136
|
+
w, h = size.split('x')
|
137
|
+
image = source.resize(w.to_i, h.to_i)
|
138
|
+
self.class.use_callbacks(:convert_image, self, to) do
|
139
|
+
FileUtils.mkpath(File.dirname(to))
|
140
|
+
image.write(to)
|
141
|
+
end
|
142
|
+
end
|
143
|
+
end
|
144
|
+
end
|
145
|
+
end
|
146
|
+
|
147
|
+
# Field value object with image paths and URL.
|
148
|
+
class Data
|
149
|
+
|
150
|
+
# Name of image size.
|
151
|
+
attr_reader :size_name
|
152
|
+
|
153
|
+
# Format of image size.
|
154
|
+
attr_reader :size
|
155
|
+
|
156
|
+
# Image width.
|
157
|
+
attr_reader :width
|
158
|
+
|
159
|
+
# Image height.
|
160
|
+
attr_reader :height
|
161
|
+
|
162
|
+
# Converted image URL.
|
163
|
+
attr_reader :url
|
164
|
+
|
165
|
+
# Converted image file.
|
166
|
+
attr_reader :file
|
167
|
+
|
168
|
+
# Original image file.
|
169
|
+
attr_reader :original
|
170
|
+
|
171
|
+
# Set image paths.
|
172
|
+
def initialize(entry, field, size)
|
173
|
+
@size_name = size
|
174
|
+
@original = entry.class.get_image_from(entry, field)
|
175
|
+
|
176
|
+
if size
|
177
|
+
@size = entry.class.image_sizes[field][size]
|
178
|
+
unless @size
|
179
|
+
raise ArgumentError, "Field `#{field}` doesn't have `#{size}` size"
|
180
|
+
end
|
181
|
+
@width, @height = @size.split('x').map { |i| i.to_i }
|
182
|
+
end
|
183
|
+
|
184
|
+
if size or entry.class.image_sizes[field].empty?
|
185
|
+
@url = entry.class.get_image_url(entry, field, size)
|
186
|
+
@file = entry.class.get_image_file(entry, field, size)
|
187
|
+
end
|
188
|
+
end
|
189
|
+
|
190
|
+
def exists?
|
191
|
+
File.exists? @original
|
192
|
+
end
|
193
|
+
|
194
|
+
end
|
195
|
+
|
196
|
+
module Model
|
197
|
+
|
198
|
+
# Hash of image sizes.
|
199
|
+
attr_accessor :image_sizes
|
200
|
+
|
201
|
+
# Set source image path.
|
202
|
+
def image_from(&block)
|
203
|
+
@image_from = block
|
204
|
+
end
|
205
|
+
|
206
|
+
# Set relative image URL path from +image_base+.
|
207
|
+
def image_url(&block)
|
208
|
+
@image_url = block
|
209
|
+
end
|
210
|
+
|
211
|
+
# Get source image path.
|
212
|
+
def get_image_from(entry, field)
|
213
|
+
PlainRecord.root(@image_from.call(entry, field))
|
214
|
+
end
|
215
|
+
|
216
|
+
# Get converted image path.
|
217
|
+
def get_image_file(entry, field, size)
|
218
|
+
unless @image_url
|
219
|
+
raise ArgumentError,
|
220
|
+
"You need to set `image_url` in #{to_s} to calculate file path."
|
221
|
+
end
|
222
|
+
File.join(Image.dir, @image_url.call(entry, field, size))
|
223
|
+
end
|
224
|
+
|
225
|
+
# Get relative image URL path.
|
226
|
+
def get_image_url(entry, field, size)
|
227
|
+
unless @image_url
|
228
|
+
raise ArgumentError,
|
229
|
+
"You need to set `image_url` in #{to_s} to calculate file path."
|
230
|
+
end
|
231
|
+
File.join(Image.url, @image_url.call(entry, field, size))
|
232
|
+
end
|
233
|
+
|
234
|
+
private
|
235
|
+
|
236
|
+
# Use pngcrush (you must install it in system) to optimize png images.
|
237
|
+
#
|
238
|
+
# class User
|
239
|
+
# include PlainRecord::Resource
|
240
|
+
# include PlainRecord::Extra::Image
|
241
|
+
#
|
242
|
+
# optimize_png
|
243
|
+
# …
|
244
|
+
# end
|
245
|
+
def optimize_png
|
246
|
+
after :convert_image do |result, entry, file|
|
247
|
+
file = file.gsub(/([^A-Za-z0-9_\-\.,:\/@])/, "\\\\\\1")
|
248
|
+
file = file.gsub(/(^|\/)\.?\.($|\/)/, '')
|
249
|
+
return unless file =~ /\.png$/
|
250
|
+
|
251
|
+
tmp = file + '.optimized'
|
252
|
+
`pngcrush -rem gAMA -rem cHRM -rem iCCP -rem sRGB "#{file}" "#{tmp}"`
|
253
|
+
FileUtils.rm(file)
|
254
|
+
FileUtils.mv(tmp, file)
|
255
|
+
end
|
256
|
+
end
|
257
|
+
|
258
|
+
# Filter to create field with image. You must set +image_from+.
|
259
|
+
#
|
260
|
+
# If you create Rails application you must set also +image_url+.
|
261
|
+
# If you create web application with another web framework, you must
|
262
|
+
# also redefine `image_url_to_path` or set `image_to`.
|
263
|
+
#
|
264
|
+
# If you create non-web application, you must set only `image_to`.
|
265
|
+
#
|
266
|
+
# You can set sizes hash (name to ImageMagick size) to convert images.
|
267
|
+
def image(sizes = { })
|
268
|
+
proc do |model, name, type|
|
269
|
+
Image.install(model) unless model.image_sizes
|
270
|
+
model.image_sizes[name] = sizes
|
271
|
+
|
272
|
+
model.add_accessors <<-EOS, __FILE__, __LINE__
|
273
|
+
def #{name}(size = nil)
|
274
|
+
PlainRecord::Extra::Image::Data.new(self, :#{name}, size)
|
275
|
+
end
|
276
|
+
EOS
|
277
|
+
end
|
278
|
+
end
|
279
|
+
|
280
|
+
end
|
281
|
+
end
|
282
|
+
end
|
@@ -0,0 +1,91 @@
|
|
1
|
+
=begin
|
2
|
+
Extention to set to get field value from external 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
|
22
|
+
# Extention to set to get field value from external file.
|
23
|
+
#
|
24
|
+
# class Post
|
25
|
+
# include PlainRecord::Resource
|
26
|
+
#
|
27
|
+
# entry_in '*/post.md'
|
28
|
+
#
|
29
|
+
# virtual :name, in_filepath(1)
|
30
|
+
# virtuel :text, file { |p| "#{p.name}/text.#{I18n.locale}.md" }
|
31
|
+
# end
|
32
|
+
module File
|
33
|
+
# Cache of field content before save.
|
34
|
+
attr_accessor :unsaved_files
|
35
|
+
|
36
|
+
# Return file pathname for fiel field.
|
37
|
+
def field_filepath(field)
|
38
|
+
path = self.class.fields_files[field]
|
39
|
+
path = path.call(self) if path.is_a? Proc
|
40
|
+
PlainRecord.root(path)
|
41
|
+
end
|
42
|
+
|
43
|
+
private
|
44
|
+
|
45
|
+
# Define class variables and events in +klass+. It should be call once.
|
46
|
+
def self.install(klass)
|
47
|
+
klass.fields_files = { }
|
48
|
+
|
49
|
+
klass.before :load do |entry|
|
50
|
+
entry.unsaved_files = { }
|
51
|
+
end
|
52
|
+
|
53
|
+
klass.before :save do |entry|
|
54
|
+
entry.unsaved_files.each_pair do |file, value|
|
55
|
+
::File.open(file, 'w') { |io| io << value; }
|
56
|
+
end
|
57
|
+
entry.unsaved_files = { }
|
58
|
+
end
|
59
|
+
end
|
60
|
+
|
61
|
+
module Model
|
62
|
+
|
63
|
+
# Field file paths.
|
64
|
+
attr_accessor :fields_files
|
65
|
+
|
66
|
+
private
|
67
|
+
|
68
|
+
# Filter to load field value from file.
|
69
|
+
def file(path = nil, &block)
|
70
|
+
proc do |model, field, type|
|
71
|
+
File.install(model) unless model.fields_files
|
72
|
+
model.fields_files[field] = block_given? ? block : path
|
73
|
+
|
74
|
+
model.add_accessors <<-EOS, __FILE__, __LINE__
|
75
|
+
def #{field}
|
76
|
+
path = field_filepath(:#{field})
|
77
|
+
return @unsaved_files[path] if @unsaved_files.has_key? path
|
78
|
+
return nil unless ::File.exists? path
|
79
|
+
::File.read(path)
|
80
|
+
end
|
81
|
+
|
82
|
+
def #{field}=(value)
|
83
|
+
@unsaved_files[field_filepath(:#{field})] = value
|
84
|
+
end
|
85
|
+
EOS
|
86
|
+
end
|
87
|
+
end
|
88
|
+
|
89
|
+
end
|
90
|
+
end
|
91
|
+
end
|
@@ -59,8 +59,8 @@ module PlainRecord
|
|
59
59
|
def in_filepath(number)
|
60
60
|
proc do |model, field, type|
|
61
61
|
if :virtual != type
|
62
|
-
raise ArgumentError,
|
63
|
-
|
62
|
+
raise ArgumentError,
|
63
|
+
"You must create filepath field #{field} virtual creator"
|
64
64
|
end
|
65
65
|
|
66
66
|
Filepath.install(model) unless model.filepath_fields
|
@@ -77,57 +77,56 @@ module PlainRecord
|
|
77
77
|
end
|
78
78
|
end
|
79
79
|
|
80
|
-
class
|
81
|
-
|
82
|
-
|
83
|
-
|
84
|
-
klass.filepath_fields = { }
|
80
|
+
# Define class variables and events in +klass+. It should be call once on
|
81
|
+
# same class after +entry_in+ or +list_in+ call.
|
82
|
+
def self.install(klass)
|
83
|
+
klass.filepath_fields = { }
|
85
84
|
|
86
|
-
|
87
|
-
|
88
|
-
|
85
|
+
path = Regexp.escape(klass.path).gsub(/\\\*\\\*(\/|$)/, '(.*)').
|
86
|
+
gsub('\\*', '([^/]+)')
|
87
|
+
klass.filepath_regexp = Regexp.new(path)
|
89
88
|
|
90
|
-
|
91
|
-
|
92
|
-
|
89
|
+
klass.class_eval do
|
90
|
+
attr_accessor :filepath_data
|
91
|
+
end
|
93
92
|
|
94
|
-
|
95
|
-
|
96
|
-
|
97
|
-
|
98
|
-
|
99
|
-
|
100
|
-
|
101
|
-
|
102
|
-
|
103
|
-
|
104
|
-
|
105
|
-
|
106
|
-
end
|
93
|
+
klass.after :load do |result, entry|
|
94
|
+
if entry.path
|
95
|
+
data = klass.filepath_regexp.match(entry.path)
|
96
|
+
entry.filepath_data = { }
|
97
|
+
klass.filepath_fields.each_pair do |number, name|
|
98
|
+
entry.filepath_data[name] = data[number]
|
99
|
+
end
|
100
|
+
else
|
101
|
+
entry.filepath_data = { }
|
102
|
+
klass.filepath_fields.each_value do |name|
|
103
|
+
entry.filepath_data[name] = entry.data[name]
|
104
|
+
entry.data.delete(name)
|
107
105
|
end
|
108
|
-
result
|
109
106
|
end
|
107
|
+
result
|
108
|
+
end
|
110
109
|
|
111
|
-
|
112
|
-
|
113
|
-
|
114
|
-
|
115
|
-
|
116
|
-
|
117
|
-
|
118
|
-
|
119
|
-
|
120
|
-
end
|
110
|
+
klass.after :path do |path, matchers|
|
111
|
+
i = 0
|
112
|
+
path.gsub /(\*\*(\/|$)|\*)/ do |pattern|
|
113
|
+
i += 1
|
114
|
+
field = klass.filepath_fields[i]
|
115
|
+
unless matchers[field].is_a? Regexp or matchers[field].nil?
|
116
|
+
matchers[field]
|
117
|
+
else
|
118
|
+
pattern
|
121
119
|
end
|
122
120
|
end
|
121
|
+
end
|
123
122
|
|
124
|
-
|
125
|
-
|
126
|
-
|
127
|
-
|
128
|
-
end
|
123
|
+
klass.before :save do |entry|
|
124
|
+
unless entry.file
|
125
|
+
path = klass.path(entry.filepath_data)
|
126
|
+
entry.file = path unless path =~ /[\*\[\?\{]/
|
129
127
|
end
|
130
128
|
end
|
131
129
|
end
|
130
|
+
|
132
131
|
end
|
133
132
|
end
|