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.
@@ -0,0 +1,157 @@
1
+ =begin
2
+ Extention to get field value depend on user locale.
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 field value depend on user locale.
23
+ #
24
+ # You can’t use this filter for texts, because you need to set hash of
25
+ # language code to translated string. For example in YAML fiels:
26
+ #
27
+ # title:
28
+ # en: Title
29
+ # ru: Заголовок
30
+ #
31
+ # Then just set filter to `virtual` or `field`.
32
+ #
33
+ # class Post
34
+ # include PlainRecord::Resource
35
+ # include PlainRecord::Extra::I18n
36
+ #
37
+ # field :title, i18n
38
+ # end
39
+ #
40
+ # By default, this filter will use current locale from Rails I18n or R18n,
41
+ # if they are defined. If you need to take locale from another space, just
42
+ # redefine `locale` model method.
43
+ #
44
+ # class Post
45
+ # def locale
46
+ # ENV['locale']
47
+ # end
48
+ # end
49
+ module I18n
50
+ class << self
51
+ def included(base)
52
+ base.send :extend, Model
53
+ end
54
+ end
55
+
56
+ # Return default locale. By default it look in R18n or Rails I18n.
57
+ # Redefine it if you need another logic.
58
+ def locale
59
+ if defined? ::R18n::I18n
60
+ ::R18n.get
61
+ elsif defined? ::I18n
62
+ ::I18n.locale
63
+ else
64
+ raise "Can't find R18n or I18n. Redefine `locale` method."
65
+ end
66
+ end
67
+
68
+ # Return locale code depend on autodetected I18n library.
69
+ def locale_code
70
+ code = locale
71
+ if defined? R18n::I18n and code.is_a? R18n::I18n
72
+ code.locale.code
73
+ elsif code.is_a? Symbol
74
+ code.to_s
75
+ else
76
+ code
77
+ end
78
+ end
79
+
80
+ # Return subvalue of `hash` depend on user locale from `locale` method.
81
+ #
82
+ # If R18n or Rails I18n is loaded it will use them logic to translate.
83
+ def get_translation(name, hash, *params)
84
+ return hash unless hash.is_a? Hash
85
+ path = "#{self.class.name}##{name}"
86
+
87
+ if defined? R18n::I18n and locale.is_a? R18n::I18n
88
+ r18n = locale
89
+ r18n.locales.each do |lang|
90
+ code = lang.code
91
+ next unless hash.has_key? code
92
+
93
+ result = hash[code]
94
+ type = self.class.fields_i18n_types[name.to_sym]
95
+
96
+ if type
97
+ return r18n.filter_list.
98
+ process(:all, type, result, lang, path, params)
99
+ elsif result.is_a? String
100
+ result = ::R18n::TranslatedString.new(result, lang, path)
101
+ return r18n.filter_list.process_string(:all, result, path, params)
102
+ else
103
+ return result
104
+ end
105
+ end
106
+
107
+ ::R18n::Untranslated.new("#{self.class.name}#", name,
108
+ locale.locale, locale.filter_list)
109
+
110
+ elsif defined? ::I18n
111
+ hash[locale.to_s]
112
+
113
+ else
114
+ hash[locale]
115
+ end
116
+ end
117
+
118
+ module Model
119
+ # R18n type for fields.
120
+ attr_accessor :fields_i18n_types
121
+
122
+ private
123
+
124
+ # Filter to return value depend on model locale.
125
+ #
126
+ # If you use R18n, you can set `type` to specify filters.
127
+ #
128
+ # field comment_count, i18n('pl')
129
+ def i18n(r18n_type = nil)
130
+ proc do |model, field, type|
131
+ if :text == type
132
+ raise ArgumentError, "You can't set i18n filter for text"
133
+ end
134
+
135
+ model.fields_i18n_types ||= { }
136
+ model.fields_i18n_types[field] = r18n_type if r18n_type
137
+
138
+ model.send :alias_method, "untraslated_#{field}", field
139
+ model.send :alias_method, "untraslated_#{field}=", "#{field}="
140
+
141
+ model.add_accessors <<-EOS, __FILE__, __LINE__
142
+ def #{field}(*params)
143
+ get_translation("#{field}", super, *params)
144
+ end
145
+ def #{field}=(value)
146
+ if untraslated_#{field}
147
+ untraslated_#{field}[locale_code] = value
148
+ else
149
+ super({ locale_code => value })
150
+ end
151
+ end
152
+ EOS
153
+ end
154
+ end
155
+ end
156
+ end
157
+ end
@@ -1,7 +1,8 @@
1
1
  =begin
