baza_models 0.0.0 → 0.0.1

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 (71) hide show
  1. checksums.yaml +4 -4
  2. data/.rubocop_todo.yml +35 -0
  3. data/Gemfile +9 -3
  4. data/Gemfile.lock +90 -51
  5. data/README.md +98 -3
  6. data/Rakefile +20 -17
  7. data/VERSION +1 -1
  8. data/baza_models.gemspec +149 -0
  9. data/config/best_project_practice_rubocop.yml +2 -0
  10. data/config/best_project_practice_rubocop_todo.yml +35 -0
  11. data/lib/baza_models.rb +15 -0
  12. data/lib/baza_models/autoloader.rb +135 -0
  13. data/lib/baza_models/baza_orm_adapter.rb +39 -0
  14. data/lib/baza_models/can_can_adapter.rb +9 -0
  15. data/lib/baza_models/class_translation.rb +44 -0
  16. data/lib/baza_models/errors.rb +24 -2
  17. data/lib/baza_models/model.rb +236 -115
  18. data/lib/baza_models/model/belongs_to_relations.rb +49 -0
  19. data/lib/baza_models/model/custom_validations.rb +24 -0
  20. data/lib/baza_models/model/delegation.rb +21 -0
  21. data/lib/baza_models/model/has_many_relations.rb +85 -0
  22. data/lib/baza_models/model/has_one_relations.rb +93 -0
  23. data/lib/baza_models/model/manipulation.rb +123 -0
  24. data/lib/baza_models/model/queries.rb +45 -0
  25. data/lib/baza_models/model/scopes.rb +19 -0
  26. data/lib/baza_models/model/translation_functionality.rb +30 -0
  27. data/lib/baza_models/model/validations.rb +95 -0
  28. data/lib/baza_models/query.rb +447 -0
  29. data/lib/baza_models/query/inspector.rb +74 -0
  30. data/lib/baza_models/query/not.rb +34 -0
  31. data/lib/baza_models/ransacker.rb +30 -0
  32. data/lib/baza_models/test_database_cleaner.rb +23 -0
  33. data/lib/baza_models/validators/base_validator.rb +14 -0
  34. data/lib/baza_models/validators/confirmation_validator.rb +12 -0
  35. data/lib/baza_models/validators/format_validator.rb +11 -0
  36. data/lib/baza_models/validators/length_validator.rb +16 -0
  37. data/lib/baza_models/validators/uniqueness_validator.rb +21 -0
  38. data/shippable.yml +3 -1
  39. data/spec/baza_models/autoloader_spec.rb +57 -0
  40. data/spec/baza_models/baza_orm_adapter_spec.rb +52 -0
  41. data/spec/baza_models/class_translation_spec.rb +25 -0
  42. data/spec/baza_models/factory_girl_spec.rb +13 -0
  43. data/spec/baza_models/model/belongs_to_relations_spec.rb +26 -0
  44. data/spec/baza_models/model/custom_validations_spec.rb +18 -0
  45. data/spec/baza_models/model/delgation_spec.rb +16 -0
  46. data/spec/baza_models/model/has_many_relations_spec.rb +68 -0
  47. data/spec/baza_models/model/has_one_relations_spec.rb +35 -0
  48. data/spec/baza_models/model/manipulation_spec.rb +25 -0
  49. data/spec/baza_models/model/queries_spec.rb +59 -0
  50. data/spec/baza_models/model/scopes_spec.rb +23 -0
  51. data/spec/baza_models/model/translate_functionality_spec.rb +13 -0
  52. data/spec/baza_models/model/validations_spec.rb +52 -0
  53. data/spec/baza_models/model_spec.rb +75 -98
  54. data/spec/baza_models/query/not_spec.rb +16 -0
  55. data/spec/baza_models/query_spec.rb +155 -0
  56. data/spec/baza_models/ransacker_spec.rb +15 -0
  57. data/spec/baza_models/validators/confirmation_validator_spec.rb +28 -0
  58. data/spec/baza_models/validators/format_validator_spec.rb +17 -0
  59. data/spec/baza_models/validators/length_validator_spec.rb +19 -0
  60. data/spec/baza_models/validators/uniqueness_validator_spec.rb +24 -0
  61. data/spec/factories/organization.rb +5 -0
  62. data/spec/factories/user.rb +7 -0
  63. data/spec/spec_helper.rb +17 -5
  64. data/spec/support/database_helper.rb +87 -0
  65. data/spec/test_classes/organization.rb +3 -0
  66. data/spec/test_classes/person.rb +3 -0
  67. data/spec/test_classes/role.rb +12 -0
  68. data/spec/test_classes/user.rb +40 -0
  69. data/spec/test_classes/user_passport.rb +3 -0
  70. metadata +146 -7
  71. data/spec/test_classes/user_test.rb +0 -17
