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
data/lib/baza_models/errors.rb
CHANGED
@@ -1,5 +1,6 @@
|
|
1
1
|
class BazaModels::Errors
|
2
2
|
class InvalidRecord < RuntimeError; end
|
3
|
+
class RecordNotFound < RuntimeError; end
|
3
4
|
|
4
5
|
def initialize
|
5
6
|
@errors = {}
|
@@ -14,10 +15,19 @@ class BazaModels::Errors
|
|
14
15
|
messages = []
|
15
16
|
|
16
17
|
@errors.each do |attribute_name, errors|
|
17
|
-
|
18
|
+
errors.each do |error|
|
19
|
+
message = ""
|
20
|
+
|
21
|
+
unless attribute_name == :base
|
22
|
+
message << "#{StringCases.snake_to_camel(attribute_name)} "
|
23
|
+
end
|
24
|
+
|
25
|
+
message << error
|
26
|
+
messages << message
|
27
|
+
end
|
18
28
|
end
|
19
29
|
|
20
|
-
|
30
|
+
messages
|
21
31
|
end
|
22
32
|
|
23
33
|
def empty?
|
@@ -27,4 +37,16 @@ class BazaModels::Errors
|
|
27
37
|
def any?
|
28
38
|
full_messages.any?
|
29
39
|
end
|
40
|
+
|
41
|
+
def to_s
|
42
|
+
"#<BazaModels::Errors full_messages=\"#{full_messages}\">"
|
43
|
+
end
|
44
|
+
|
45
|
+
def inspect
|
46
|
+
to_s
|
47
|
+
end
|
48
|
+
|
49
|
+
def [](key)
|
50
|
+
@errors[key] || []
|
51
|
+
end
|
30
52
|
end
|
data/lib/baza_models/model.rb
CHANGED
@@ -1,21 +1,51 @@
|
|
1
1
|
require "string-cases"
|
2
2
|
|
3
3
|
class BazaModels::Model
|
4
|
-
|
4
|
+
path = "#{File.dirname(__FILE__)}/model"
|
5
|
+
|
6
|
+
autoload :BelongsToRelations, "#{path}/belongs_to_relations"
|
7
|
+
autoload :CustomValidations, "#{path}/custom_validations"
|
8
|
+
autoload :Delegation, "#{path}/delegation"
|
9
|
+
autoload :HasManyRelations, "#{path}/has_many_relations"
|
10
|
+
autoload :HasOneRelations, "#{path}/has_one_relations"
|
11
|
+
autoload :Manipulation, "#{path}/manipulation"
|
12
|
+
autoload :Queries, "#{path}/queries"
|
13
|
+
autoload :Scopes, "#{path}/scopes"
|
14
|
+
autoload :TranslationFunctionality, "#{path}/translation_functionality"
|
15
|
+
autoload :Validations, "#{path}/validations"
|
16
|
+
|
17
|
+
include BelongsToRelations
|
18
|
+
include Delegation
|
19
|
+
include CustomValidations
|
20
|
+
include HasManyRelations
|
21
|
+
include HasOneRelations
|
22
|
+
include Manipulation
|
23
|
+
include Queries
|
24
|
+
include Scopes
|
25
|
+
include TranslationFunctionality
|
26
|
+
include Validations
|
27
|
+
|
28
|
+
attr_accessor :data, :db
|
5
29
|
attr_reader :changes, :errors
|
6
30
|
|
7
31
|
# Define all callback methods.
|
8
|
-
CALLBACK_TYPES = [
|
32
|
+
CALLBACK_TYPES = [
|
33
|
+
:before_create, :after_create, :before_save, :after_save, :before_destroy, :after_destroy,
|
9
34
|
:before_validation, :after_validation, :before_validation_on_create, :after_validation_on_create,
|
10
|
-
:before_validation_on_update, :after_validation_on_update
|
35
|
+
:before_validation_on_update, :after_validation_on_update
|
36
|
+
]
|
11
37
|
|
12
38
|
CALLBACK_TYPES.each do |callback_type|
|
39
|
+
# rubocop:disable Style/ClassVars
|
13
40
|
@@callbacks ||= {}
|
14
|
-
|
41
|
+
# rubocop:enable Style/ClassVars
|
42
|
+
|
43
|
+
@@callbacks[callback_type] = {}
|
15
44
|
callbacks = @@callbacks
|
16
45
|
|
17
|
-
(class << self; self; end).
|
18
|
-
callbacks[callback_type]
|
46
|
+
(class << self; self; end).__send__(:define_method, callback_type) do |method_name = nil, *args, &blk|
|
47
|
+
callbacks[callback_type][name] ||= []
|
48
|
+
callbacks[callback_type][name] << {
|
19
49
|
block: blk,
|
20
50
|
method_name: method_name,
|
21
51
|
args: args
|
@@ -23,13 +53,29 @@ class BazaModels::Model
|
|
23
53
|
end
|
24
54
|
end
|
25
55
|
|
26
|
-
|
56
|
+
|
57
|
+
QUERY_METHODS = [
|
58
|
+
:all, :any?, :empty?, :none?, :count, :first, :find_first, :last, :length, :select, :includes,
|
59
|
+
:joins, :group, :where, :order, :limit, :to_a, :accessible_by
|
60
|
+
]
|
61
|
+
QUERY_METHODS.each do |query_method|
|
62
|
+
(class << self; self; end).__send__(:define_method, query_method) do |*args, &blk|
|
63
|
+
BazaModels::Query.new(model: self).__send__(query_method, *args, &blk)
|
64
|
+
end
|
65
|
+
end
|
66
|
+
|
67
|
+
def initialize(data = {}, args = {})
|
27
68
|
self.class.init_model unless self.class.model_initialized?
|
28
69
|
|
29
|
-
|
70
|
+
reset_errors
|
30
71
|
@changes = {}
|
31
72
|
|
32
|
-
|
73
|
+
if args[:init]
|
74
|
+
@data = self.class.__blank_attributes.merge(real_attributes(data))
|
75
|
+
else
|
76
|
+
@data = self.class.__blank_attributes.clone
|
77
|
+
@changes.merge!(real_attributes(data))
|
78
|
+
end
|
33
79
|
|
34
80
|
if @data[:id]
|
35
81
|
@new_record = false
|
@@ -38,18 +84,45 @@ class BazaModels::Model
|
|
38
84
|
end
|
39
85
|
end
|
40
86
|
|
87
|
+
# rubocop:disable Style/TrivialAccessors
|
41
88
|
def new_record?
|
42
|
-
|
89
|
+
# rubocop:enable Style/TrivialAccessors
|
90
|
+
|
91
|
+
@new_record
|
92
|
+
end
|
93
|
+
|
94
|
+
def persisted?
|
95
|
+
!new_record?
|
43
96
|
end
|
44
97
|
|
45
98
|
def db
|
46
99
|
return @db if @db
|
47
|
-
|
100
|
+
@db ||= self.class.db
|
48
101
|
end
|
49
102
|
|
103
|
+
attr_writer :db
|
104
|
+
|
50
105
|
def self.db
|
106
|
+
@db = nil if @db && @db.closed?
|
51
107
|
return @db if @db
|
52
|
-
|
108
|
+
@db ||= BazaModels.primary_db
|
109
|
+
raise "No Baza database has been configured" unless @db
|
110
|
+
@db
|
111
|
+
end
|
112
|
+
|
113
|
+
def self.to_adapter
|
114
|
+
BazaModels::BazaOrmAdapter.new(class: self)
|
115
|
+
end
|
116
|
+
|
117
|
+
class << self
|
118
|
+
attr_writer :db, :table_name
|
119
|
+
end
|
120
|
+
|
121
|
+
attr_writer :table_name
|
122
|
+
|
123
|
+
def autoloads
|
124
|
+
@autoloads ||= {}
|
125
|
+
@autoloads
|
53
126
|
end
|
54
127
|
|
55
128
|
def table_name
|
@@ -57,172 +130,220 @@ class BazaModels::Model
|
|
57
130
|
end
|
58
131
|
|
59
132
|
def self.table_name
|
60
|
-
@table_name ||= "#{StringCases.camel_to_snake(name)}s"
|
133
|
+
@table_name ||= "#{StringCases.camel_to_snake(name.gsub("::", ""))}s"
|
61
134
|
end
|
62
135
|
|
63
|
-
def self.
|
64
|
-
@
|
65
|
-
|
66
|
-
init_attribute_from_column(column)
|
67
|
-
end
|
136
|
+
def self.relationships
|
137
|
+
@relationships ||= {}
|
138
|
+
end
|
68
139
|
|
69
|
-
|
140
|
+
def to_model
|
141
|
+
self
|
70
142
|
end
|
71
143
|
|
72
|
-
def self.
|
73
|
-
|
144
|
+
def self.init_model
|
145
|
+
@table = db.tables[table_name]
|
74
146
|
|
75
|
-
|
76
|
-
return @changes.fetch(column_name) if @changes.key?(column_name)
|
77
|
-
return @data.fetch(column_name)
|
78
|
-
end
|
147
|
+
@__blank_attributes ||= {}
|
79
148
|
|
80
|
-
|
81
|
-
|
149
|
+
@table.columns do |column|
|
150
|
+
init_attribute_from_column(column)
|
151
|
+
@__blank_attributes[column.name.to_sym] = nil
|
82
152
|
end
|
83
153
|
|
84
|
-
|
85
|
-
@changes[column_name] = new_value
|
86
|
-
end
|
154
|
+
@model_initialized = true
|
87
155
|
end
|
88
156
|
|
89
|
-
def
|
90
|
-
|
157
|
+
def id
|
158
|
+
@data.fetch(:id)
|
91
159
|
end
|
92
160
|
|
93
|
-
def
|
94
|
-
|
95
|
-
|
96
|
-
class_name = "#{validator_camel_name}Validator"
|
161
|
+
def to_param
|
162
|
+
id.to_s if id
|
163
|
+
end
|
97
164
|
|
98
|
-
|
99
|
-
|
100
|
-
|
165
|
+
def to_key
|
166
|
+
if new_record?
|
167
|
+
nil
|
168
|
+
else
|
169
|
+
[id]
|
101
170
|
end
|
102
171
|
end
|
103
172
|
|
104
|
-
def
|
105
|
-
|
173
|
+
def reload
|
174
|
+
@data = db.single(table_name, {id: id}, limit: 1)
|
175
|
+
raise BazaModels::Errors::RecordNotFound unless @data
|
176
|
+
@changes = {}
|
177
|
+
self
|
106
178
|
end
|
107
179
|
|
108
|
-
def
|
109
|
-
if
|
110
|
-
|
111
|
-
|
180
|
+
def to_s
|
181
|
+
if new_record?
|
182
|
+
"#<#{self.class.name} new!>"
|
183
|
+
else
|
184
|
+
"#<#{self.class.name} id=#{id}>"
|
185
|
+
end
|
186
|
+
end
|
112
187
|
|
113
|
-
|
114
|
-
|
115
|
-
|
188
|
+
def inspect
|
189
|
+
data_str = ""
|
190
|
+
@data.each do |key, value|
|
191
|
+
if @changes.key?(key)
|
192
|
+
value_to_use = @changes.fetch(key)
|
116
193
|
else
|
117
|
-
|
194
|
+
value_to_use = value
|
118
195
|
end
|
119
196
|
|
120
|
-
|
121
|
-
|
122
|
-
|
197
|
+
data_str << " " unless data_str.empty?
|
198
|
+
data_str << "#{key}=\"#{value_to_use}\""
|
199
|
+
end
|
200
|
+
|
201
|
+
"#<#{self.class.name} #{data_str}>"
|
202
|
+
end
|
123
203
|
|
124
|
-
|
125
|
-
|
204
|
+
def ==(other)
|
205
|
+
return false unless self.class == other.class
|
126
206
|
|
127
|
-
|
207
|
+
if new_record? && other.new_record?
|
208
|
+
return merged_data == other.__send__(:merged_data)
|
128
209
|
else
|
129
|
-
return
|
210
|
+
return id == other.id
|
130
211
|
end
|
131
212
|
end
|
132
213
|
|
133
|
-
|
134
|
-
|
135
|
-
|
136
|
-
|
137
|
-
raise BazaModels::Errors::InvalidRecord
|
138
|
-
end
|
214
|
+
# rubocop:disable Style/PredicateName
|
215
|
+
def has_attribute?(name)
|
216
|
+
# rubocop:enable Style/PredicateName
|
217
|
+
self.class.column_names.include?(name.to_s)
|
139
218
|
end
|
140
219
|
|
141
|
-
def
|
142
|
-
@
|
143
|
-
@changes = {}
|
144
|
-
return self
|
220
|
+
def self.column_names
|
221
|
+
@column_names ||= __blank_attributes.keys.map(&:to_s)
|
145
222
|
end
|
146
223
|
|
147
|
-
def
|
148
|
-
|
149
|
-
errors.add(:base, "cannot destroy new record")
|
150
|
-
return false
|
151
|
-
else
|
152
|
-
fire_callbacks(:before_destroy)
|
153
|
-
db.delete(table_name, id: id)
|
154
|
-
fire_callbacks(:after_destroy)
|
155
|
-
return true
|
156
|
-
end
|
224
|
+
def self.ransack(params)
|
225
|
+
BazaModels::Ransacker.new(class: self, params: params)
|
157
226
|
end
|
158
227
|
|
159
|
-
def
|
160
|
-
|
228
|
+
def [](key)
|
229
|
+
read_attribute(key)
|
161
230
|
end
|
162
231
|
|
163
|
-
def
|
164
|
-
|
232
|
+
def []=(key, value)
|
233
|
+
write_attribute(key, value)
|
165
234
|
end
|
166
235
|
|
167
|
-
def
|
168
|
-
|
169
|
-
|
236
|
+
def read_attribute(attribute_name)
|
237
|
+
return @changes.fetch(attribute_name) if @changes.key?(attribute_name)
|
238
|
+
@data.fetch(attribute_name)
|
170
239
|
end
|
171
240
|
|
172
|
-
def
|
173
|
-
|
174
|
-
|
241
|
+
def write_attribute(attribute_name, value)
|
242
|
+
@changes[attribute_name] = value
|
243
|
+
end
|
244
|
+
|
245
|
+
def changed?
|
246
|
+
changed = false
|
247
|
+
@changes.each do |key, value|
|
248
|
+
next if @data.fetch(key) == value
|
249
|
+
changed = true
|
250
|
+
break
|
175
251
|
end
|
252
|
+
|
253
|
+
changed
|
176
254
|
end
|
177
255
|
|
178
|
-
|
179
|
-
fire_callbacks(:before_validation)
|
256
|
+
protected
|
180
257
|
|
181
|
-
|
182
|
-
|
183
|
-
|
184
|
-
fire_callbacks(:before_validation_on_update)
|
185
|
-
end
|
258
|
+
class << self
|
259
|
+
attr_reader :__blank_attributes
|
260
|
+
end
|
186
261
|
|
187
|
-
|
262
|
+
# rubocop:disable Style/TrivialAccessors
|
263
|
+
def self.model_initialized?
|
264
|
+
# rubocop:enable Style/TrivialAccessors
|
265
|
+
@model_initialized
|
266
|
+
end
|
188
267
|
|
189
|
-
|
190
|
-
|
191
|
-
next unless @@validators.key?(attribute_name)
|
268
|
+
def self.init_attribute_from_column(column)
|
269
|
+
column_name = column.name.to_sym
|
192
270
|
|
193
|
-
|
194
|
-
|
195
|
-
|
271
|
+
define_method(column_name) do
|
272
|
+
read_attribute(column_name)
|
273
|
+
end
|
274
|
+
|
275
|
+
define_method("#{column_name}_was") do
|
276
|
+
return @data.fetch(column_name)
|
196
277
|
end
|
197
278
|
|
198
|
-
|
279
|
+
define_method("#{column_name}=") do |new_value|
|
280
|
+
write_attribute(column_name, new_value)
|
281
|
+
end
|
199
282
|
|
200
|
-
|
201
|
-
|
202
|
-
else
|
203
|
-
fire_callbacks(:after_validation_on_update)
|
283
|
+
define_method("#{column_name}?") do
|
284
|
+
!@data.fetch(column_name).to_s.strip.empty?
|
204
285
|
end
|
205
286
|
|
206
|
-
|
287
|
+
define_method("#{column_name}_changed?") do
|
288
|
+
if @changes.key?(column_name) && @changes.fetch(column_name) != @data.fetch(column_name)
|
289
|
+
true
|
290
|
+
else
|
291
|
+
false
|
292
|
+
end
|
293
|
+
end
|
207
294
|
end
|
208
295
|
|
209
|
-
protected
|
210
|
-
|
211
296
|
def reset_errors
|
212
297
|
@errors = BazaModels::Errors.new
|
213
298
|
end
|
214
299
|
|
215
300
|
def fire_callbacks(name)
|
216
|
-
if
|
217
|
-
|
218
|
-
|
219
|
-
|
220
|
-
|
221
|
-
|
301
|
+
return if !@@callbacks[name] || !@@callbacks[name][self.class.name]
|
302
|
+
|
303
|
+
@@callbacks[name][self.class.name].each do |callback_data|
|
304
|
+
if callback_data[:block]
|
305
|
+
instance_eval(&callback_data.fetch(:block))
|
306
|
+
elsif callback_data[:method_name]
|
307
|
+
__send__(callback_data[:method_name], *callback_data.fetch(:args))
|
308
|
+
else
|
309
|
+
raise "Didn't know how to perform callbacks for #{name}"
|
310
|
+
end
|
311
|
+
end
|
312
|
+
end
|
313
|
+
|
314
|
+
def merged_data
|
315
|
+
@data.merge(@changes)
|
316
|
+
end
|
317
|
+
|
318
|
+
# Converts attributes like "user" to "user_id" and so on.
|
319
|
+
def real_attributes(attributes)
|
320
|
+
new_attributes = {}
|
321
|
+
attributes.each do |attribute_name, attribute_value|
|
322
|
+
belongs_to_relations = self.class.instance_variable_get(:@belongs_to_relations)
|
323
|
+
|
324
|
+
if belongs_to_relations
|
325
|
+
belongs_to_relations.each do |relation|
|
326
|
+
if attribute_name.to_s == relation[:relation_name].to_s
|
327
|
+
attribute_name = :"#{attribute_name}_id"
|
328
|
+
attribute_value = attribute_value.id
|
329
|
+
end
|
330
|
+
end
|
331
|
+
end
|
332
|
+
|
333
|
+
unless has_attribute?(attribute_name)
|
334
|
+
set_method_name = "#{attribute_name}="
|
335
|
+
|
336
|
+
if respond_to?(set_method_name)
|
337
|
+
__send__(set_method_name, attribute_value)
|
338
|
+
next
|
222
339
|
else
|
223
|
-
raise "
|
340
|
+
raise "Unknown attribute: #{attribute_name}"
|
224
341
|
end
|
225
342
|
end
|
343
|
+
|
344
|
+
new_attributes[attribute_name.to_sym] = attribute_value
|
226
345
|
end
|
346
|
+
|
347
|
+
new_attributes
|
227
348
|
end
|
228
349
|
end
|