csv_record 2.1.2 → 3.0.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.
Files changed (46) hide show
  1. checksums.yaml +7 -0
  2. data/.ruby-gemset +1 -0
  3. data/.ruby-version +1 -0
  4. data/.travis.yml +4 -1
  5. data/README.md +51 -39
  6. data/Rakefile +1 -1
  7. data/csv_record.gemspec +8 -6
  8. data/lib/csv_record.rb +0 -23
  9. data/lib/csv_record/associations.rb +1 -1
  10. data/lib/csv_record/callback.rb +4 -2
  11. data/lib/csv_record/callbacks.rb +21 -19
  12. data/lib/csv_record/{csv_queries/condition.rb → condition.rb} +4 -2
  13. data/lib/csv_record/connector.rb +9 -7
  14. data/lib/csv_record/csv_validations/custom_validation.rb +8 -5
  15. data/lib/csv_record/csv_validations/presence_validation.rb +6 -4
  16. data/lib/csv_record/csv_validations/uniqueness_validation.rb +8 -5
  17. data/lib/csv_record/csv_validations/validations.rb +23 -28
  18. data/lib/csv_record/document.rb +12 -12
  19. data/lib/csv_record/field.rb +6 -4
  20. data/lib/csv_record/fields.rb +45 -0
  21. data/lib/csv_record/helpers.rb +10 -6
  22. data/lib/csv_record/{csv_queries/query.rb → query.rb} +8 -15
  23. data/lib/csv_record/reader.rb +120 -0
  24. data/lib/csv_record/timestamps.rb +4 -2
  25. data/lib/csv_record/version.rb +3 -3
  26. data/lib/csv_record/writer.rb +143 -0
  27. data/test/csv_record/associations_test.rb +1 -1
  28. data/test/csv_record/connector_test.rb +3 -3
  29. data/test/csv_record/reader_test.rb +6 -6
  30. data/test/csv_record/validation_test.rb +1 -1
  31. data/test/models/callback_test_class.rb +1 -2
  32. data/test/models/custom_errors_class.rb +2 -1
  33. data/test/models/customized_class.rb +1 -1
  34. data/test/models/jedi.rb +4 -3
  35. data/test/models/jedi_order.rb +2 -2
  36. data/test/models/padawan.rb +1 -1
  37. data/test/test_helper.rb +3 -3
  38. data/test/{helpers.rb → test_helpers.rb} +2 -2
  39. metadata +80 -52
  40. data/lib/csv_record/csv_fields.rb +0 -45
  41. data/lib/csv_record/csv_readers/class_reader.rb +0 -82
  42. data/lib/csv_record/csv_readers/instance_reader.rb +0 -29
  43. data/lib/csv_record/csv_readers/reader.rb +0 -9
  44. data/lib/csv_record/csv_writers/class_writer.rb +0 -52
  45. data/lib/csv_record/csv_writers/instance_writer.rb +0 -86
  46. data/lib/csv_record/csv_writers/writer.rb +0 -9
@@ -1,3 +1,5 @@
1
+ # frozen_string_literal: true
2
+
1
3
  class CsvRecord::CustomValidation
2
4
  attr_accessor :message
3
5
 
@@ -6,15 +8,16 @@ class CsvRecord::CustomValidation
6
8
  end
7
9
 
8
10
  def run_on(obj)
9
- if self.message.is_a? Proc
10
- obj.instance_eval &self.message
11
+ if message.is_a?(Proc)
12
+ obj.instance_eval(&self.message)
11
13
  else
12
- obj.send self.message
14
+ obj.send message
13
15
  end
14
16
  end
15
17
 
16
- private
18
+ private
19
+
17
20
  def get_correct_block_type
18
21
  self.class.send "#{self.type}_block"
19
22
  end
20
- end
23
+ end
@@ -1,3 +1,5 @@
1
+ # frozen_string_literal: true
2
+
1
3
  class CsvRecord::PresenceValidation
2
4
  attr_accessor :field
3
5
 
@@ -6,8 +8,8 @@ class CsvRecord::PresenceValidation
6
8
  end
7
9
 
8
10
  def run_on(obj)
9
- if obj.public_send(self.field).nil?
10
- obj.errors.add self.field
11
- end
11
+ return unless obj.public_send(field).nil?
12
+
13
+ obj.errors.add field
12
14
  end
13
- end
15
+ end
@@ -1,3 +1,5 @@
1
+ # frozen_string_literal: true
2
+
1
3
  class CsvRecord::UniquenessValidation
2
4
  attr_accessor :field
