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.
- checksums.yaml +4 -4
- data/.rubocop_todo.yml +35 -0
- data/Gemfile +9 -3
- data/Gemfile.lock +90 -51
- data/README.md +98 -3
- data/Rakefile +20 -17
- data/VERSION +1 -1
- data/baza_models.gemspec +149 -0
- data/config/best_project_practice_rubocop.yml +2 -0
- data/config/best_project_practice_rubocop_todo.yml +35 -0
- data/lib/baza_models.rb +15 -0
- data/lib/baza_models/autoloader.rb +135 -0
- data/lib/baza_models/baza_orm_adapter.rb +39 -0
- data/lib/baza_models/can_can_adapter.rb +9 -0
- data/lib/baza_models/class_translation.rb +44 -0
- data/lib/baza_models/errors.rb +24 -2
- data/lib/baza_models/model.rb +236 -115
- data/lib/baza_models/model/belongs_to_relations.rb +49 -0
- data/lib/baza_models/model/custom_validations.rb +24 -0
- data/lib/baza_models/model/delegation.rb +21 -0
- data/lib/baza_models/model/has_many_relations.rb +85 -0
- data/lib/baza_models/model/has_one_relations.rb +93 -0
- data/lib/baza_models/model/manipulation.rb +123 -0
- data/lib/baza_models/model/queries.rb +45 -0
- data/lib/baza_models/model/scopes.rb +19 -0
- data/lib/baza_models/model/translation_functionality.rb +30 -0
- data/lib/baza_models/model/validations.rb +95 -0
- data/lib/baza_models/query.rb +447 -0
- data/lib/baza_models/query/inspector.rb +74 -0
- data/lib/baza_models/query/not.rb +34 -0
- data/lib/baza_models/ransacker.rb +30 -0
- data/lib/baza_models/test_database_cleaner.rb +23 -0
- data/lib/baza_models/validators/base_validator.rb +14 -0
- data/lib/baza_models/validators/confirmation_validator.rb +12 -0
- data/lib/baza_models/validators/format_validator.rb +11 -0
- data/lib/baza_models/validators/length_validator.rb +16 -0
- data/lib/baza_models/validators/uniqueness_validator.rb +21 -0
- data/shippable.yml +3 -1
- data/spec/baza_models/autoloader_spec.rb +57 -0
- data/spec/baza_models/baza_orm_adapter_spec.rb +52 -0
- data/spec/baza_models/class_translation_spec.rb +25 -0
- data/spec/baza_models/factory_girl_spec.rb +13 -0
- data/spec/baza_models/model/belongs_to_relations_spec.rb +26 -0
- data/spec/baza_models/model/custom_validations_spec.rb +18 -0
- data/spec/baza_models/model/delgation_spec.rb +16 -0
- data/spec/baza_models/model/has_many_relations_spec.rb +68 -0
- data/spec/baza_models/model/has_one_relations_spec.rb +35 -0
- data/spec/baza_models/model/manipulation_spec.rb +25 -0
- data/spec/baza_models/model/queries_spec.rb +59 -0
- data/spec/baza_models/model/scopes_spec.rb +23 -0
- data/spec/baza_models/model/translate_functionality_spec.rb +13 -0
- data/spec/baza_models/model/validations_spec.rb +52 -0
- data/spec/baza_models/model_spec.rb +75 -98
- data/spec/baza_models/query/not_spec.rb +16 -0
- data/spec/baza_models/query_spec.rb +155 -0
- data/spec/baza_models/ransacker_spec.rb +15 -0
- data/spec/baza_models/validators/confirmation_validator_spec.rb +28 -0
- data/spec/baza_models/validators/format_validator_spec.rb +17 -0
- data/spec/baza_models/validators/length_validator_spec.rb +19 -0
- data/spec/baza_models/validators/uniqueness_validator_spec.rb +24 -0
- data/spec/factories/organization.rb +5 -0
- data/spec/factories/user.rb +7 -0
- data/spec/spec_helper.rb +17 -5
- data/spec/support/database_helper.rb +87 -0
- data/spec/test_classes/organization.rb +3 -0
- data/spec/test_classes/person.rb +3 -0
- data/spec/test_classes/role.rb +12 -0
- data/spec/test_classes/user.rb +40 -0
- data/spec/test_classes/user_passport.rb +3 -0
- metadata +146 -7
- 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
|