2
- Extention to get property from entry file path.
2
+ Extention to get field from entry file path.
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
@@ -18,18 +19,18 @@ along with this program. If not, see <http://www.gnu.org/licenses/>.
18
19
  =end
19
20
 
20
21
  module PlainRecord
21
- # Extention to get properties from enrty file path. For example, your blog
22
+ # Extention to get fields from enrty file path. For example, your blog
22
23
  # 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
+ # field. Also if you set name field to Model#first or Model#all method,
24
25
  # they will load entry directly only by it file.
25
26
  #
26
- # To define filepath property:
27
+ # To define filepath field:
27
28
  # 1. Use <tt>*</tt> or <tt>**</tt> pattern in model path in +enrty_in+ or
28
29
  # +list_in+.
29
- # 2. In +virtual+ method use <tt>in_filepath(i)</tt> definer after name with
30
+ # 2. In +virtual+ method use <tt>in_filepath(i)</tt> filter after name with
30
31
  # <tt>*</tt> or <tt>**</tt> number (start from 1).
31
32
  #
32
- # Define filepath property only after +entry_in+ or +list_in+ call.
33
+ # Define filepath field only after +entry_in+ or +list_in+ call.
33
34
  #
34
35
  # class Post
35
36
  # include PlainRecord::Resource
@@ -48,21 +49,31 @@ module PlainRecord
48
49
  #
49
50
  # bests = Post.all(category: 'best') # Look up only in best/ dir
50
51
  module Filepath
51
- attr_accessor :filepath_properties
52
+ attr_accessor :filepath_fields
52
53
  attr_accessor :filepath_regexp
53
54
 
54
55
  private
55
56
 
56
- # Return definer for filepath property for +number+ <tt>*</tt> or
57
+ # Return filter for filepath field for +number+ <tt>*</tt> or
57
58
  # <tt>**</tt> pattern in path.
58
59
  def in_filepath(number)
59
- proc do |property, caller|
60
- if :virtual != caller
61
- raise ArgumentError, "You must create filepath property #{property}" +
60
+ proc do |model, field, type|
61
+ if :virtual != type
62
+ raise ArgumentError, "You must create filepath field #{field}" +
62
63
  ' virtual creator'
63
64
  end