3
5
 
@@ -7,10 +9,11 @@ class CsvRecord::UniquenessValidation
7
9
 
8
10
  def run_on(obj)
9
11
  condition = {}
10
- condition[self.field] = obj.public_send field
12
+ condition[field] = obj.public_send field
11
13
  records = obj.class.__where__ condition
12
- if records.any? { |record| record != obj }
13
- obj.errors.add field
14
- end
14
+
15
+ return unless records.any? { |record| record != obj }
16
+
17
+ obj.errors.add field
15
18
  end
16
- end
19
+ end
@@ -1,6 +1,8 @@
1
- require 'csv_record/csv_validations/custom_validation'
2
- require 'csv_record/csv_validations/uniqueness_validation'
3
- require 'csv_record/csv_validations/presence_validation'
1
+ # frozen_string_literal: true
2
+
3
+ require_relative 'custom_validation'
4
+ require_relative 'uniqueness_validation'
5
+ require_relative 'presence_validation'
4
6
 
5
7
  module CsvRecord::Validations
6
8
  module ClassMethods
@@ -20,7 +22,7 @@ module CsvRecord::Validations
20
22
  define_method "__#{class_macro}__" do |*field_names|
21
23
  field_names.each do |field|
22
24
  validation_obj = validation_class_name.new field
23
- self.public_send "add_to_#{validator_collection}", validation_obj
25
+ public_send "add_to_#{validator_collection}", validation_obj
24
26
  end
25
27
  end
26
28
 
@@ -32,11 +34,10 @@ module CsvRecord::Validations
32
34
  end
33
35
 
34
36
  def validate(*methods, &block)
35
- @custom_validators ||= []
36
37
  methods.each do |method|
37
- @custom_validators << (CsvRecord::CustomValidation.new method)
38
+ custom_validators << CsvRecord::CustomValidation.new(method)
38
39
  end
39
- @custom_validators << (CsvRecord::CustomValidation.new block) if block_given?
40
+ custom_validators << CsvRecord::CustomValidation.new(block) if block_given?
40
41
  end
41
42
  end
42
43
 
@@ -49,39 +50,33 @@ module CsvRecord::Validations
49
50
  end
50
51
 
51
52
  def invalid?
52
- not self.__valid__?
53
+ not __valid__?
53
54
  end
54
55
 
55
56
  def errors
56
- unless @errors
57
- @errors = []
58
- def @errors.add attribute
59
- self << attribute
60
- end
61
- end
62
-
63
- @errors
57
+ @errors ||= Errors.new
64
58
  end
65
59
 
66
60
  alias :valid? :__valid__?
67
61
 
68
62
  private
69
- def trigger_presence_validations
70
- self.class.fields_to_validate_presence.each do |validator|
71
- validator.run_on self
72
- end
73
- end
74
63
 
75
- def trigger_uniqueness_validations
76
- self.class.fields_to_validate_uniqueness.each do |validator|
77
- validator.run_on self
64
+ [:presence, :uniqueness].each do |type|
65
+ define_method "trigger_#{type}_validations" do
66
+ fields_to_validate = self.class.public_send "fields_to_validate_#{type}"
67
+ fields_to_validate.each do |validator|
68
+ validator.run_on self
69
+ end
78
70
  end
79
71
  end
80
72
 
81
73
  def trigger_custom_validations
82
- self.class.custom_validators.each do |validator|
83
- validator.run_on self
84
- end
74
+ self.class.custom_validators
75
+ .each { |validator| validator.run_on self }
85
76
  end
86
77
  end
87
- end
78
+
79
+ class Errors < Array
80
+ alias :add :<<
81
+ end
82
+ end
@@ -1,17 +1,17 @@
1
1
  require 'active_support/core_ext/string/inflections.rb'
2
2
 
3
- require 'csv_record/connector'
4
- require 'csv_record/csv_writers/writer'
5
- require 'csv_record/csv_readers/reader'
6
- require 'csv_record/timestamps'
7
- require 'csv_record/callbacks'
8
- require 'csv_record/helpers'
9
- require 'csv_record/associations'
10
- require 'csv_record/csv_validations/validations'
3
+ require_relative 'connector'
4
+ require_relative 'writer'
5
+ require_relative 'reader'
6
+ require_relative 'timestamps'
7
+ require_relative 'callbacks'
8
+ require_relative 'helpers'
9
+ require_relative 'associations'
10
+ require_relative 'csv_validations/validations'
11
11
 
