csv_record 2.0.0 → 2.1.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/.gitignore +1 -0
- data/.travis.yml +2 -0
- data/README.md +3 -3
- data/Rakefile +1 -1
- data/csv_record.gemspec +6 -7
- data/lib/csv_record/callbacks.rb +3 -3
- data/lib/csv_record/connector.rb +7 -7
- data/lib/csv_record/csv_fields.rb +45 -0
- data/lib/csv_record/csv_queries/query.rb +1 -1
- data/lib/csv_record/{reader.rb → csv_readers/class_reader.rb} +11 -29
- data/lib/csv_record/csv_readers/instance_reader.rb +29 -0
- data/lib/csv_record/csv_readers/reader.rb +9 -0
- data/lib/csv_record/csv_validations/custom_validation.rb +1 -1
- data/lib/csv_record/csv_validations/validations.rb +2 -2
- data/lib/csv_record/csv_writers/class_writer.rb +52 -0
- data/lib/csv_record/{writer.rb → csv_writers/instance_writer.rb} +4 -21
- data/lib/csv_record/csv_writers/writer.rb +9 -0
- data/lib/csv_record/document.rb +11 -12
- data/lib/csv_record/exceptions.rb +1 -0
- data/lib/csv_record/field.rb +21 -0
- data/lib/csv_record/helpers.rb +1 -1
- data/lib/csv_record/timestamps.rb +11 -2
- data/lib/csv_record/version.rb +4 -1
- data/test/csv_record/associations_test.rb +0 -3
- data/test/csv_record/callbacks_test.rb +0 -1
- data/test/csv_record/connector_test.rb +0 -2
- data/test/csv_record/helpers_test.rb +0 -1
- data/test/csv_record/query_test.rb +0 -1
- data/test/csv_record/reader_test.rb +122 -116
- data/test/csv_record/timestamps_test.rb +2 -5
- data/test/csv_record/validation_test.rb +13 -14
- data/test/csv_record/writer_test.rb +60 -18
- data/test/helpers.rb +7 -0
- data/test/models/customized_class.rb +11 -0
- data/test/monkey_patches/object_test.rb +2 -4
- data/test/test_helper.rb +9 -6
- metadata +38 -43
data/.gitignore
CHANGED
data/.travis.yml
CHANGED
data/README.md
CHANGED
@@ -1,6 +1,6 @@
|
|
1
1
|
# CsvRecord
|
2
2
|
|
3
|
-
[
|
3
|
+
[](https://travis-ci.org/lukelex/csv_record) [](https://codeclimate.com/github/lukelex/csv_record) [](https://gemnasium.com/lukasalexandre/csv_record) [](http://badge.fury.io/rb/csv_record)
|
4
4
|
|
5
5
|
|
6
6
|
CSV Record connects Ruby classes to CSV documents database to establish an almost zero-configuration persistence layer for applications.
|
@@ -11,7 +11,7 @@ CSV Record connects Ruby classes to CSV documents database to establish an almos
|
|
11
11
|
|
12
12
|
The CSV_Record Wiki has lots of additional information about CSV_Record. Please browse the Wiki after finishing this README:
|
13
13
|
|
14
|
-
|
14
|
+
https://github.com/lukelex/csv_record/wiki
|
15
15
|
|
16
16
|
##Bug reports
|
17
17
|
|
@@ -21,7 +21,7 @@ If you discover a problem with CSV_Record, we would like to know about it. Howev
|
|
21
21
|
|
22
22
|
We hope that you will consider contributing to CSV_Record. Please read this short overview for some information about how to get started:
|
23
23
|
|
24
|
-
https://github.com/
|
24
|
+
https://github.com/lukelex/csv_record/wiki/Contributing
|
25
25
|
|
26
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.
|
27
27
|
|
data/Rakefile
CHANGED
data/csv_record.gemspec
CHANGED
@@ -6,7 +6,7 @@ Gem::Specification.new do |gem|
|
|
6
6
|
gem.email = ["lukasalexandre@gmail.com"]
|
7
7
|
gem.description = %q{CSV Object-relational mapping for Ruby}
|
8
8
|
gem.summary = %q{CSV Record connects Ruby classes to CSV documents database to establish an almost zero-configuration persistence layer for applications.}
|
9
|
-
gem.homepage = "https://github.com/
|
9
|
+
gem.homepage = "https://github.com/lukelex/csv_record"
|
10
10
|
|
11
11
|
gem.license = 'MIT'
|
12
12
|
|
@@ -17,11 +17,10 @@ Gem::Specification.new do |gem|
|
|
17
17
|
gem.require_paths = ["lib"]
|
18
18
|
gem.version = CsvRecord::VERSION
|
19
19
|
|
20
|
-
gem.add_dependency 'activesupport'
|
20
|
+
gem.add_dependency 'activesupport', '~> 3.2.9'
|
21
21
|
|
22
|
-
gem.add_development_dependency 'rake'
|
23
|
-
gem.add_development_dependency '
|
24
|
-
gem.add_development_dependency '
|
25
|
-
gem.add_development_dependency '
|
26
|
-
gem.add_development_dependency 'minitest'
|
22
|
+
gem.add_development_dependency 'rake', '~> 0.9.2.2'
|
23
|
+
gem.add_development_dependency 'timecop', '~> 0.5.3'
|
24
|
+
gem.add_development_dependency 'turn', '~> 0.9.6'
|
25
|
+
gem.add_development_dependency 'minitest', '~> 4.3.1'
|
27
26
|
end
|
data/lib/csv_record/callbacks.rb
CHANGED
@@ -39,9 +39,9 @@ module CsvRecord::Callbacks
|
|
39
39
|
define_method "run_#{callback}_callbacks" do
|
40
40
|
const_variable = "#{callback}_callbacks".upcase
|
41
41
|
if self.class.const_defined? const_variable
|
42
|
-
callbacks_collection = self.class.const_get
|
42
|
+
callbacks_collection = self.class.const_get const_variable
|
43
43
|
callbacks_collection.each do |callback|
|
44
|
-
callback.run_on
|
44
|
+
callback.run_on self
|
45
45
|
end
|
46
46
|
end
|
47
47
|
end
|
@@ -49,7 +49,7 @@ module CsvRecord::Callbacks
|
|
49
49
|
|
50
50
|
[:build, :initialize].each do |initialize_method|
|
51
51
|
define_method initialize_method do |*args|
|
52
|
-
result = super
|
52
|
+
result = super *args
|
53
53
|
self.run_after_initialize_callbacks
|
54
54
|
result
|
55
55
|
end
|
data/lib/csv_record/connector.rb
CHANGED
@@ -16,14 +16,14 @@ module CsvRecord::Connector
|
|
16
16
|
__initialize_db_directory__
|
17
17
|
unless db_initialized?
|
18
18
|
open_database_file WRITE_MODE do |csv|
|
19
|
-
csv <<
|
19
|
+
csv << doppelganger_fields
|
20
20
|
end
|
21
21
|
end
|
22
22
|
end
|
23
23
|
|
24
24
|
# Checks wheter the database file exists
|
25
25
|
def db_initialized?
|
26
|
-
File.exist? self.const_get
|
26
|
+
File.exist? self.const_get 'DATABASE_LOCATION'
|
27
27
|
end
|
28
28
|
|
29
29
|
# Open the database file
|
@@ -32,7 +32,7 @@ module CsvRecord::Connector
|
|
32
32
|
def __open_database_file__(mode=READ_MODE)
|
33
33
|
__initialize_db__ if mode == READ_MODE # fix this later
|
34
34
|
CSV.open(self.const_get('DATABASE_LOCATION'), mode, headers: true) do |csv|
|
35
|
-
yield
|
35
|
+
yield csv
|
36
36
|
end
|
37
37
|
end
|
38
38
|
|
@@ -42,7 +42,7 @@ module CsvRecord::Connector
|
|
42
42
|
CSV.open(self.const_get('DATABASE_LOCATION_TMP'), WRITE_MODE, headers: true) do |copy|
|
43
43
|
copy << fields
|
44
44
|
csv.entries.each do |entry|
|
45
|
-
new_row = yield
|
45
|
+
new_row = yield entry
|
46
46
|
copy << new_row if new_row
|
47
47
|
end
|
48
48
|
end
|
@@ -54,10 +54,10 @@ module CsvRecord::Connector
|
|
54
54
|
|
55
55
|
# Rename the TMP database file to replace the original
|
56
56
|
def rename_database
|
57
|
-
old_file = self.const_get
|
58
|
-
tmp_file = self.const_get
|
57
|
+
old_file = self.const_get 'DATABASE_LOCATION'
|
58
|
+
tmp_file = self.const_get 'DATABASE_LOCATION_TMP'
|
59
59
|
File.delete old_file
|
60
|
-
File.rename
|
60
|
+
File.rename tmp_file, old_file
|
61
61
|
end
|
62
62
|
|
63
63
|
alias :initialize_db_directory :__initialize_db_directory__
|
@@ -0,0 +1,45 @@
|
|
1
|
+
class CsvRecord::CsvFields
|
2
|
+
include Enumerable
|
3
|
+
|
4
|
+
def fields
|
5
|
+
@fields ||= []
|
6
|
+
end
|
7
|
+
|
8
|
+
def <<(field)
|
9
|
+
self.fields << field
|
10
|
+
end
|
11
|
+
|
12
|
+
def include?(field)
|
13
|
+
self.has_doppelganger? field
|
14
|
+
end
|
15
|
+
|
16
|
+
[:name, :doppelganger].each do |attribute|
|
17
|
+
define_method "has_#{attribute}?" do |field|
|
18
|
+
self.fields.any? do |field_model|
|
19
|
+
field_model.public_send(attribute) == field
|
20
|
+
end
|
21
|
+
end
|
22
|
+
end
|
23
|
+
|
24
|
+
def find_with_doppelganger(doppelganger)
|
25
|
+
self.fields.select do |field|
|
26
|
+
field.is? doppelganger
|
27
|
+
end.first
|
28
|
+
end
|
29
|
+
|
30
|
+
def each(&block)
|
31
|
+
self.fields.each &block
|
32
|
+
end
|
33
|
+
|
34
|
+
def method_missing(meth, *args, &block)
|
35
|
+
if self.to_a.respond_to? meth
|
36
|
+
self.to_a.public_send meth, *args, &block
|
37
|
+
else
|
38
|
+
super # You *must* call super if you don't handle the
|
39
|
+
# method, otherwise you'll mess up Ruby's method
|
40
|
+
# lookup.
|
41
|
+
end
|
42
|
+
end
|
43
|
+
|
44
|
+
alias :add :<<
|
45
|
+
end
|
@@ -1,4 +1,4 @@
|
|
1
|
-
|
1
|
+
require 'csv_record/csv_queries/query'
|
2
2
|
|
3
3
|
module CsvRecord::Reader
|
4
4
|
module ClassMethods
|
@@ -7,13 +7,20 @@ module CsvRecord::Reader
|
|
7
7
|
def build(params={})
|
8
8
|
inst = new
|
9
9
|
params.each do |key, value|
|
10
|
-
|
10
|
+
attribute = fields.find_with_doppelganger(key)
|
11
|
+
attr_name = attribute ? attribute.name : key
|
12
|
+
inst.public_send "#{attr_name}=", value
|
11
13
|
end if params
|
14
|
+
yield inst if block_given?
|
12
15
|
inst
|
13
16
|
end
|
14
17
|
|
15
18
|
def __fields__
|
16
|
-
@
|
19
|
+
@fields ||= CsvRecord::CsvFields.new
|
20
|
+
end
|
21
|
+
|
22
|
+
def __doppelganger_fields__
|
23
|
+
self.__fields__.map &:doppelganger
|
17
24
|
end
|
18
25
|
|
19
26
|
def all
|
@@ -70,31 +77,6 @@ module CsvRecord::Reader
|
|
70
77
|
alias :find :__find__
|
71
78
|
alias :count :__count__
|
72
79
|
alias :where :__where__
|
73
|
-
|
74
|
-
|
75
|
-
module InstanceMethods
|
76
|
-
def __values__
|
77
|
-
self.class.fields.map { |attribute| self.public_send(attribute) }
|
78
|
-
end
|
79
|
-
|
80
|
-
def __attributes__
|
81
|
-
Hash[self.class.fields.zip self.values]
|
82
|
-
end
|
83
|
-
|
84
|
-
def __to_param__
|
85
|
-
self.id.to_s
|
86
|
-
end
|
87
|
-
|
88
|
-
def ==(obj)
|
89
|
-
self.class == obj.class and self.to_param == obj.to_param
|
90
|
-
end
|
91
|
-
|
92
|
-
def !=(obj)
|
93
|
-
self.class != obj.class || self.to_param != obj.to_param
|
94
|
-
end
|
95
|
-
|
96
|
-
alias :attributes :__attributes__
|
97
|
-
alias :values :__values__
|
98
|
-
alias :to_param :__to_param__
|
80
|
+
alias :doppelganger_fields :__doppelganger_fields__
|
99
81
|
end
|
100
82
|
end
|
@@ -0,0 +1,29 @@
|
|
1
|
+
module CsvRecord::Reader
|
2
|
+
module InstanceMethods
|
3
|
+
def __values__
|
4
|
+
self.class.fields.map do |attribute|
|
5
|
+
self.public_send attribute.name
|
6
|
+
end
|
7
|
+
end
|
8
|
+
|
9
|
+
def __attributes__
|
10
|
+
Hash[self.class.fields.zip self.values]
|
11
|
+
end
|
12
|
+
|
13
|
+
def __to_param__
|
14
|
+
self.id.to_s
|
15
|
+
end
|
16
|
+
|
17
|
+
def ==(obj)
|
18
|
+
self.class == obj.class and self.to_param == obj.to_param
|
19
|
+
end
|
20
|
+
|
21
|
+
def !=(obj)
|
22
|
+
self.class != obj.class || self.to_param != obj.to_param
|
23
|
+
end
|
24
|
+
|
25
|
+
alias :attributes :__attributes__
|
26
|
+
alias :values :__values__
|
27
|
+
alias :to_param :__to_param__
|
28
|
+
end
|
29
|
+
end
|
@@ -20,7 +20,7 @@ module CsvRecord::Validations
|
|
20
20
|
define_method "__#{class_macro}__" do |*field_names|
|
21
21
|
field_names.each do |field|
|
22
22
|
validation_obj = validation_class_name.new field
|
23
|
-
self.public_send
|
23
|
+
self.public_send "add_to_#{validator_collection}", validation_obj
|
24
24
|
end
|
25
25
|
end
|
26
26
|
|
@@ -55,7 +55,7 @@ module CsvRecord::Validations
|
|
55
55
|
def errors
|
56
56
|
unless @errors
|
57
57
|
@errors = []
|
58
|
-
def @errors.add
|
58
|
+
def @errors.add attribute
|
59
59
|
self << attribute
|
60
60
|
end
|
61
61
|
end
|
@@ -0,0 +1,52 @@
|
|
1
|
+
module CsvRecord::Writer
|
2
|
+
module ClassMethods
|
3
|
+
def __create__(attributes={})
|
4
|
+
instance = self.build attributes
|
5
|
+
yield instance if block_given?
|
6
|
+
result = instance.save
|
7
|
+
instance
|
8
|
+
end
|
9
|
+
|
10
|
+
[:attr_accessor, :attr_writer].each do |custom_accessor|
|
11
|
+
define_method custom_accessor do |*attributes|
|
12
|
+
attributes.each do |attribute|
|
13
|
+
unless self.fields.has_name? attribute
|
14
|
+
self.fields << CsvRecord::Field.new(attribute)
|
15
|
+
end
|
16
|
+
end
|
17
|
+
super *attributes
|
18
|
+
end
|
19
|
+
end
|
20
|
+
|
21
|
+
def store_as(name)
|
22
|
+
@table_name = name.to_s.underscore.pluralize
|
23
|
+
redefine_database_location
|
24
|
+
|
25
|
+
@table_name
|
26
|
+
end
|
27
|
+
|
28
|
+
def table_name
|
29
|
+
@table_name ||= store_as name
|
30
|
+
end
|
31
|
+
|
32
|
+
def mapping(config=[])
|
33
|
+
config.each do |field, doppelganger|
|
34
|
+
unless self.fields.include? field
|
35
|
+
self.fields << (CsvRecord::Field.new field, doppelganger)
|
36
|
+
end
|
37
|
+
end
|
38
|
+
end
|
39
|
+
|
40
|
+
def redefine_database_location
|
41
|
+
if const_defined?('DATABASE_LOCATION') || const_defined?('DATABASE_LOCATION_TMP')
|
42
|
+
send :remove_const, 'DATABASE_LOCATION'
|
43
|
+
send :remove_const, 'DATABASE_LOCATION_TMP'
|
44
|
+
end
|
45
|
+
|
46
|
+
const_set 'DATABASE_LOCATION', "db/#{@table_name}.csv"
|
47
|
+
const_set 'DATABASE_LOCATION_TMP', "db/#{@table_name}_tmp.csv"
|
48
|
+
end
|
49
|
+
|
50
|
+
alias :create :__create__
|
51
|
+
end
|
52
|
+
end
|
@@ -1,22 +1,4 @@
|
|
1
1
|
module CsvRecord::Writer
|
2
|
-
module ClassMethods
|
3
|
-
def __create__(attributes={})
|
4
|
-
instance = self.build attributes
|
5
|
-
result = instance.save
|
6
|
-
instance
|
7
|
-
end
|
8
|
-
|
9
|
-
[:attr_accessor, :attr_writer].each do |custom_accessor|
|
10
|
-
define_method custom_accessor do |*args|
|
11
|
-
@relevant_instance_variables ||= []
|
12
|
-
args.each { |arg| @relevant_instance_variables << arg }
|
13
|
-
super *args
|
14
|
-
end
|
15
|
-
end
|
16
|
-
|
17
|
-
alias :create :__create__
|
18
|
-
end
|
19
|
-
|
20
2
|
module InstanceMethods
|
21
3
|
def self.included(receiver)
|
22
4
|
receiver.send :attr_accessor, :id
|
@@ -41,11 +23,14 @@ module CsvRecord::Writer
|
|
41
23
|
|
42
24
|
def __update_attributes__(params={validate: true})
|
43
25
|
validate = params[:validate]
|
44
|
-
params.delete
|
26
|
+
params.delete :validate
|
45
27
|
|
46
28
|
params.each do |field, value|
|
47
29
|
self.public_send "#{field}=", value
|
48
30
|
end
|
31
|
+
|
32
|
+
yield self if block_given?
|
33
|
+
|
49
34
|
self.save validate
|
50
35
|
end
|
51
36
|
|
@@ -66,12 +51,10 @@ module CsvRecord::Writer
|
|
66
51
|
end
|
67
52
|
|
68
53
|
def append_registry
|
69
|
-
set_created_at
|
70
54
|
write_object
|
71
55
|
end
|
72
56
|
|
73
57
|
def update_registry
|
74
|
-
set_updated_at
|
75
58
|
self.class.parse_database_file do |row|
|
76
59
|
new_row = row
|
77
60
|
new_row = self.values if self.id.to_i == row.field('id').to_i
|
data/lib/csv_record/document.rb
CHANGED
@@ -1,32 +1,31 @@
|
|
1
1
|
require 'active_support/core_ext/string/inflections.rb'
|
2
2
|
|
3
3
|
require 'csv_record/connector'
|
4
|
-
require 'csv_record/writer'
|
5
|
-
require 'csv_record/reader'
|
4
|
+
require 'csv_record/csv_writers/writer'
|
5
|
+
require 'csv_record/csv_readers/reader'
|
6
6
|
require 'csv_record/timestamps'
|
7
7
|
require 'csv_record/callbacks'
|
8
8
|
require 'csv_record/helpers'
|
9
9
|
require 'csv_record/associations'
|
10
10
|
require 'csv_record/csv_validations/validations'
|
11
11
|
|
12
|
+
require 'csv_record/csv_fields'
|
13
|
+
require 'csv_record/field'
|
14
|
+
require 'csv_record/exceptions'
|
15
|
+
|
12
16
|
# This is the base module for all domain objects that need to be persisted to
|
13
17
|
# the database.
|
14
18
|
module CsvRecord::Document
|
15
19
|
def self.included(receiver)
|
16
|
-
klass = receiver.name
|
17
|
-
|
18
|
-
receiver.const_set 'DATABASE_LOCATION',"db/#{klass.underscore.pluralize}.csv"
|
19
|
-
receiver.const_set 'DATABASE_LOCATION_TMP',"db/#{klass.underscore.pluralize}_tmp.csv"
|
20
|
-
|
21
20
|
receiver.extend CsvRecord::Connector
|
22
|
-
receiver.extend CsvRecord::Writer::ClassMethods
|
23
|
-
receiver.extend CsvRecord::Reader::ClassMethods
|
24
21
|
receiver.extend CsvRecord::Associations
|
25
22
|
receiver.extend CsvRecord::Validations::ClassMethods
|
26
|
-
receiver.send :include, CsvRecord::
|
27
|
-
receiver.send :include, CsvRecord::
|
28
|
-
receiver.send :include, CsvRecord::Timestamps
|
23
|
+
receiver.send :include, CsvRecord::Reader
|
24
|
+
receiver.send :include, CsvRecord::Writer
|
29
25
|
receiver.send :include, CsvRecord::Validations::InstanceMethods
|
30
26
|
receiver.send :include, CsvRecord::Callbacks
|
27
|
+
receiver.send :include, CsvRecord::Timestamps
|
28
|
+
|
29
|
+
receiver.store_as receiver.name
|
31
30
|
end
|
32
31
|
end
|
@@ -0,0 +1 @@
|
|
1
|
+
class UndefienedCsvField < Exception ; end
|
@@ -0,0 +1,21 @@
|
|
1
|
+
class CsvRecord::Field
|
2
|
+
attr_reader :name, :doppelganger
|
3
|
+
|
4
|
+
def initialize(name, doppelganger=nil)
|
5
|
+
@name = name
|
6
|
+
@doppelganger = doppelganger
|
7
|
+
end
|
8
|
+
|
9
|
+
def doppelganger
|
10
|
+
@doppelganger || @name
|
11
|
+
end
|
12
|
+
|
13
|
+
def to_s
|
14
|
+
self.name.to_s
|
15
|
+
end
|
16
|
+
|
17
|
+
def is?(value)
|
18
|
+
self.doppelganger.to_sym == value.to_sym ||
|
19
|
+
self.name.to_sym == value.to_sym
|
20
|
+
end
|
21
|
+
end
|
data/lib/csv_record/helpers.rb
CHANGED
@@ -2,8 +2,17 @@
|
|
2
2
|
# timestamp.
|
3
3
|
module CsvRecord::Timestamps
|
4
4
|
def self.included(receiver)
|
5
|
-
receiver.
|
6
|
-
|
5
|
+
receiver.class_eval do
|
6
|
+
attr_accessor :created_at, :updated_at
|
7
|
+
|
8
|
+
before_create do
|
9
|
+
set_created_at
|
10
|
+
end
|
11
|
+
|
12
|
+
after_update do
|
13
|
+
set_updated_at
|
14
|
+
end
|
15
|
+
end
|
7
16
|
end
|
8
17
|
|
9
18
|
private
|
data/lib/csv_record/version.rb
CHANGED