csv_record 2.1.2 → 3.0.0

Sign up to get free protection for your applications and to get access to all the features.
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