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.
- 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
|