csv_record 1.6.0 → 1.7.0

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/README.md CHANGED
@@ -33,13 +33,10 @@ class Car
33
33
  include CsvRecord::Document
34
34
 
35
35
  attr_accessor :year, :make, :model, :description, :price
36
-
37
- validates_presence_of :price, :model
38
36
  end
39
37
  ```
40
38
 
41
- By including this module you will gain for free the following methods:
42
-
39
+ ##Persistance
43
40
  ```ruby
44
41
  Car.create( # save the new record in the database
45
42
  year: 2007,
@@ -50,14 +47,19 @@ Car.create( # save the new record in the database
50
47
  )
51
48
 
52
49
  car.save # save the record in the database (either creating or changing)
53
- car.valid? # verifies its validity, specially if it is declared some of the available validators (e.x:validates_presence_of).
50
+
54
51
  car.update_attribute :year, 1999 # update a single field of an object
55
52
  car.update_attributes year: 1999, model: 'E762' # update multiple fields at the same time
56
53
 
57
54
  car.destroy # removes the record from the database
58
55
 
59
56
  car.new_record? # checks if the record is new
57
+ ```
58
+
59
+ ##Retrieving
60
+ Records can be queried through the following methds:
60
61
 
62
+ ```ruby
61
63
  Car.all # retrieves all saved records
62
64
 
63
65
  Car.find car.id # find through its id
@@ -77,6 +79,7 @@ Car.last # retrieves the last record in the database
77
79
  ##Associations
78
80
  ###Belongs To
79
81
  A Belongs To association can be declared through the following method:
82
+
80
83
  ```ruby
81
84
  class Company
82
85
  include CsvRecord::Document
@@ -105,6 +108,7 @@ car.company # #<Company:0x007f9b249b24d8>
105
108
 
106
109
  ###Has Many
107
110
  Extending the previous example, you can use the `has_many` method to establish the inverse relationship:
111
+
108
112
  ```ruby
109
113
  class Company
110
114
  include CsvRecord::Document
@@ -123,37 +127,61 @@ company.cars # [#<Car:0x007f9b249b24d8>]
123
127
  ```
124
128
 
125
129
  ##Callbacks
130
+ ###Overview
126
131
  Callbacks can be used to execute code on predetermined moments.
127
132
 
133
+ ####Usage
128
134
  ```ruby
129
- before_create do |obj|
130
- obj.do_something
131
- end
132
-
133
135
  after_create do |obj|
134
136
  obj.do_something
135
137
  end
136
138
  ```
137
139
  `obj` refers to the instance you are in
138
140
 
141
+ ###Avaiable Callbacks
142
+ Here is a list with all the available callbacks, listed in the same order in which they will get called during the respective operations:
143
+
144
+ ####Creating an Object
145
+ * before_create
146
+ * before_validation
147
+ * after_validation
148
+ * after_create
149
+
150
+ ####Updating an Object
151
+ * before_validation
152
+ * after_validation
153
+
139
154
  ##Validations
140
155
 
141
156
  Helpers available:
142
157
 
143
158
  `validates_presence_of`: Ensures if the specified attributes were filled
144
159
 
160
+ `validates_uniqueness_of`: Ensures that the specified attribute(s) are unique within the database
161
+
162
+ `validate`: Uses custom methods to validate the model
163
+
145
164
  ```ruby
146
165
  class Company
147
166
  include CsvRecord::Document
148
- validates_presence_of :name
167
+
149
168
  attr_accessor :name
169
+
170
+ validates_presence_of :name
171
+ validates_uniqueness_of :name
172
+
173
+ validate :my_custom_validator_method
174
+
175
+ def my_custom_validator_method
176
+ @errors = self.errors.add attribute
177
+ end
150
178
  end
151
179
 
152
- company = Company.create :name => ''
180
+ company = Company.create
153
181
  company.save
154
182
  # => false
155
183
 
156
- company = Company.create :name => ''
184
+ company = Company.create
157
185
  company.valid?
158
186
  # => false
159
187
  company.invalid?
@@ -1,12 +1,13 @@
1
1
  module CsvRecord
2
2
  module Callbacks
3
-
4
3
  CALLBACKS = [
5
- :after_create,
6
4
  :after_initialize,
7
- :after_save,
5
+ :before_validation,
6
+ :after_validation,
8
7
  :before_create,
9
8
  :before_save,
9
+ :after_create,
10
+ :after_save
10
11
  ].freeze
11
12
 
12
13
  module ClassMethods
@@ -1,15 +1,20 @@
1
1
  module CsvRecord
2
2
  module Connector
3
+ DATABASE_FOLDER = 'db'.freeze
4
+ APPEND_MODE = 'a'.freeze
5
+ WRITE_MODE = 'wb'.freeze
6
+ READ_MODE = 'r'.freeze
7
+
3
8
  def __initialize_db_directory__
4
- unless Dir.exists? 'db'
5
- Dir.mkdir 'db'
9
+ unless Dir.exists? DATABASE_FOLDER
10
+ Dir.mkdir DATABASE_FOLDER
6
11
  end
7
12
  end
8
13
 
9
14
  def __initialize_db__
10
- initialize_db_directory
15
+ __initialize_db_directory__
11
16
  unless db_initialized?
12
- open_database_file 'wb' do |csv|
17
+ open_database_file WRITE_MODE do |csv|
13
18
  csv << fields
14
19
  end
15
20
  end
@@ -19,7 +24,8 @@ module CsvRecord
19
24
  File.exist? self.const_get('DATABASE_LOCATION')
20
25
  end
21
26
 
22
- def __open_database_file__(mode='r')
27
+ def __open_database_file__(mode=READ_MODE)
28
+ __initialize_db__ if mode == READ_MODE # fix this later
23
29
  CSV.open(self.const_get('DATABASE_LOCATION'), mode, headers: true) do |csv|
24
30
  yield(csv)
25
31
  end
@@ -27,7 +33,7 @@ module CsvRecord
27
33
 
28
34
  def __parse_database_file__
29
35
  open_database_file do |csv|
30
- CSV.open(self.const_get('DATABASE_LOCATION_TMP'), 'w', headers: true) do |copy|
36
+ CSV.open(self.const_get('DATABASE_LOCATION_TMP'), WRITE_MODE, headers: true) do |copy|
31
37
  copy << fields
32
38
  csv.entries.each do |entry|
33
39
  new_row = yield(entry)
@@ -105,6 +105,14 @@ module CsvRecord
105
105
  self.id
106
106
  end
107
107
 
108
+ def ==(obj)
109
+ self.class == obj.class && self.to_param == obj.to_param
110
+ end
111
+
112
+ def !=(obj)
113
+ self.class != obj.class || self.to_param != obj.to_param
114
+ end
115
+
108
116
  alias :attributes :__attributes__
109
117
  alias :values :__values__
110
118
  alias :to_param :__to_param__
@@ -1,22 +1,34 @@
1
1
  module CsvRecord
2
2
  module Validations
3
3
  module ClassMethods
4
- attr_writer :fields_to_validate
4
+ [:presence, :uniqueness].each do |kind|
5
+ define_method "fields_to_validate_#{kind}" do
6
+ eval "@fields_to_validate_#{kind} || []"
7
+ end
8
+
9
+ define_method "__validates_#{kind}_of__" do |*attr_names|
10
+ eval "@fields_to_validate_#{kind} = attr_names"
11
+ end
5
12
 
6
- def fields_to_validate
7
- @fields_to_validate || []
13
+ eval "alias :validates_#{kind}_of :__validates_#{kind}_of__"
8
14
  end
9
15
 
10
- def __validates_presence_of__(*attr_names)
11
- @fields_to_validate = attr_names
16
+ def custom_validators
17
+ @custom_validators || []
12
18
  end
13
19
 
14
- alias :validates_presence_of :__validates_presence_of__
20
+ def validate(*args, &block)
21
+ @custom_validators ||= []
22
+ args.each { |arg| @custom_validators << arg }
23
+ @custom_validators << block if block_given?
24
+ end
15
25
  end
16
26
 
17
27
  module InstanceMethods
18
28
  def __valid__?
19
- validate_each(self.class.fields_to_validate)
29
+ trigger_presence_validations
30
+ trigger_uniqueness_validations
31
+ trigger_custom_validations
20
32
  errors.empty?
21
33
  end
22
34
 
@@ -31,13 +43,34 @@ module CsvRecord
31
43
  alias :valid? :__valid__?
32
44
 
33
45
  private
34
- def validate_each(attributes)
35
- attributes.each do |attribute|
46
+ def trigger_presence_validations
47
+ self.class.fields_to_validate_presence.each do |attribute|
36
48
  if self.public_send(attribute).nil?
37
49
  @errors = self.errors.add attribute
38
50
  end
39
51
  end
40
52
  end
53
+
54
+ def trigger_uniqueness_validations
55
+ self.class.fields_to_validate_uniqueness.each do |attribute|
56
+ condition = {}
57
+ condition[attribute] = self.public_send attribute
58
+ records = self.class.__where__ condition
59
+ if records.any? { |record| record != self }
60
+ @errors = self.errors.add attribute
61
+ end
62
+ end
63
+ end
64
+
65
+ def trigger_custom_validations
66
+ self.class.custom_validators.each do |validator|
67
+ if not validator.is_a? Proc
68
+ self.send validator
69
+ else
70
+ validator.call self
71
+ end
72
+ end
73
+ end
41
74
  end
42
75
  end
43
76
  end
@@ -1,3 +1,3 @@
1
1
  module CsvRecord
2
- VERSION = "1.6.0"
2
+ VERSION = "1.7.0"
3
3
  end
@@ -26,11 +26,15 @@ module CsvRecord
26
26
  end
27
27
 
28
28
  def __save__
29
+ result = nil
30
+ self.run_before_validation_callbacks
29
31
  if self.valid?
30
- self.new_record? ? self.append_registry : self.update_registry
32
+ result = self.new_record? ? self.append_registry : self.update_registry
31
33
  else
32
- false
34
+ result = false
33
35
  end
36
+ self.run_after_validation_callbacks
37
+ result
34
38
  end
35
39
 
36
40
  def new_record?
@@ -66,7 +70,6 @@ module CsvRecord
66
70
  end
67
71
 
68
72
  def append_registry
69
- self.class.initialize_db
70
73
  set_created_at
71
74
  write_object
72
75
  end
@@ -83,7 +86,7 @@ module CsvRecord
83
86
 
84
87
  def __write_object__
85
88
  calculate_id
86
- self.class.open_database_file 'a' do |csv|
89
+ self.class.open_database_file CsvRecord::Connector::APPEND_MODE do |csv|
87
90
  csv << values
88
91
  end
89
92
  true
@@ -19,6 +19,8 @@ describe CsvRecord::Callbacks do
19
19
 
20
20
  it ('run before_create callbacks') { klass.must_respond_to(:run_before_create_callbacks) }
21
21
  it ('run after_create callbacks') { klass.must_respond_to(:run_after_create_callbacks) }
22
+ it ('run before_validation callbacks') { klass.must_respond_to(:run_before_validation_callbacks) }
23
+ it ('run after_validation callbacks') { klass.must_respond_to(:run_after_validation_callbacks) }
22
24
  it ('run before_save callbacks') { klass.must_respond_to(:run_before_save_callbacks) }
23
25
  it ('run after_save callbacks') { klass.must_respond_to(:run_after_save_callbacks) }
24
26
  it ('run after_initialize callbacks') { klass.must_respond_to(:run_after_initialize_callbacks) }
@@ -34,5 +36,13 @@ describe CsvRecord::Callbacks do
34
36
  it 'after_create' do
35
37
  object_created.after_create_called.must_equal true
36
38
  end
39
+
40
+ it 'before_validation' do
41
+ object_created.before_validation_called.must_equal true
42
+ end
43
+
44
+ it 'after_validation' do
45
+ object_created.after_create_called.must_equal true
46
+ end
37
47
  end
38
48
  end
@@ -1,6 +1,7 @@
1
1
  require_relative '../test_helper'
2
2
 
3
3
  require_relative '../models/car'
4
+ require_relative '../models/jedi'
4
5
 
5
6
  describe CsvRecord::Reader do
6
7
  describe 'initializing class methods' do
@@ -11,6 +12,9 @@ describe CsvRecord::Reader do
11
12
  describe 'initializing instance methods' do
12
13
  it ('responds to values') { Car.new.must_respond_to :values }
13
14
  it ('responds to attributes') { Car.new.must_respond_to :attributes }
15
+ it ('responds to to_param') { Car.new.must_respond_to :to_param }
16
+ it ('responds to ==') { Car.new.must_respond_to :== }
17
+ it ('responds to !=') { Car.new.must_respond_to :!= }
14
18
  end
15
19
 
16
20
  describe 'validating the methods behavior' do
@@ -56,7 +60,7 @@ describe CsvRecord::Reader do
56
60
  Car.all.size.must_equal 2
57
61
  end
58
62
 
59
- it "Retrieves all registries" do
63
+ it "counting the registries" do
60
64
  car.save
61
65
  Car.count.must_equal 1
62
66
  second_car.save
@@ -70,6 +74,46 @@ describe CsvRecord::Reader do
70
74
  car.to_param.must_equal 1
71
75
  end
72
76
 
77
+ describe '==' do
78
+ before do
79
+ yoda.save
80
+ luke.save
81
+ jedi_council.save
82
+ end
83
+
84
+ it 'comparing with the same registry' do
85
+ (yoda == yoda).must_equal true
86
+ end
87
+
88
+ it 'comparing with a diferent registry' do
89
+ (yoda == luke).must_equal false
90
+ end
91
+
92
+ it 'comparing with another class registry' do
93
+ (yoda == jedi_council).must_equal false
94
+ end
95
+ end
96
+
97
+ describe '!=' do
98
+ before do
99
+ yoda.save
100
+ luke.save
101
+ jedi_council.save
102
+ end
103
+
104
+ it 'comparing with the same registry' do
105
+ (yoda != yoda).must_equal false
106
+ end
107
+
108
+ it 'comparing with a diferent registry' do
109
+ (yoda != luke).must_equal true
110
+ end
111
+
112
+ it 'comparing with another class registry' do
113
+ (yoda != jedi_council).must_equal true
114
+ end
115
+ end
116
+
73
117
  describe 'simple query' do
74
118
  let (:cars) { [] }
75
119
 
@@ -6,6 +6,7 @@ require_relative '../models/jedi_order'
6
6
  describe CsvRecord::Validations do
7
7
  describe 'initializing class methods' do
8
8
  it ('responds to validates_presence_of') { Jedi.must_respond_to :validates_presence_of }
9
+ it ('responds to validates_uniqueness_of') { Jedi.must_respond_to :validates_uniqueness_of }
9
10
  it ('responds to valid?') { Jedi.new.must_respond_to :valid? }
10
11
  it ('responds to invalid?') { Jedi.new.must_respond_to :invalid? }
11
12
  end
@@ -72,6 +73,38 @@ describe CsvRecord::Validations do
72
73
  invalid_jedi.valid?
73
74
  invalid_jedi.errors.length.must_equal 2
74
75
  end
76
+
77
+ it 'should contain the errors found' do
78
+ invalid_jedi.valid?
79
+ invalid_jedi.errors.must_include :name
80
+ invalid_jedi.errors.must_include :age
81
+ end
82
+ end
83
+ end
84
+
85
+ describe 'validates_uniqueness_of' do
86
+ before { yoda.save }
87
+
88
+ let :fake_yoda do
89
+ fake_yoda = Jedi.new name: 'Yoda the green', age: 238, midi_chlorians: '1k'
90
+ end
91
+
92
+ it 'can`t have the same name' do
93
+ fake_yoda.valid?.must_equal false
94
+ end
95
+ end
96
+
97
+ describe 'custom_validator' do
98
+ it ('responds to validate') { Jedi.must_respond_to :validate }
99
+
100
+ before { yoda.valid? }
101
+
102
+ it 'adding a custom validator' do
103
+ yoda.custom_validator_checker.must_equal true
104
+ end
105
+
106
+ it 'validate can have a block' do
107
+ yoda.custom_validator_checker_with_block.must_equal true
75
108
  end
76
109
  end
77
110
  end
@@ -5,11 +5,20 @@ class CallbackTestClass
5
5
  end
6
6
 
7
7
  attr_accessor :before_create_called, :after_create_called
8
+ attr_accessor :before_validation_called, :after_validation_called
8
9
 
9
10
  before_create do |obj|
10
11
  obj.before_create_called = true
11
12
  end
12
13
 
14
+ before_validation do |obj|
15
+ obj.before_validation_called = true
16
+ end
17
+
18
+ after_validation do |obj|
19
+ obj.after_validation_called = true
20
+ end
21
+
13
22
  after_create do |obj|
14
23
  obj.after_create_called = true
15
24
  end
data/test/models/car.rb CHANGED
@@ -1,5 +1,3 @@
1
- require 'csv_record'
2
-
3
1
  class Car
4
2
  include CsvRecord::Document
5
3
 
data/test/models/jedi.rb CHANGED
@@ -1,11 +1,20 @@
1
- require 'csv_record'
2
-
3
1
  class Jedi
4
2
  include CsvRecord::Document
5
3
 
4
+ attr_accessor :name, :age, :midi_chlorians
5
+ attr_reader :custom_validator_checker, :custom_validator_checker_with_block
6
+
6
7
  belongs_to :jedi_order
7
8
 
8
9
  validates_presence_of :name, :age
10
+ validates_uniqueness_of :name
11
+
12
+ validate :my_custom_validator_method
13
+ validate do |obj|
14
+ obj.instance_eval do
15
+ @custom_validator_checker_with_block = true
16
+ end
17
+ end
9
18
 
10
19
  def initialize(params={})
11
20
  params.each do |key, value|
@@ -13,5 +22,8 @@ class Jedi
13
22
  end
14
23
  end
15
24
 
16
- attr_accessor :name, :age, :midi_chlorians
25
+ private
26
+ def my_custom_validator_method
27
+ @custom_validator_checker = true
28
+ end
17
29
  end
@@ -1,8 +1,8 @@
1
- require 'csv_record'
2
-
3
1
  class JediOrder
4
2
  include CsvRecord::Document
5
3
 
4
+ attr_accessor :rank
5
+
6
6
  has_many :jedis
7
7
 
8
8
  def initialize(params={})
@@ -10,6 +10,4 @@ class JediOrder
10
10
  self.public_send("#{key}=", value)
11
11
  end
12
12
  end
13
-
14
- attr_accessor :rank
15
13
  end
metadata CHANGED
@@ -1,7 +1,7 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: csv_record
3
3
  version: !ruby/object:Gem::Version
4
- version: 1.6.0
4
+ version: 1.7.0
5
5
  prerelease:
6
6
  platform: ruby
7
7
  authors:
@@ -9,7 +9,7 @@ authors:
9
9
  autorequire:
10
10
  bindir: bin
11
11
  cert_chain: []
12
- date: 2012-11-23 00:00:00.000000000 Z
12
+ date: 2012-11-28 00:00:00.000000000 Z
13
13
  dependencies:
14
14
  - !ruby/object:Gem::Dependency
15
15
  name: activesupport