64
- Filepath.define_property(self, property, number)
65
- nil
65
+
66
+ Filepath.install(model) unless model.filepath_fields
67
+ model.filepath_fields[number] = field
68
+
69
+ model.add_accessors <<-EOS, __FILE__, __LINE__
70
+ def #{field}
71
+ @filepath_data[:#{field}]
72
+ end
73
+ def #{field}=(value)
74
+ @filepath_data[:#{field}] = value
75
+ end
76
+ EOS
66
77
  end
67
78
  end
68
79
 
@@ -70,7 +81,7 @@ module PlainRecord
70
81
  # Define class variables and events in +klass+. It should be call once on
71
82
  # same class after +entry_in+ or +list_in+ call.
72
83
  def install(klass)
73
- klass.filepath_properties = { }
84
+ klass.filepath_fields = { }
74
85
 
75
86
  path = Regexp.escape(klass.path).gsub(/\\\*\\\*(\/|$)/, '(.*)').
76
87
  gsub('\\*', '([^/]+)')
@@ -84,12 +95,12 @@ module PlainRecord
84
95
  if entry.path
85
96
  data = klass.filepath_regexp.match(entry.path)
86
97
  entry.filepath_data = { }
87
- klass.filepath_properties.each_pair do |number, name|
98
+ klass.filepath_fields.each_pair do |number, name|
88
99
  entry.filepath_data[name] = data[number]
89
100
  end
90
101
  else
91
102
  entry.filepath_data = { }
92
- klass.filepath_properties.each_value do |name|
103
+ klass.filepath_fields.each_value do |name|
93
104
  entry.filepath_data[name] = entry.data[name]
94
105
  entry.data.delete(name)
95
106
  end
@@ -101,9 +112,9 @@ module PlainRecord
101
112
  i = 0
102
113
  path.gsub /(\*\*(\/|$)|\*)/ do |pattern|
103
114
  i += 1
104
- property = klass.filepath_properties[i]
105
- unless matchers[property].is_a? Regexp or matchers[property].nil?
106
- matchers[property]
115
+ field = klass.filepath_fields[i]
116
+ unless matchers[field].is_a? Regexp or matchers[field].nil?
117
+ matchers[field]
107
118
  else
108
119
  pattern
109
120
  end
@@ -117,25 +128,6 @@ module PlainRecord
117
128
  end
118
129
  end
119
130
  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
131
  end
140
132
  end
141
133
  end
@@ -1,7 +1,8 @@
1
1
  =begin
2
2
  Class with static methods for entry_in model class.
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
@@ -1,7 +1,8 @@
1
1
  =begin
2
2
  Class with static methods for list_in model class.
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
@@ -1,7 +1,8 @@
1
1
  =begin
2
2
  Class with static methods for model class.
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
@@ -28,16 +29,18 @@ module PlainRecord
28
29
  autoload :List, (dir + 'list').to_s
29
30
 
30
31
  include PlainRecord::Callbacks
32
+ include PlainRecord::Default
31
33
  include PlainRecord::Filepath
32
34
  include PlainRecord::Associations
35
+ include PlainRecord::Type
33
36
 
34
- # YAML properties names.
35
- attr_accessor :properties
37
+ # YAML fields names.
38
+ attr_accessor :fields
36
39
 
37
- # Name of special properties with big text.
40
+ # Name of special fields with big text.
38
41
  attr_accessor :texts
39
42
 
40
- # Properties names with dynamic value.
43
+ # Fields names with dynamic value.
41
44
  attr_accessor :virtuals
42
45
 
43
46
  # Storage type: +:entry+ or +:list+.
@@ -46,11 +49,15 @@ module PlainRecord
46
49
  # Content of already loaded files.
47
50
  attr_accessor :loaded
48
51
 
49
- def self.extended(base) #:nodoc:
50
- base.properties = []
51
- base.virtuals = []
52
- base.texts = []
53
- base.loaded = { }
52
+ # Named modules, created by +add_accessors+.
53
+ attr_accessor :accessors_modules
54
+
55
+ def self.extended(base)
56
+ base.fields = []
57
+ base.virtuals = []
58
+ base.texts = []
59
+ base.loaded = { }
60
+ base.accessors_modules = { }
54
61
  end
55
62
 
56
63
  # Load and return all entries in +file+.
@@ -87,7 +94,7 @@ module PlainRecord
87
94
  # Return all entries, which is match for +matchers+ and return true on
88
95
  # +block+.
89
96
  #
90
- # Matchers is a Hash with property name in key and String or Regexp for
97
+ # Matchers is a Hash with field name in key and String or Regexp for
91
98
  # match in value.
92
99
  #
93
100
  # Post.all(title: 'Post title')
@@ -103,7 +110,7 @@ module PlainRecord
103
110
  # Return first entry, which is match for +matchers+ and return true on
104
111
  # +block+.
105
112
  #
106
- # Matchers is a Hash with property name in key and String or Regexp for
113
+ # Matchers is a Hash with field name in key and String or Regexp for
107
114
  # match in value.
108
115
  #
109
116
  # Post.first(title: 'Post title')
@@ -137,6 +144,50 @@ module PlainRecord
137
144
  end
138
145
  end
139
146
 
147
+ # Create new anonymous module and include in model.
148
+ #
149
+ # You can set +name+ and it will old module, if it was created with same
150
+ # name.
151
+ #
152
+ # It is helper to create model fields accessors and filters for it with
153
+ # +super+ support.
154
+ #
155
+ # add_accessors <<-EOS, __FILE__, __LINE__
156
+ # def #{name}
157
+ # @data['#{name}']
158
+ # end
159
+ # EOS
160
+ def add_accessors(name = nil, file = nil, line = nil, code = nil)
161
+ if name.is_a? String
162
+ code = line
163
+ line = file
164
+ file = name
165
+ name = nil
166
+ end
167
+
168
+ if file and code.nil?
169
+ code = file
170
+ file = line = nil
171
+ end
172
+
173
+ if name and @accessors_modules.has_key? name
174
+ mod = @accessors_modules[name]
175
+ else
176
+ mod = Module.new
177
+ if name
178
+ @accessors_modules[name] = mod
179
+ end
180
+ include mod
181
+ end
182
+
183
+ if file and code
184
+ mod.module_eval(file, line, code)
185
+ elsif code
186
+ mod.module_eval(code)
187
+ end
188
+ mod
189
+ end
190
+
140
191
  private
141
192
 
142
193
  # Return all model entries, which is may be match for +matchers+.
@@ -200,13 +251,12 @@ module PlainRecord
200
251
  self.extend PlainRecord::Model::List
201
252
  end
202
253
 
203
- # Add virtual property with some +name+ to model. It value willn’t be in
254
+ # Add virtual field with some +name+ to model. It value willn’t be in
204
255
  # file and will be calculated dynamically.
205
256
  #
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.
257
+ # You _must_ provide your own define logic by +filters+. Filter Proc
258
+ # will be call with models class as first argument, field name as second and
259
+ # field type as second.
210
260
  #
211
261
  # class Post
212
262
  # include PlainRecord::Resource
@@ -215,61 +265,53 @@ module PlainRecord
215
265
  #
216
266
  # virtual :name, in_filepath(1)
217
267
  # end
218
- def virtual(name, *definers)
268
+ def virtual(name, *filters)
219
269
  @virtuals ||= []
220
270
  @virtuals << name
221
271
 
222
- accessors = call_definers(definers, name, :virtual)
223
-
224
- if accessors[:reader] or accessors[:writer]
272
+ if filters.length.zero?
225
273
  raise ArgumentError, 'You must provide you own accessors for virtual ' +
226
- "property #{name}"
274
+ "field #{name} by any filter"
227
275
  end
276
+
277
+ field_filters(filters, name, :virtual)
228
278
  end
229
279
 
230
- # Add property with some +name+ to model. It will be stored as YAML.
280
+ # Add field with some +name+ to model. It will be stored as YAML.
231
281
  #
232
- # You can provide your own define logic by +definers+. Definer Proc
233
- # will be call with property name in first argument and may return
234
- # +:accessor+, +:writer+ or +:reader+ this method create standard methods
235
- # to access to property.
282
+ # You may provide your own define logic by +filters+. Filter Proc will be
283
+ # call with models class as first argument, field name as second and
284
+ # field type as second.
236
285
  #
237
286
  # class Post
238
287
  # include PlainRecord::Resource
239
288
  #
240
289
  # entry_in 'posts/*/post.md'
241
290
  #
242
- # property :title
291
+ # field :title
243
292
  # end
244
- def property(name, *definers)
245
- @properties ||= []
246
- @properties << name
247
-
248
- accessors = call_definers(definers, name, :property)
249
-
250
- if accessors[:reader]
251
- class_eval <<-EOS, __FILE__, __LINE__
252
- def #{name}
253
- @data['#{name}']
254
- end
255
- EOS
256
- end
257
- if accessors[:writer]
258
- class_eval <<-EOS, __FILE__, __LINE__
259
- def #{name}=(value)
260
- @data['#{name}'] = value
261
- end
262
- EOS
263
- end
293
+ def field(name, *filters)
294
+ @fields ||= []
295
+ @fields << name
296
+
297
+ add_accessors :main, <<-EOS, __FILE__, __LINE__
298
+ def #{name}(*params)
299
+ @data['#{name}']
300
+ end
301
+ def #{name}=(value)
302
+ @data['#{name}'] = value
303
+ end
304
+ EOS
305
+
306
+ field_filters(filters, name, :field)
264
307
  end
265
308
 
266
- # Add special property with big text (for example, blog entry content). It
267
- # will stored after 3 dashes (<tt>---</tt>).
309
+ # Add special field with big text (for example, blog entry content).
310
+ # It will stored after 3 dashes (<tt>---</tt>).
268
311
  #
269
- # You can provide your own define logic by +definers+. Definer Proc
270
- # will be call with property name in first argument and may return
271
- # +:accessor+, +:writer+ or +:reader+ this method create standard methods
272
- # to access to property.
312
+ # You may provide your own define logic by +filter+. Filter Proc will be
313
+ # call with models class as first argument, field name as second and
314
+ # field type as second.
273
315
  #
274
316
  # Note, that text is supported by only +entry_in+ models, which entry store
275
317
  # in separated files.
@@ -283,9 +325,9 @@ module PlainRecord
283
325
  #
284
326
  # entry_in 'posts/*/post.md'
285
327
  #
286
- # property :title
287
- # text :summary
288
- # text :content
328
+ # field :title
329
+ # text :summary
330
+ # text :content
289
331
  # end
290
332
  #
291
333
  # File:
@@ -295,7 +337,7 @@ module PlainRecord
295
337
  # Post summary
296
338
  # ---
297
339
  # Post text
298
- def text(name, *definers)
340
+ def text(name, *filters)
299
341
  if :list == @storage
300
342
  raise ArgumentError, 'Text is supported by only entry_in models'
301
343
  end
@@ -304,41 +346,28 @@ module PlainRecord
304
346
  @texts << name
305
347
  number = @texts.length - 1
306
348
 
307
- accessors = call_definers(definers, name, :text)
349
+ add_accessors :main, <<-EOS, __FILE__, __LINE__
350
+ def #{name}
351
+ @texts[#{number}]
352
+ end
353
+ def #{name}=(value)
354
+ @texts[#{number}] = value
355
+ end
356
+ EOS
308
357
 
309
- if accessors[:reader]
310
- class_eval <<-EOS, __FILE__, __LINE__
311
- def #{name}
312
- @texts[#{number}]
313
- end
314
- EOS
315
- end
316
- if accessors[:writer]
317
- class_eval <<-EOS, __FILE__, __LINE__
318
- def #{name}=(value)
319
- @texts[#{number}] = value
320
- end
321
- EOS
322
- end
358
+ field_filters(filters, name, :text)
323
359
  end
324
360
 
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
327
- # be created as standart by +property+ or +text+ method.
328
- def call_definers(definers, name, caller)
329
- accessors = { :reader => true, :writer => true }
330
-
331
- definers.each do |definer|
332
- access = definer.call(name, caller)
333
- if :writer == access or access.nil?
334
- accessors[:reader] = false
335
- end
336
- if :reader == access or access.nil?
337
- accessors[:writer] = false
361
+ # Call all +filters+ for some +field+ with +type+.
362
+ def field_filters(filters, field, type)
363
+ filters.each do |filter|
364
+ if filter.is_a? Hash
365
+ filter = filter.map { |name, param| send(name, param) }
366
+ field_filters(filter, field, type)
367
+ else
368
+ filter.call(self, field, type)
338
369
  end
339
370
  end
340
-
341
- accessors
342
371
  end
343
372
  end
344
373
  end
@@ -1,7 +1,8 @@
1
1
  =begin
2
2
  Module to be included into model class.
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
@@ -29,7 +30,7 @@ module PlainRecord
29
30
  # * <tt>save(entry)</tt> – write entry to file.
30
31
  # See PlainRecord::Callbacks for details.
31
32
  #
32
- # You can define properties from entry file path, by +in_filepath+ definer.
33
+ # You can define fields from entry file path, by +in_filepath+ filter.
33
34
  # See PlainRecord::Filepath for details.
34
35
  #
35
36
  # class Post
@@ -42,18 +43,18 @@ module PlainRecord
42
43
  # end
43
44
  #
44
45
  # virtual :name, in_filepath(1)
45
- # property :title
46
- # text :summary
47
- # text :content
46
+ # field :title
47
+ # text :summary
48
+ # text :content
48
49
  # end
49
50
  module Resource
50
51
  class << self
51
- def included(base) #:nodoc:
52
+ def included(base)
52
53
  base.send :extend, Model
53
54
  end
54
55
  end
55
56
 
56
- # Properties values.
57
+ # Fields values.
57
58
  attr_reader :data
58
59
 
59
60
  # Texts values.
@@ -102,7 +103,7 @@ module PlainRecord
102
103
  self.file = self.class.path
103
104
  else
104
105
  raise ArgumentError, "There isn't file to save entry. " +
105
- "Set filepath properties or file."
106
+ "Set filepath fields or file."
106
107
  end
107
108
  end
108
109
 
@@ -122,7 +123,7 @@ module PlainRecord
122
123
  @data.to_yaml(opts)
123
124
  end
124
125
 
125
- # Compare if its properties and texts are equal.
126
+ # Compare if its fields and texts are equal.
126
127
  def eql?(other)
127
128
  return false unless other.kind_of?(self.class)
128
129
  @file == other.file and @data == other.data and @texts == @texts