12
- require 'csv_record/csv_fields'
13
- require 'csv_record/field'
14
- require 'csv_record/exceptions'
12
+ require_relative 'fields'
13
+ require_relative 'field'
14
+ require_relative 'exceptions'
15
15
 
16
16
  # This is the base module for all domain objects that need to be persisted to
17
17
  # the database.
@@ -28,4 +28,4 @@ module CsvRecord::Document
28
28
 
29
29
  receiver.store_as receiver.name
30
30
  end
31
- end
31
+ end
@@ -1,3 +1,5 @@
1
+ # frozen_string_literal: true
2
+
1
3
  class CsvRecord::Field
2
4
  attr_reader :name, :doppelganger
3
5
 
@@ -11,11 +13,11 @@ class CsvRecord::Field
11
13
  end
12
14
 
13
15
  def to_s
14
- self.name.to_s
16
+ name.to_s
15
17
  end
16
18
 
17
19
  def is?(value)
18
- self.doppelganger.to_sym == value.to_sym ||
19
- self.name.to_sym == value.to_sym
20
+ doppelganger.to_sym == value.to_sym ||
21
+ name.to_sym == value.to_sym
20
22
  end
21
- end
23
+ end
@@ -0,0 +1,45 @@
1
+ # frozen_string_literal: true
2
+
3
+ class CsvRecord::Fields
4
+ include Enumerable
5
+
6
+ def fields
7
+ @fields ||= []
8
+ end
9
+
10
+ def <<(field)
11
+ fields << field
12
+ end
13
+
14
+ def include?(field)
15
+ has_doppelganger? field
16
+ end
17
+
18
+ [:name, :doppelganger].each do |attribute|
19
+ define_method "has_#{attribute}?" do |field|
20
+ fields.any? do |field_model|
21
+ field_model.public_send(attribute) == field
22
+ end
23
+ end
24
+ end
25
+
26
+ def find_with_doppelganger(doppelganger)
27
+ fields
28
+ .select { |field| field.is? doppelganger }
29
+ .first
30
+ end
31
+
32
+ def each(&block)
33
+ fields.each(&block)
34
+ end
35
+
36
+ def method_missing(meth, *args, &block)
37
+ if to_a.respond_to?(meth)
38
+ to_a.public_send(meth, *args, &block)
39
+ else
40
+ super
41
+ end
42
+ end
43
+
44
+ alias :add :<<
45
+ end
@@ -1,11 +1,13 @@
1
+ # frozen_string_literal: true
2
+
1
3
  class Object
2
4
  def integer?
3
- /(?<number>^\d+$)/ =~ self.to_s
5
+ /(?<number>^\d+$)/ =~ to_s
4
6
  not number.nil?
5
7
  end
6
8
 
7
9
  def float?
8
- /(?<number>^\d+\.\d+)$/ =~ self.to_s
10
+ /(?<number>^\d+\.\d+)$/ =~ to_s
9
11
  not number.nil?
10
12
  end
11
13
 
@@ -20,16 +22,18 @@ end
20
22
 
21
23
  class String
22
24
  def constantize
23
- self.split('_').map { |w| w.capitalize }.join
25
+ split('_').map { |w| w.capitalize }.join
24
26
  end
27
+
25
28
  def to_class
26
- Object.const_get self.constantize.singularize
29
+ Object.const_get constantize.singularize
27
30
  end
31
+
28
32
  def underscore
29
- self.gsub(/::/, '/').
33
+ gsub(/::/, '/').
30
34
  gsub(/([A-Z]+)([A-Z][a-z])/,'\1_\2').
31
35
  gsub(/([a-z\d])([A-Z])/,'\1_\2').
32
36
  tr("-", "_").
33
37
  downcase
34
38
  end
35
- end
39
+ end
@@ -1,3 +1,5 @@
1
+ # frozen_string_literal: true
2
+
1
3
  require_relative 'condition'
2
4
 
3
5
  class CsvRecord::Query
@@ -9,7 +11,7 @@ class CsvRecord::Query
9
11
  @klass = klass
10
12
 
11
13
  @conditions = conditions.map do |condition|
12
- CsvRecord::Condition.new *condition
14
+ CsvRecord::Condition.new(*condition)
13
15
  end
14
16
  end
15
17
 
@@ -21,7 +23,7 @@ class CsvRecord::Query
21
23
 
22
24
  def __trigger__
23
25
  klass.open_database_file do |csv|
24
- rows = search_for csv, self.conditions
26
+ rows = search_for csv, conditions
25
27
  rows.map { |row| klass.build row.to_hash }
26
28
  end
27
29
  end