@@ -0,0 +1,49 @@
1
+ module BazaModels::Model::BelongsToRelations
2
+ def self.included(base)
3
+ base.extend(ClassMethods)
4
+ end
5
+
6
+ module ClassMethods
7
+ def belongs_to(relation_name, args = {})
8
+ relation = {
9
+ type: :belongs_to,
10
+ relation_name: relation_name,
11
+ table_name: args[:table_name] || StringCases.pluralize(relation_name),
12
+ foreign_key: :"#{relation_name}_id"
13
+ }
14
+
15
+ if args[:class_name]
16
+ relation[:class_name] = args.fetch(:class_name)
17
+ else
18
+ relation[:class_name] = StringCases.snake_to_camel(relation_name)
19
+ end
20
+
21
+ @belongs_to_relations ||= []
22
+ @belongs_to_relations << relation
23
+
24
+ @relationships ||= {}
25
+ @relationships[relation_name] = relation
26
+
27
+ define_method(relation_name) do
28
+ if (model = @changes[relation_name])
29
+ model
30
+ elsif (model = autoloads[relation_name])
31
+ model
32
+ else
33
+ if relation[:class_name]
34
+ class_name = relation.fetch(:class_name)
35
+ else
36
+ class_name = StringCases.snake_to_camel(relation_name)
37
+ end
38
+
39
+ Object.const_get(class_name).find(@data.fetch(relation.fetch(:foreign_key)))
40
+ end
41
+ end
42
+
43
+ define_method("#{relation_name}=") do |new_model|
44
+ @changes[relation.fetch(:foreign_key)] = new_model.id
45
+ autoloads.delete(relation_name)
46
+ end
47
+ end
48
+ end
49
+ end
@@ -0,0 +1,24 @@
1
+ module BazaModels::Model::CustomValidations
2
+ def self.included(base)
3
+ base.extend(ClassMethods)
4
+ end
5
+
6
+ module ClassMethods
7
+ attr_reader :custom_validations
8
+
9
+ def validate(method_name)
10
+ @custom_validations ||= []
11
+ @custom_validations << method_name
12
+ end
13
+ end
14
+
15
+ private
16
+
17
+ def execute_custom_validations
18
+ return unless self.class.custom_validations
19
+
20
+ self.class.custom_validations.each do |method_name|
21
+ __send__(method_name)
22
+ end
23
+ end
24
+ end
@@ -0,0 +1,21 @@
1
+ module BazaModels::Model::Delegation
2
+ def self.included(base)
3
+ base.extend(ClassMethods)
4
+ end
5
+
6
+ module ClassMethods
7
+ def delegate(*methods, args)
8
+ methods.each do |method|
9
+ if args[:prefix]
10
+ method_name = "#{args.fetch(:to)}_#{method}"
11
+ else
12
+ method_name = method
13
+ end
14
+
15
+ define_method(method_name) do |*method_args, &method_blk|
16
+ __send__(args[:to]).__send__(method, *method_args, &method_blk)
17
+ end
18
+ end
19
+ end
20
+ end
21
+ end
@@ -0,0 +1,85 @@
1
+ module BazaModels::Model::HasManyRelations
2
+ def self.included(base)
3
+ base.extend(ClassMethods)
4
+ end
5
+
6
+ module ClassMethods
7
+ # rubocop:disable Style/PredicateName
8
+ def has_many(relation_name, *all_args)
9
+ # rubocop:enable Style/PredicateName
10
+
11
+ args = all_args.pop
12
+
13
+ relation = {
14
+ type: :has_many,
15
+ relation_name: relation_name,
16
+ table_name: args[:table_name] || relation_name,
17
+ args: args,
18
+ all_args: all_args
19
+ }
20
+
21
+ if args[:foreign_key]
22
+ relation[:foreign_key] = args.fetch(:foreign_key)
23
+ else
24
+ relation[:foreign_key] = :"#{StringCases.camel_to_snake(name)}_id"
25
+ end
26
+
27
+ relation[:dependent] = args.fetch(:dependent) if args[:dependent]
28
+
29
+ if args && args[:class_name]
30
+ relation[:class_name] = args.fetch(:class_name)
31
+ else
32
+ relation[:class_name] = StringCases.snake_to_camel(relation_name.to_s.gsub(/s$/, ""))
33
+ end
34
+
35
+ @has_many_relations ||= []
36
+ @has_many_relations << relation
37
+
38
+ @relationships ||= {}
39
+ @relationships[relation_name] = relation
40
+
41
+ define_method(relation_name) do
42
+ class_instance = Object.const_get(relation.fetch(:class_name))
43
+ query = class_instance.where(relation.fetch(:foreign_key) => id)
44
+ query._previous_model = self
45
+ query._relation = relation
46
+
47
+ all_args.each do |arg|
48
+ query = query.instance_exec(&arg) if arg.is_a?(Proc)
49
+ end
50
+
51
+ return query
52
+ end
53
+ end
54
+ end
55
+
56
+ private
57
+
58
+ def restrict_has_many_relations
59
+ self.class.relationships.each do |relation_name, relation|
60
+ next if relation.fetch(:type) != :has_many || relation[:dependent] != :restrict_with_error
61
+
62
+ if __send__(relation_name).any?
63
+ errors.add(:base, "can't be destroyed because it contains #{relation_name}")
64
+ return false
65
+ end
66
+ end
67
+
68
+ true
69
+ end
70
+
71
+ def destroy_has_many_relations
72
+ self.class.relationships.each do |relation_name, relation|
73
+ next if relation.fetch(:type) != :has_many || relation[:dependent] != :destroy
74
+
75
+ __send__(relation_name).each do |model|
76
+ unless model.destroy
77
+ errors.add(:base, model.errors.full_messages.join(". "))
78
+ return false
79
+ end
80
+ end
81
+ end
82
+
83
+ true
84
+ end
85
+ end
@@ -0,0 +1,93 @@
1
+ module BazaModels::Model::HasOneRelations
2
+ def self.included(base)
3
+ base.extend(ClassMethods)
4
+ end
5
+
6
+ module ClassMethods
7
+ # rubocop:disable Style/PredicateName
8
+ def has_one(relation_name, *all_args)
9
+ # rubocop:enable Style/PredicateName
10
+
11
+ args = all_args.pop
12
+
13
+ relation = {
14
+ type: :has_one,
15
+ relation_name: relation_name,
16
+ table_name: args[:table_name] || StringCases.pluralize(relation_name),
17
+ args: args,
18
+ all_args: all_args
19
+ }
20
+
21
+ if args[:foreign_key]
22
+ relation[:foreign_key] = args.fetch(:foreign_key)
23
+ else
24
+ relation[:foreign_key] = :"#{StringCases.camel_to_snake(name)}_id"
25
+ end
26
+
27
+ relation[:dependent] = args.fetch(:dependent) if args[:dependent]
28
+
29
+ if args && args[:class_name]
30
+ relation[:class_name] = args.fetch(:class_name)
31
+ else
32
+ relation[:class_name] = StringCases.snake_to_camel(relation_name)
33
+ end
34
+
35
+ @has_one_relations ||= []
36
+ @has_one_relations << relation
37
+
38
+ relationships[relation_name] = relation
39
+
40
+ define_method(relation_name) do
41
+ if (model = autoloads[relation_name])
42
+ model
43
+ else
44
+ if relation[:args][:through]
45
+ __send__(relation[:args][:through]).__send__(relation_name)
46
+ else
47
+ class_instance = Object.const_get(relation.fetch(:class_name))
48
+
49
+ query = class_instance.where(relation.fetch(:foreign_key) => id)
50
+ query._previous_model = self
51
+ query._relation = relation
52
+
53
+ all_args.each do |arg|
54
+ query = query.instance_exec(&arg) if arg.is_a?(Proc)
55
+ end
56
+
57
+ query.first
58
+ end
59
+ end
60
+ end
61
+ end
62
+ end
63
+
64
+ private
65
+
66
+ def restrict_has_one_relations
67
+ self.class.relationships.each do |relation_name, relation|
68
+ next if relation.fetch(:type) != :has_one || relation[:dependent] != :restrict_with_error
69
+
70
+ if __send__(relation_name)
71
+ errors.add(:base, "can't be destroyed because it contains #{relation_name}")
72
+ return false
73
+ end
74
+ end
75
+
76
+ true
77
+ end
78
+
79
+ def destroy_has_one_relations
80
+ self.class.relationships.each do |relation_name, relation|
81
+ next if relation.fetch(:type) != :has_one || relation[:dependent] != :destroy
82
+
83
+ model = __send__(relation_name)
84
+
85
+ if model && !model.destroy
86
+ errors.add(:base, model.errors.full_messages.join(". "))
87
+ return false
88
+ end
89
+ end
90
+
91
+ true
92
+ end
93
+ end
@@ -0,0 +1,123 @@
1
+ module BazaModels::Model::Manipulation
2
+ def self.included(base)
3
+ base.extend(ClassMethods)
4
+ end
5
+
6
+ def save(args = {})
7
+ if args[:validate] == false || valid?
8
+ new_record = new_record?
9
+ fire_callbacks(:before_save)
10
+ self.updated_at = Time.now if has_attribute?(:updated_at)
11
+
12
+ if new_record
13
+ status = create
14
+ else
15
+ db.update(table_name, @changes, id: id)
16
+ status = true
17
+ end
18
+
19
+ return false unless status
20
+
21
+ @changes = {}
22
+ @new_record = false
23
+ reload
24
+
25
+ fire_callbacks(:after_save)
26
+ fire_callbacks(:after_create) if new_record
27
+
28
+ return true
29
+ else
30
+ return false
31
+ end
32
+ end
33
+
34
+ def create
35
+ unless new_record?
36
+ errors.add(:base, "cannot create unless new record")
37
+ return false
38
+ end
39
+
40
+ fire_callbacks(:before_create)
41
+ self.created_at = Time.now if has_attribute?(:created_at) && !created_at?
42
+
43
+ @data[:id] = db.insert(table_name, @changes, return_id: true)
44
+
45
+ if @autoloads
46
+ @autoloads.each do |relation_name, collection|
47
+ relation = self.class.relationships.fetch(relation_name)
48
+
49
+ collection.each do |model|
50
+ model.assign_attributes(relation.fetch(:foreign_key) => id)
51
+ model.create! if model.new_record?
52
+ end
53
+ end
54
+ end
55
+
56
+ true
57
+ end
58
+
59
+ def create!
60
+ if create
61
+ return true
62
+ else
63
+ raise BazaModels::Errors::InvalidRecord, errors.full_messages.join(". ")
64
+ end
65
+ end
66
+
67
+ def save!(args = {})
68
+ if save(args)
69
+ return true
70
+ else
71
+ raise BazaModels::Errors::InvalidRecord, errors.full_messages.join(". ")
72
+ end
73
+ end
74
+
75
+ def update_attributes(attributes)
76
+ assign_attributes(attributes)
77
+ save
78
+ end
79
+
80
+ def update_attributes!(attributes)
81
+ raise BazaModels::Errors::InvalidRecord, @errors.full_messages.join(". ") unless update_attributes(attributes)
82
+ end
83
+
84
+ def assign_attributes(attributes)
85
+ @changes.merge!(real_attributes(attributes))
86
+ end
87
+
88
+ def destroy
89
+ if new_record?
90
+ errors.add(:base, "cannot destroy new record")
91
+ return false
92
+ else
93
+ fire_callbacks(:before_destroy)
94
+
95
+ return false unless restrict_has_one_relations
96
+ return false unless restrict_has_many_relations
97
+ return false unless destroy_has_one_relations
98
+ return false unless destroy_has_many_relations
99
+
100
+ db.delete(table_name, id: id)
101
+ fire_callbacks(:after_destroy)
102
+ return true
103
+ end
104
+ end
105
+
106
+ def destroy!
107
+ raise BazaModels::Errors::InvalidRecord, @errors.full_messages.join(". ") unless destroy
108
+ end
109
+
110
+ module ClassMethods
111
+ def create(data = {})
112
+ model = new(data)
113
+ model.save
114
+ model
115
+ end
116
+
117
+ def create!(data = {})
118
+ model = new(data)
119
+ model.save!
120
+ model
121
+ end
122
+ end
123
+ end
@@ -0,0 +1,45 @@
1
+ module BazaModels::Model::Queries
2
+ def self.included(base)
3
+ base.extend(ClassMethods)
4
+ end
5
+
6
+ module ClassMethods
7
+ def find(id)
8
+ row = db.select(table_name, {id: id}, limit: 1).fetch
9
+ raise BazaModels::Errors::RecordNotFound, "Record not found by ID: #{id}" unless row
10
+ new(row, init: true)
11
+ end
12
+
13
+ def find_by(where_hash)
14
+ row = db.select(table_name, where_hash, limit: 1).fetch
15
+ return new(row, init: true) if row
16
+ false
17
+ end
18
+
19
+ def find_by!(where_hash)
20
+ model = find_by(where_hash)
21
+ return model if model
22
+ raise BazaModels::Errors::RecordNotFound, "Record not found by arguments: #{where_hash}" unless model
23
+ end
24
+
25
+ def find_or_initialize_by(data)
26
+ model = find_by(data)
27
+ return model if model
28
+ new(data)
29
+ end
30
+
31
+ def find_or_create_by(data)
32
+ model = find_or_initialize_by(data)
33
+ model.save if model.new_record?
34
+ yield model if block_given?
35
+ model
36
+ end
37
+
38
+ def find_or_create_by!(data)
39
+ model = find_or_initialize_by(data)
40
+ model.save! if model.new_record?
41
+ yield model if block_given?
42
+ model
43
+ end
44
+ end
45
+ end