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 CHANGED
@@ -1,237 +1,29 @@
1
1
  # CsvRecord
2
2
 
3
- [![Build Status](https://secure.travis-ci.org/lukasalexandre/csv_record.png)](http://travis-ci.org/lukasalexandre/csv_record) [![Code Climate](https://codeclimate.com/badge.png)](https://codeclimate.com/github/lukasalexandre/csv_record) [![Dependency Status](https://gemnasium.com/lukasalexandre/csv_record.png)](https://gemnasium.com/lukasalexandre/csv_record)
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
- car.company = company
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
- `validate`: Uses custom method(s) to validate the model
8
+ #Information
198
9
 
199
- ```ruby
200
- class Company
201
- include CsvRecord::Document
10
+ ##The CSV_Record wiki
202
11
 
203
- attr_accessor :name
12
+ The CSV_Record Wiki has lots of additional information about CSV_Record. Please browse the Wiki after finishing this README:
204
13
 
205
- validates_presence_of :name
206
- validates_uniqueness_of :name
14
+ wiki.github.com/lukasalexandre/csv_record
207
15
 
208
- validate :my_custom_validator_method
16
+ ##Bug reports
209
17
 
210
- validate do
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
- def my_custom_validator_method
215
- self.errors.add :attribute
216
- end
217
- end
20
+ ##Contributing
218
21
 
219
- company = Company.create
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
- company = Company.create
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
@@ -23,4 +23,5 @@ Gem::Specification.new do |gem|
23
23
  gem.add_development_dependency 'pry'
24
24
  gem.add_development_dependency 'timecop'
25
25
  gem.add_development_dependency 'turn'
26
+ gem.add_development_dependency 'minitest'
26
27
  end
@@ -1,40 +1,38 @@
1
- module CsvRecord
2
- module Associations
3
- def belongs_to(klass)
4
- klass_name = klass.to_s
1
+ module CsvRecord::Associations
2
+ def belongs_to(klass)
3
+ klass_name = klass.to_s
5
4
 
6
- self.class_eval do
7
- self.send :attr_writer, "#{klass}_id"
5
+ self.class_eval do
6
+ self.send :attr_writer, "#{klass}_id"
8
7
 
9
- define_method klass do
10
- klass_name.to_class.find self.id
11
- end
12
- define_method "#{klass}=" do |value|
13
- self.send "#{klass}_id=", value.to_param
14
- end
15
- define_method "#{klass}_id" do
16
- eval("@#{klass}_id").to_i
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
- def has_many(klass)
22
- self.class_eval do
23
- define_method klass do
24
- klass.to_s.to_class.where :"#{self.underscored_class_name}_id" => self.id
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
- def has_one(klass)
30
- self.class_eval do
31
- define_method "#{klass}=" do |obj|
32
- obj.send "#{self.underscored_class_name}_id=", self.id
33
- obj.save
34
- end
35
- define_method klass do
36
- klass.to_s.to_class.where("#{self.underscored_class_name}_id" => self.id).first
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
@@ -0,0 +1,12 @@
1
+ class CsvRecord::Callback
2
+ attr_accessor :kind, :code
3
+
4
+ def initialize(kind, code)
5
+ self.kind = kind
6
+ self.code = code
7
+ end
8
+
9
+ def run_on(obj)
10
+ obj.instance_eval &self.code
11
+ end
12
+ end
@@ -1,97 +1,99 @@
1
- module CsvRecord
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
- module ClassMethods
19
- CALLBACKS.each do |callback|
20
- define_method callback do |*args, &block|
21
- const_variable = "#{callback}_callbacks".upcase
22
- const_set(const_variable, []) unless const_defined? const_variable
23
- const_get(const_variable) << block if block
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
- def __where__(*args)
28
- results = super
29
- results.each &:run_after_find_callbacks
30
- results
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
- module InstanceMethods
35
- CALLBACKS.each do |callback|
36
- define_method "run_#{callback}_callbacks" do
37
- const_variable = "#{callback}_callbacks".upcase
38
- if self.class.const_defined? const_variable
39
- callbacks_collection = self.class.const_get("#{callback}_callbacks".upcase)
40
- callbacks_collection.each do |callback_proc|
41
- self.instance_eval &callback_proc
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
- [:build, :initialize].each do |initialize_method|
48
- define_method initialize_method do |*args|
49
- result = super(*args)
50
- self.run_after_initialize_callbacks
51
- result
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
- def destroy
63
- self.run_before_destroy_callbacks
64
- is_destroyed = super
65
- self.run_after_destroy_callbacks if is_destroyed
66
- is_destroyed
67
- end
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
- def save(*args)
70
- self.run_before_save_callbacks
71
- is_saved = super
72
- self.run_after_save_callbacks if is_saved
73
- is_saved
74
- end
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
- def append_registry
77
- self.run_before_create_callbacks
78
- is_saved = super
79
- self.run_after_create_callbacks if is_saved
80
- is_saved
81
- end
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
- def update_registry
84
- self.run_before_update_callbacks
85
- saved = super
86
- self.run_after_destroy_callbacks if saved
87
- self.run_after_update_callbacks if saved
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 self.included(receiver)
93
- receiver.extend ClassMethods
94
- receiver.send :include, InstanceMethods
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
@@ -1,61 +1,67 @@
1
- module CsvRecord
2
- module Connector
3
- DATABASE_FOLDER = 'db'.freeze
4
- APPEND_MODE = 'a'.freeze
5
- WRITE_MODE = 'wb'.freeze
6
- READ_MODE = 'r'.freeze
7
-
8
- def __initialize_db_directory__
9
- unless Dir.exists? DATABASE_FOLDER
10
- Dir.mkdir DATABASE_FOLDER
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
- def __initialize_db__
15
- __initialize_db_directory__
16
- unless db_initialized?
17
- open_database_file WRITE_MODE do |csv|
18
- csv << fields
19
- end
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
- def db_initialized?
24
- File.exist? self.const_get('DATABASE_LOCATION')
25
- end
24
+ # Checks wheter the database file exists
25
+ def db_initialized?
26
+ File.exist? self.const_get('DATABASE_LOCATION')
27
+ end
26
28
 
27
- def __open_database_file__(mode=READ_MODE)
28
- __initialize_db__ if mode == READ_MODE # fix this later
29
- CSV.open(self.const_get('DATABASE_LOCATION'), mode, headers: true) do |csv|
30
- yield(csv)
31
- end
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
- def __parse_database_file__
35
- open_database_file do |csv|
36
- CSV.open(self.const_get('DATABASE_LOCATION_TMP'), WRITE_MODE, headers: true) do |copy|
37
- copy << fields
38
- csv.entries.each do |entry|
39
- new_row = yield(entry)
40
- copy << new_row if new_row
41
- end
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
- protected
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
- alias :initialize_db_directory :__initialize_db_directory__
57
- alias :initialize_db :__initialize_db__
58
- alias :open_database_file :__open_database_file__
59
- alias :parse_database_file :__parse_database_file__
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