@@ -39,7 +41,7 @@ class CsvRecord::Query
39
41
  end
40
42
 
41
43
  def each(&block)
42
- to_a.each &block
44
+ to_a.each(&block)
43
45
  end
44
46
 
45
47
  def empty?
@@ -50,7 +52,7 @@ class CsvRecord::Query
50
52
  alias :trigger :__trigger__
51
53
  alias :to_a :__to_a__
52
54
 
53
- protected
55
+ private
54
56
 
55
57
  def search_for(csv, params)
56
58
  csv.entries.select do |attributes|
@@ -59,15 +61,6 @@ protected
59
61
  end
60
62
 
61
63
  def conditions_as_string
62
- conditions_string = ''
63
- self.conditions.each_with_index do |condition, index|
64
- conditions_string << condition.to_code
65
- conditions_string << ' and ' if first_and_not_last? index
66
- end
67
- conditions_string
68
- end
69
-
70
- def first_and_not_last?(index)
71
- (self.conditions.size > 1) and (index != self.conditions.size - 1)
64
+ @_conditions_as_string ||= conditions.map(&:to_code).join ' and '
72
65
  end
73
- end
66
+ end
@@ -0,0 +1,120 @@
1
+ # frozen_string_literal: true
2
+
3
+ require 'csv_record/query'
4
+
5
+ module CsvRecord::Reader
6
+ module ClassMethods
7
+ DYNAMIC_FINDER_PATTERN = /^find_by_(.+)$/
8
+
9
+ def build(params={})
10
+ new.tap do |inst|
11
+ break unless params
12
+
13
+ params.each do |key, value|
14
+ attribute = fields.find_with_doppelganger(key)
15
+ attr_name = attribute ? attribute.name : key
16
+ inst.public_send "#{attr_name}=", value
17
+ end
18
+
19
+ yield inst if block_given?
20
+ end
21
+ end
22
+
23
+ def __fields__
24
+ @fields ||= ::CsvRecord::Fields.new
25
+ end
26
+
27
+ def __doppelganger_fields__
28
+ __fields__.map(&:doppelganger)
29
+ end
30
+
31
+ def all
32
+ open_database_file do |csv|
33
+ csv.entries.map(&method(:build))
34
+ end
35
+ end
36
+
37
+ def first
38
+ all.first
39
+ end
40
+
41
+ def last
42
+ all.last
43
+ end
44
+
45
+ def __count__
46
+ open_database_file do |csv|
47
+ csv.entries.size
48
+ end
49
+ end
50
+
51
+ def __find__(condition)
52
+ (__where__ id: condition.to_param).first
53
+ end
54
+
55
+ def __where__(params)
56
+ ::CsvRecord::Query.new self, params
57
+ end
58
+
59
+ def method_missing(meth, *args, &block)
60
+ if meth.to_s =~ DYNAMIC_FINDER_PATTERN
61
+ dynamic_finder $1, *args, &block
62
+ else
63
+ super
64
+ end
65
+ end
66
+
67
+ def respond_to?(meth)
68
+ (meth.to_s =~ DYNAMIC_FINDER_PATTERN) || super
69
+ end
70
+
71
+ private
72
+
73
+ def dynamic_finder(meth, *args, &block)
74
+ properties = meth.split '_and_'
75
+ conditions = Hash[properties.zip args]
76
+ __where__(conditions).first
77
+ end
78
+
79
+ alias :fields :__fields__
80
+ alias :find :__find__
81
+ alias :count :__count__
82
+ alias :where :__where__
83
+ alias :doppelganger_fields :__doppelganger_fields__
84
+ end
85
+
86
+ module InstanceMethods
87
+ def __values__
88
+ self.class.fields.map do |attribute|
89
+ public_send attribute.name
90
+ end
91
+ end
92
+
93
+ def __attributes__
94
+ Hash[self.class.fields.zip(values)]
95
+ end
96
+
97
+ def __to_param__
98
+ id.to_s
99
+ end
100
+
101
+ def ==(obj)
102
+ self.class == obj.class and
103
+ to_param == obj.to_param
104
+ end
105
+
106
+ def !=(obj)
107
+ self.class != obj.class ||
108
+ to_param != obj.to_param
109
+ end
110
+
111
+ alias :attributes :__attributes__
112
+ alias :values :__values__
113
+ alias :to_param :__to_param__
114
+ end
115
+
116
+ def self.included(receiver)
117
+ receiver.extend ClassMethods
118
+ receiver.send :include, InstanceMethods
119
+ end
120
+ end