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