baza_models 0.0.0 → 0.0.1

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