-
Notifications
You must be signed in to change notification settings - Fork 1
/
modelizer.rb
138 lines (108 loc) · 3.64 KB
/
modelizer.rb
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
require "modelizer/assertions"
require "modelizer/validations"
module Modelizer
# Duh.
VERSION = "4.0.0"
include Modelizer::Assertions
# Test classes that should be considered abstract when rendering
# tests for a model template.
TEST_CLASSES = []
%w(Test::Unit::TestCase Minitest::Unit::TestCase
ActiveSupport::TestCase).each do |k|
TEST_CLASSES <<
k.split("::").inject(Object) { |a, b| a.const_get b } rescue nil
end
@@cache = {}
def self.cache; @@cache end
def self.included target
target.extend ClassMethods
target.extend Modelizer::Validations
end
def self.method_name_for model_class
underscore model_class.name
end
def self.model_class_for test_class
test_class.name.gsub(/Test$/, "").constantize
end
def self.underscore classname
classname.gsub(/::/, '_').
gsub(/([A-Z]+)([A-Z][a-z])/,'\1_\2').
gsub(/([a-z\d])([A-Z])/,'\1_\2').
tr("-", "_").
downcase
end
def assign_model_template_attributes model, attributes
model.assign_attributes attributes, without_protection: true
model
end
def valid_model_template_attributes klass, extras = {}
defaults, block = ::Modelizer.cache[klass]
lazy = block && instance_eval(&block)
[defaults, lazy, extras].compact.inject { |t, s| t.merge s }
end
def valid_model_template_attributes_without klass, excluded
valid_model_template_attributes(klass).delete_if do |k, v|
excluded.include? k
end
end
module ClassMethods
def model_template_for klass, defaults = {}, &block
if defaults.nil? && !block
raise ArgumentError, "default attributes or lazy block required"
end
::Modelizer.cache[klass] = [defaults, block]
model = ::Modelizer.method_name_for klass
klass = klass.name
module_eval <<-END, __FILE__, __LINE__ + 1
def valid_#{model}_attributes extras = {}
valid_model_template_attributes #{klass}, extras
end
def valid_#{model}_attributes_without *excluded
valid_model_template_attributes_without #{klass}, excluded
end
def new_#{model} extras = {}
assign_model_template_attributes #{klass}.new,
valid_model_template_attributes(#{klass}, extras)
end
def new_#{model}_without *excluded
assign_model_template_attributes #{klass}.new,
valid_model_template_attributes_without(#{klass}, excluded)
end
def create_#{model} extras = {}
(m = new_#{model}(extras)).save; m
end
def create_#{model}! extras = {}
(m = new_#{model}(extras)).save!; m
end
def create_#{model}_without *excluded
(m = new_#{model}_without(*excluded)).save; m
end
def create_#{model}_without! *excluded
(m = new_#{model}_without(*excluded)).save!; m
end
END
# Install a test that ensures the model template is valid. If
# the template is defined in one of the abstract test
# superclasses, generate a whole new testcase. If it's in a
# concrete test, just generate a method.
file, line = caller.first.split ":"
line = line.to_i
test = <<-END
def test_model_template_for_#{model}
assert (m = new_#{model}).valid?,
"#{klass} template is invalid: " +
m.errors.full_messages.to_sentence
end
END
if TEST_CLASSES.include? self
eval <<-END, nil, file, line - 2
class ::ModelTemplateFor#{klass}Test < ActiveSupport::TestCase
#{test}
end
END
else
module_eval test, file, line - 1
end
end
end
end