csv_record 1.9.0 → 2.0.0
Sign up to get free protection for your applications and to get access to all the features.
- data/README.md +13 -221
- data/csv_record.gemspec +1 -0
- data/lib/csv_record/associations.rb +27 -29
- data/lib/csv_record/callback.rb +12 -0
- data/lib/csv_record/callbacks.rb +79 -77
- data/lib/csv_record/connector.rb +52 -46
- data/lib/csv_record/csv_queries/condition.rb +18 -0
- data/lib/csv_record/csv_queries/query.rb +73 -0
- data/lib/csv_record/csv_validations/custom_validation.rb +20 -0
- data/lib/csv_record/csv_validations/presence_validation.rb +13 -0
- data/lib/csv_record/csv_validations/uniqueness_validation.rb +16 -0
- data/lib/csv_record/csv_validations/validations.rb +87 -0
- data/lib/csv_record/document.rb +18 -21
- data/lib/csv_record/reader.rb +76 -97
- data/lib/csv_record/timestamps.rb +29 -29
- data/lib/csv_record/version.rb +1 -1
- data/lib/csv_record/writer.rb +78 -80
- data/test/csv_record/callbacks_test.rb +1 -1
- data/test/csv_record/condition_test.rb +35 -0
- data/test/csv_record/connector_test.rb +11 -11
- data/test/csv_record/query_test.rb +58 -0
- data/test/csv_record/reader_test.rb +64 -75
- data/test/csv_record/timestamps_test.rb +9 -16
- data/test/csv_record/validation_test.rb +8 -29
- data/test/csv_record/writer_test.rb +51 -65
- data/test/models/jedi.rb +1 -2
- data/test/test_helper.rb +1 -11
- metadata +30 -10
- data/lib/csv_record/validations.rb +0 -83
- data/test/csv_record/csv_record_test.rb +0 -8
- data/test/csv_record/document_test.rb +0 -6
- data/test/models/car.rb +0 -11
data/README.md
CHANGED
@@ -1,237 +1,29 @@
|
|
1
1
|
# CsvRecord
|
2
2
|
|
3
|
-
[![Build Status](https://
|
3
|
+
[[![Build Status](https://travis-ci.org/lukelex/csv_record.png?branch=2.0.0)](https://travis-ci.org/lukelex/csv_record) [![Code Climate](https://codeclimate.com/badge.png)](https://codeclimate.com/github/lukelex/csv_record) [![Dependency Status](https://gemnasium.com/lukasalexandre/csv_record.png)](https://gemnasium.com/lukasalexandre/csv_record) [![Gem Version](https://fury-badge.herokuapp.com/rb/csv_record.png)](http://badge.fury.io/rb/csv_record)
|
4
4
|
|
5
|
-
CSV Record connects Ruby classes to CSV documents database to establish an almost zero-configuration persistence layer for applications.
|
6
|
-
|
7
|
-
## Installation
|
8
|
-
|
9
|
-
Add this line to your application's Gemfile:
|
10
|
-
|
11
|
-
```ruby
|
12
|
-
gem 'csv_record'
|
13
|
-
```
|
14
|
-
|
15
|
-
And then execute:
|
16
|
-
|
17
|
-
```
|
18
|
-
$ bundle
|
19
|
-
```
|
20
|
-
|
21
|
-
Or install it yourself as:
|
22
|
-
|
23
|
-
```
|
24
|
-
$ gem install csv_record
|
25
|
-
```
|
26
|
-
|
27
|
-
## Usage
|
28
|
-
|
29
|
-
```ruby
|
30
|
-
require 'csv_record'
|
31
|
-
|
32
|
-
class Car
|
33
|
-
include CsvRecord::Document
|
34
|
-
|
35
|
-
attr_accessor :year, :make, :model, :description, :price
|
36
|
-
end
|
37
|
-
```
|
38
|
-
|
39
|
-
##Persistance
|
40
|
-
```ruby
|
41
|
-
Car.create( # save the new record in the database
|
42
|
-
year: 2007,
|
43
|
-
make: 'Chevrolet',
|
44
|
-
model: 'F450',
|
45
|
-
description: 'ac, abs, moon',
|
46
|
-
price: 5000.00
|
47
|
-
)
|
48
|
-
|
49
|
-
car.save # save the record in the database (either creating or changing)
|
50
|
-
|
51
|
-
car.update_attribute :year, 1999 # update a single field of an object
|
52
|
-
car.update_attributes year: 1999, model: 'E762' # update multiple fields at the same time
|
53
|
-
|
54
|
-
car.destroy # removes the record from the database
|
55
|
-
|
56
|
-
car.new_record? # checks if the record is new
|
57
|
-
```
|
58
|
-
|
59
|
-
##Retrieving
|
60
|
-
Records can be queried through the following methods:
|
61
|
-
|
62
|
-
```ruby
|
63
|
-
Car.all # retrieves all saved records
|
64
|
-
|
65
|
-
Car.find car.id # find through its id
|
66
|
-
Car.find car # find through the record
|
67
|
-
|
68
|
-
Car.find_by_model 'F450' # find dynamically with a property
|
69
|
-
Car.find_by_model_and_price 'F450', 5000.00 # find dynamically with multiple properties
|
70
|
-
|
71
|
-
Car.where year: 2007, make: 'Chevrolet', model: 'F450' # find with a multiple parameters hash
|
72
|
-
|
73
|
-
Car.count # returns the amount of records in the database
|
74
|
-
|
75
|
-
Car.first # retrieves the first record in the database
|
76
|
-
Car.last # retrieves the last record in the database
|
77
|
-
```
|
78
|
-
|
79
|
-
##Associations
|
80
|
-
###Belongs To
|
81
|
-
A Belongs To association can be declared through the following method:
|
82
|
-
|
83
|
-
```ruby
|
84
|
-
class Company
|
85
|
-
include CsvRecord::Document
|
86
|
-
|
87
|
-
attr_accessor :name
|
88
|
-
end
|
89
|
-
|
90
|
-
class Car
|
91
|
-
include CsvRecord::Document
|
92
|
-
|
93
|
-
belongs_to :company
|
94
|
-
end
|
95
|
-
|
96
|
-
company = Company.create :name => 'Chuts'
|
97
|
-
|
98
|
-
car = Car.new :model => 'F450'
|
99
5
|
|
100
|
-
|
101
|
-
# or
|
102
|
-
car.company_id = company.id
|
103
|
-
|
104
|
-
car.save
|
105
|
-
|
106
|
-
car.company # #<Company:0x007f9b249b24d8>
|
107
|
-
```
|
108
|
-
|
109
|
-
###Has Many
|
110
|
-
Extending the previous example, you can use the `has_many` method to establish the inverse relationship:
|
111
|
-
|
112
|
-
```ruby
|
113
|
-
class Company
|
114
|
-
include CsvRecord::Document
|
115
|
-
|
116
|
-
attr_accessor :name
|
117
|
-
|
118
|
-
has_many :cars
|
119
|
-
end
|
120
|
-
|
121
|
-
company = Company.create :name => 'Chutz'
|
122
|
-
|
123
|
-
car.company = company
|
124
|
-
car.save
|
125
|
-
|
126
|
-
company.cars # [#<Car:0x007f9b249b24d8>]
|
127
|
-
```
|
128
|
-
|
129
|
-
###Has One
|
130
|
-
The same as has_many but limited to one associated record.
|
131
|
-
|
132
|
-
```ruby
|
133
|
-
class Company
|
134
|
-
include CsvRecord::Document
|
135
|
-
|
136
|
-
attr_accessor :name
|
137
|
-
|
138
|
-
has_one :car
|
139
|
-
end
|
140
|
-
|
141
|
-
company = Company.create :name => 'Chutz'
|
142
|
-
|
143
|
-
car.save
|
144
|
-
company.car = car
|
145
|
-
|
146
|
-
company.car # #<Car:0x007f9b249b24d8>
|
147
|
-
```
|
148
|
-
|
149
|
-
##Callbacks
|
150
|
-
###Overview
|
151
|
-
Callbacks can be used to execute code on predetermined moments.
|
152
|
-
|
153
|
-
####Usage
|
154
|
-
```ruby
|
155
|
-
after_create do
|
156
|
-
self.do_something
|
157
|
-
end
|
158
|
-
```
|
159
|
-
`self` refers to the instance you are in
|
160
|
-
|
161
|
-
###Avaiable Callbacks
|
162
|
-
Here is a list with all the available callbacks, listed in the same order in which they will get called during the respective operations:
|
163
|
-
|
164
|
-
####Finding an Object
|
165
|
-
* after_initialize
|
166
|
-
* after_find
|
167
|
-
|
168
|
-
####Creating an Object
|
169
|
-
* after_initialize
|
170
|
-
* before_validation
|
171
|
-
* after_validation
|
172
|
-
* before_save
|
173
|
-
* before_create
|
174
|
-
* after_create
|
175
|
-
* after_save
|
176
|
-
|
177
|
-
####Updating an Object
|
178
|
-
* before_validation
|
179
|
-
* after_validation
|
180
|
-
* before_save
|
181
|
-
* before_update
|
182
|
-
* after_update
|
183
|
-
* after_save
|
184
|
-
|
185
|
-
####Destroying an Object
|
186
|
-
* before_destroy
|
187
|
-
* after_destroy
|
188
|
-
|
189
|
-
##Validations
|
190
|
-
|
191
|
-
Helpers available:
|
192
|
-
|
193
|
-
`validates_presence_of`: Ensures if the specified attribute(s) were filled
|
194
|
-
|
195
|
-
`validates_uniqueness_of`: Ensures that the specified attribute(s) are unique within the database
|
6
|
+
CSV Record connects Ruby classes to CSV documents database to establish an almost zero-configuration persistence layer for applications.
|
196
7
|
|
197
|
-
|
8
|
+
#Information
|
198
9
|
|
199
|
-
|
200
|
-
class Company
|
201
|
-
include CsvRecord::Document
|
10
|
+
##The CSV_Record wiki
|
202
11
|
|
203
|
-
|
12
|
+
The CSV_Record Wiki has lots of additional information about CSV_Record. Please browse the Wiki after finishing this README:
|
204
13
|
|
205
|
-
|
206
|
-
validates_uniqueness_of :name
|
14
|
+
wiki.github.com/lukasalexandre/csv_record
|
207
15
|
|
208
|
-
|
16
|
+
##Bug reports
|
209
17
|
|
210
|
-
|
211
|
-
self.errors.add :attribute
|
212
|
-
end
|
18
|
+
If you discover a problem with CSV_Record, we would like to know about it. However, we ask that you please review these guidelines before submitting a bug report:
|
213
19
|
|
214
|
-
|
215
|
-
self.errors.add :attribute
|
216
|
-
end
|
217
|
-
end
|
20
|
+
##Contributing
|
218
21
|
|
219
|
-
|
220
|
-
company.save # => false
|
22
|
+
We hope that you will consider contributing to CSV_Record. Please read this short overview for some information about how to get started:
|
221
23
|
|
222
|
-
|
223
|
-
company.valid? # => false
|
224
|
-
company.invalid? # => true
|
24
|
+
https://github.com/lukasalexandre/csv_record/wiki/Contributing
|
225
25
|
|
226
|
-
|
26
|
+
You will usually want to write tests for your changes. To run the test suite, go into CSV_Record's top-level directory and run "bundle install" and "rake". For the tests to pass.
|
227
27
|
|
228
28
|
##Precautions
|
229
|
-
CsvRecord creates a `db` folder in the root of your application. Be sure that it has permission to do so.
|
230
|
-
|
231
|
-
## Contributing
|
232
|
-
|
233
|
-
1. Fork it
|
234
|
-
2. Create your feature branch (`git checkout -b my-new-feature`)
|
235
|
-
3. Commit your changes (`git commit -am 'Added some feature'`)
|
236
|
-
4. Push to the branch (`git push origin my-new-feature`)
|
237
|
-
5. Create new Pull Request
|
29
|
+
CsvRecord creates a `db` folder in the root of your application. Be sure that it has permission to do so.
|
data/csv_record.gemspec
CHANGED
@@ -1,40 +1,38 @@
|
|
1
|
-
module CsvRecord
|
2
|
-
|
3
|
-
|
4
|
-
klass_name = klass.to_s
|
1
|
+
module CsvRecord::Associations
|
2
|
+
def belongs_to(klass)
|
3
|
+
klass_name = klass.to_s
|
5
4
|
|
6
|
-
|
7
|
-
|
5
|
+
self.class_eval do
|
6
|
+
self.send :attr_writer, "#{klass}_id"
|
8
7
|
|
9
|
-
|
10
|
-
|
11
|
-
|
12
|
-
|
13
|
-
|
14
|
-
|
15
|
-
|
16
|
-
|
17
|
-
end
|
8
|
+
define_method klass do
|
9
|
+
klass_name.to_class.find self.id
|
10
|
+
end
|
11
|
+
define_method "#{klass}=" do |value|
|
12
|
+
self.send "#{klass}_id=", value.to_param
|
13
|
+
end
|
14
|
+
define_method "#{klass}_id" do
|
15
|
+
eval("@#{klass}_id").to_i
|
18
16
|
end
|
19
17
|
end
|
18
|
+
end
|
20
19
|
|
21
|
-
|
22
|
-
|
23
|
-
|
24
|
-
|
25
|
-
end
|
20
|
+
def has_many(klass)
|
21
|
+
self.class_eval do
|
22
|
+
define_method klass do
|
23
|
+
klass.to_s.to_class.where :"#{self.underscored_class_name}_id" => self.id
|
26
24
|
end
|
27
25
|
end
|
26
|
+
end
|
28
27
|
|
29
|
-
|
30
|
-
|
31
|
-
|
32
|
-
|
33
|
-
|
34
|
-
|
35
|
-
|
36
|
-
|
37
|
-
end
|
28
|
+
def has_one(klass)
|
29
|
+
self.class_eval do
|
30
|
+
define_method "#{klass}=" do |obj|
|
31
|
+
obj.send "#{self.underscored_class_name}_id=", self.id
|
32
|
+
obj.save
|
33
|
+
end
|
34
|
+
define_method klass do
|
35
|
+
klass.to_s.to_class.where("#{self.underscored_class_name}_id" => self.id).first
|
38
36
|
end
|
39
37
|
end
|
40
38
|
end
|
data/lib/csv_record/callbacks.rb
CHANGED
@@ -1,97 +1,99 @@
|
|
1
|
-
|
2
|
-
module Callbacks
|
3
|
-
CALLBACKS = [
|
4
|
-
:after_initialize,
|
5
|
-
:after_find,
|
6
|
-
:before_validation,
|
7
|
-
:after_validation,
|
8
|
-
:before_save,
|
9
|
-
:after_save,
|
10
|
-
:after_destroy,
|
11
|
-
:before_destroy,
|
12
|
-
:before_create,
|
13
|
-
:after_create,
|
14
|
-
:before_update,
|
15
|
-
:after_update
|
16
|
-
].freeze
|
1
|
+
require 'csv_record/callback'
|
17
2
|
|
18
|
-
|
19
|
-
|
20
|
-
|
21
|
-
|
22
|
-
|
23
|
-
|
3
|
+
module CsvRecord::Callbacks
|
4
|
+
CALLBACK_TYPES = [
|
5
|
+
:after_initialize,
|
6
|
+
:after_find,
|
7
|
+
:before_validation,
|
8
|
+
:after_validation,
|
9
|
+
:before_save,
|
10
|
+
:after_save,
|
11
|
+
:after_destroy,
|
12
|
+
:before_destroy,
|
13
|
+
:before_create,
|
14
|
+
:after_create,
|
15
|
+
:before_update,
|
16
|
+
:after_update
|
17
|
+
].freeze
|
18
|
+
|
19
|
+
module ClassMethods
|
20
|
+
CALLBACK_TYPES.each do |callback_type|
|
21
|
+
define_method callback_type do |*args, &block|
|
22
|
+
const_variable = "#{callback_type}_callbacks".upcase
|
23
|
+
const_set(const_variable, []) unless const_defined? const_variable
|
24
|
+
if block
|
25
|
+
const_get(const_variable) << (CsvRecord::Callback.new callback_type, block)
|
24
26
|
end
|
25
27
|
end
|
28
|
+
end
|
26
29
|
|
27
|
-
|
28
|
-
|
29
|
-
|
30
|
-
|
31
|
-
end
|
30
|
+
def find(*args)
|
31
|
+
result = super
|
32
|
+
result.send :run_after_find_callbacks
|
33
|
+
result
|
32
34
|
end
|
35
|
+
end
|
33
36
|
|
34
|
-
|
35
|
-
|
36
|
-
|
37
|
-
|
38
|
-
|
39
|
-
|
40
|
-
|
41
|
-
|
42
|
-
end
|
37
|
+
module InstanceMethods
|
38
|
+
CALLBACK_TYPES.each do |callback|
|
39
|
+
define_method "run_#{callback}_callbacks" do
|
40
|
+
const_variable = "#{callback}_callbacks".upcase
|
41
|
+
if self.class.const_defined? const_variable
|
42
|
+
callbacks_collection = self.class.const_get(const_variable)
|
43
|
+
callbacks_collection.each do |callback|
|
44
|
+
callback.run_on(self)
|
43
45
|
end
|
44
46
|
end
|
45
47
|
end
|
48
|
+
end
|
46
49
|
|
47
|
-
|
48
|
-
|
49
|
-
|
50
|
-
|
51
|
-
|
52
|
-
end
|
53
|
-
end
|
54
|
-
|
55
|
-
def valid?
|
56
|
-
self.run_before_validation_callbacks
|
57
|
-
is_valid = super
|
58
|
-
self.run_after_validation_callbacks if is_valid
|
59
|
-
is_valid
|
50
|
+
[:build, :initialize].each do |initialize_method|
|
51
|
+
define_method initialize_method do |*args|
|
52
|
+
result = super(*args)
|
53
|
+
self.run_after_initialize_callbacks
|
54
|
+
result
|
60
55
|
end
|
56
|
+
end
|
61
57
|
|
62
|
-
|
63
|
-
|
64
|
-
|
65
|
-
|
66
|
-
|
67
|
-
|
58
|
+
def valid?
|
59
|
+
self.run_before_validation_callbacks
|
60
|
+
is_valid = super
|
61
|
+
self.run_after_validation_callbacks if is_valid
|
62
|
+
is_valid
|
63
|
+
end
|
68
64
|
|
69
|
-
|
70
|
-
|
71
|
-
|
72
|
-
|
73
|
-
|
74
|
-
|
65
|
+
def destroy
|
66
|
+
self.run_before_destroy_callbacks
|
67
|
+
is_destroyed = super
|
68
|
+
self.run_after_destroy_callbacks if is_destroyed
|
69
|
+
is_destroyed
|
70
|
+
end
|
75
71
|
|
76
|
-
|
77
|
-
|
78
|
-
|
79
|
-
|
80
|
-
|
81
|
-
|
72
|
+
def save(*args)
|
73
|
+
self.run_before_save_callbacks
|
74
|
+
is_saved = super
|
75
|
+
self.run_after_save_callbacks if is_saved
|
76
|
+
is_saved
|
77
|
+
end
|
82
78
|
|
83
|
-
|
84
|
-
|
85
|
-
|
86
|
-
|
87
|
-
|
88
|
-
saved
|
89
|
-
end
|
79
|
+
def append_registry
|
80
|
+
self.run_before_create_callbacks
|
81
|
+
is_saved = super
|
82
|
+
self.run_after_create_callbacks if is_saved
|
83
|
+
is_saved
|
90
84
|
end
|
91
85
|
|
92
|
-
def
|
93
|
-
|
94
|
-
|
86
|
+
def update_registry
|
87
|
+
self.run_before_update_callbacks
|
88
|
+
saved = super
|
89
|
+
self.run_after_destroy_callbacks if saved
|
90
|
+
self.run_after_update_callbacks if saved
|
91
|
+
saved
|
95
92
|
end
|
96
93
|
end
|
94
|
+
|
95
|
+
def self.included(receiver)
|
96
|
+
receiver.extend ClassMethods
|
97
|
+
receiver.send :include, InstanceMethods
|
98
|
+
end
|
97
99
|
end
|
data/lib/csv_record/connector.rb
CHANGED
@@ -1,61 +1,67 @@
|
|
1
|
-
module CsvRecord
|
2
|
-
|
3
|
-
|
4
|
-
|
5
|
-
|
6
|
-
|
7
|
-
|
8
|
-
|
9
|
-
|
10
|
-
|
11
|
-
end
|
1
|
+
module CsvRecord::Connector
|
2
|
+
DATABASE_FOLDER = 'db'.freeze
|
3
|
+
APPEND_MODE = 'a'.freeze
|
4
|
+
WRITE_MODE = 'wb'.freeze
|
5
|
+
READ_MODE = 'r'.freeze
|
6
|
+
|
7
|
+
# Checks wheter the database directory exists
|
8
|
+
def __initialize_db_directory__
|
9
|
+
unless Dir.exists? DATABASE_FOLDER
|
10
|
+
Dir.mkdir DATABASE_FOLDER
|
12
11
|
end
|
12
|
+
end
|
13
13
|
|
14
|
-
|
15
|
-
|
16
|
-
|
17
|
-
|
18
|
-
|
19
|
-
|
14
|
+
# Initialize the database file with its headers
|
15
|
+
def __initialize_db__
|
16
|
+
__initialize_db_directory__
|
17
|
+
unless db_initialized?
|
18
|
+
open_database_file WRITE_MODE do |csv|
|
19
|
+
csv << fields
|
20
20
|
end
|
21
21
|
end
|
22
|
+
end
|
22
23
|
|
23
|
-
|
24
|
-
|
25
|
-
|
24
|
+
# Checks wheter the database file exists
|
25
|
+
def db_initialized?
|
26
|
+
File.exist? self.const_get('DATABASE_LOCATION')
|
27
|
+
end
|
26
28
|
|
27
|
-
|
28
|
-
|
29
|
-
|
30
|
-
|
31
|
-
|
29
|
+
# Open the database file
|
30
|
+
# Params:
|
31
|
+
# +mode+:: the operation mode (defaults to READ_MODE)
|
32
|
+
def __open_database_file__(mode=READ_MODE)
|
33
|
+
__initialize_db__ if mode == READ_MODE # fix this later
|
34
|
+
CSV.open(self.const_get('DATABASE_LOCATION'), mode, headers: true) do |csv|
|
35
|
+
yield(csv)
|
32
36
|
end
|
37
|
+
end
|
33
38
|
|
34
|
-
|
35
|
-
|
36
|
-
|
37
|
-
|
38
|
-
|
39
|
-
|
40
|
-
|
41
|
-
|
39
|
+
# Creates a modified copy of the database file with the new data and then replaces the original
|
40
|
+
def __parse_database_file__
|
41
|
+
open_database_file do |csv|
|
42
|
+
CSV.open(self.const_get('DATABASE_LOCATION_TMP'), WRITE_MODE, headers: true) do |copy|
|
43
|
+
copy << fields
|
44
|
+
csv.entries.each do |entry|
|
45
|
+
new_row = yield(entry)
|
46
|
+
copy << new_row if new_row
|
42
47
|
end
|
43
48
|
end
|
44
|
-
rename_database
|
45
49
|
end
|
50
|
+
rename_database
|
51
|
+
end
|
46
52
|
|
47
|
-
|
48
|
-
|
49
|
-
def rename_database
|
50
|
-
old_file = self.const_get('DATABASE_LOCATION')
|
51
|
-
tmp_file = self.const_get('DATABASE_LOCATION_TMP')
|
52
|
-
File.delete old_file
|
53
|
-
File.rename(tmp_file, old_file)
|
54
|
-
end
|
53
|
+
protected
|
55
54
|
|
56
|
-
|
57
|
-
|
58
|
-
|
59
|
-
|
55
|
+
# Rename the TMP database file to replace the original
|
56
|
+
def rename_database
|
57
|
+
old_file = self.const_get('DATABASE_LOCATION')
|
58
|
+
tmp_file = self.const_get('DATABASE_LOCATION_TMP')
|
59
|
+
File.delete old_file
|
60
|
+
File.rename(tmp_file, old_file)
|
60
61
|
end
|
62
|
+
|
63
|
+
alias :initialize_db_directory :__initialize_db_directory__
|
64
|
+
alias :initialize_db :__initialize_db__
|
65
|
+
alias :open_database_file :__open_database_file__
|
66
|
+
alias :parse_database_file :__parse_database_file__
|
61
67
|
end
|
@@ -0,0 +1,18 @@
|
|
1
|
+
class CsvRecord::Condition
|
2
|
+
attr_reader :field, :value
|
3
|
+
|
4
|
+
def initialize(field, value)
|
5
|
+
@field = field
|
6
|
+
@value = value
|
7
|
+
end
|
8
|
+
|
9
|
+
def self.create_from_hashes(hashes)
|
10
|
+
hashes.map do |hash|
|
11
|
+
new *hash
|
12
|
+
end
|
13
|
+
end
|
14
|
+
|
15
|
+
def to_code
|
16
|
+
"attributes['#{@field}'] == '#{@value}'"
|
17
|
+
end
|
18
|
+
end
|