flatten_record 1.0.2 → 1.0.3
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +4 -4
- data/lib/flatten_record/definition.rb +16 -11
- data/lib/flatten_record/meta/id_column.rb +1 -2
- data/lib/flatten_record/meta/normalized_attr.rb +52 -35
- data/lib/flatten_record/version.rb +1 -1
- data/lib/generators/flatten_record/migration/migration_generator.rb +76 -62
- data/spec/dummy/log/test.log +14730 -0
- data/spec/lib/flatten_record/flattener/denormalize_spec.rb +61 -0
- data/spec/support/flattener.rb +2 -0
- metadata +1 -1
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA1:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: b3f760e4ba28e47a8cb7e46621300db8c99d36c9
|
4
|
+
data.tar.gz: 4129c16a709afbd09567ee9ce888257651f20a19
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: 5713c428677377b05b1feb93bc50fa821c173e6ccc33a52c6c571e1731c495d372b4f9006920d4ef811686b8d990ee6ef2f513f3efa3a9861e1d88fc71f851ba
|
7
|
+
data.tar.gz: 3ea39337ec9243801d6883bf1831267e3bbb28f70f6bf6af543dd8684e19ea7a4456e9c3c4029e2c507901d43900ba149525f9c94fa2c72e27148c8951a5cf2f
|
@@ -45,21 +45,13 @@ module FlattenRecord
|
|
45
45
|
|
46
46
|
protected
|
47
47
|
def validate_except(attrs)
|
48
|
-
attrs
|
49
|
-
error = "unknown attribute '#{attr}' in #{@target_model.name.to_s}"
|
50
|
-
@errors << error unless target_method?(attr)
|
51
|
-
@except << attr
|
52
|
-
end
|
48
|
+
validate_attrs(:except, attrs)
|
53
49
|
end
|
54
50
|
|
55
51
|
def validate_only(attrs)
|
56
|
-
attrs
|
57
|
-
error = "unknown attribute '#{attr}' in #{@target_model.name.to_s}"
|
58
|
-
@errors << error unless target_method?(attr)
|
59
|
-
@only << attr
|
60
|
-
end
|
52
|
+
validate_attrs(:only, attrs)
|
61
53
|
end
|
62
|
-
|
54
|
+
|
63
55
|
def validate_methods(methods)
|
64
56
|
methods.each do |method, type|
|
65
57
|
error = "undefined method '#{method}' in #{@target_model.name}"
|
@@ -92,6 +84,19 @@ module FlattenRecord
|
|
92
84
|
end
|
93
85
|
|
94
86
|
private
|
87
|
+
def validate_attrs(name, attrs)
|
88
|
+
attrs.each do |attr|
|
89
|
+
error = "unknown attribute '#{attr}' in #{@target_model.name.to_s}"
|
90
|
+
@errors << error unless target_method?(attr)
|
91
|
+
case name
|
92
|
+
when :only
|
93
|
+
@only << attr
|
94
|
+
when :except
|
95
|
+
@except << attr
|
96
|
+
end
|
97
|
+
end
|
98
|
+
end
|
99
|
+
|
95
100
|
def model_method?(method)
|
96
101
|
@model.attribute_method?(method) ||
|
97
102
|
@model.method_defined?(method)
|
@@ -7,12 +7,8 @@ module FlattenRecord
|
|
7
7
|
end
|
8
8
|
|
9
9
|
def all_columns
|
10
|
-
|
11
|
-
|
12
|
-
n[:columns] + n[:methods] + n[:compute]
|
13
|
-
end
|
14
|
-
child_columns.flatten!
|
15
|
-
@columns = @base_columns + @methods + @compute + child_columns
|
10
|
+
child_columns = @include.values.collect(&:all_columns)
|
11
|
+
@base_columns + @methods + @compute + child_columns.flatten
|
16
12
|
end
|
17
13
|
|
18
14
|
def associated_models
|
@@ -59,22 +55,27 @@ module FlattenRecord
|
|
59
55
|
|
60
56
|
def build(definition)
|
61
57
|
super(definition)
|
62
|
-
|
58
|
+
|
59
|
+
@foreign_keys = []
|
63
60
|
primary_key = target_columns.select(&:primary).first
|
64
61
|
@id_column = IdColumn.new(self, primary_key, target_model, model)
|
65
|
-
|
66
|
-
@excluded_columns = []
|
62
|
+
|
67
63
|
@compute = build_compute(definition) || []
|
68
64
|
@methods = build_methods(definition) || []
|
69
65
|
@include = build_children(definition) || {}
|
70
66
|
@base_columns = build_columns(definition) || []
|
71
67
|
|
72
|
-
|
73
|
-
|
74
|
-
raise "#{@excluded_columns.inspect}Duplicate columns found: #{dups.join(", ")}"
|
75
|
-
end
|
68
|
+
validate_columns(all_columns)
|
69
|
+
|
76
70
|
self
|
77
71
|
end
|
72
|
+
|
73
|
+
def validate_columns(columns)
|
74
|
+
dups = find_dup_columns(columns)
|
75
|
+
if dups.present?
|
76
|
+
raise "Duplicate columns found: #{dups.join(", ")}"
|
77
|
+
end
|
78
|
+
end
|
78
79
|
|
79
80
|
def children
|
80
81
|
(@base_columns + @compute + @methods + @include.values)
|
@@ -130,38 +131,54 @@ module FlattenRecord
|
|
130
131
|
end
|
131
132
|
|
132
133
|
private
|
133
|
-
def find_dup_columns
|
134
|
+
def find_dup_columns(columns)
|
134
135
|
dups = []
|
135
|
-
|
136
|
-
|
137
|
-
|
138
|
-
|
139
|
-
|
140
|
-
dups << "#{column.name} was from #{parent_target}'s #{original_name}"
|
141
|
-
end
|
136
|
+
columns.each do |column|
|
137
|
+
if match_columns?(columns, column)
|
138
|
+
parent_target = column.parent.target_model
|
139
|
+
original_name = column.column.name
|
140
|
+
dups << "#{column.name} was from #{parent_target}'s #{original_name}"
|
142
141
|
end
|
143
142
|
end
|
144
143
|
dups
|
145
144
|
end
|
146
145
|
|
147
|
-
def
|
148
|
-
|
146
|
+
def match_columns?(columns, column)
|
147
|
+
columns.each do |c|
|
148
|
+
if c.parent != column.parent && c.name == column.name
|
149
|
+
return true
|
150
|
+
end
|
151
|
+
end
|
152
|
+
false
|
153
|
+
end
|
149
154
|
|
155
|
+
def associated_node_factory(parent, child, class_name)
|
150
156
|
association = target_model.reflect_on_association(child)
|
151
|
-
|
152
|
-
|
153
|
-
class_name ||= association.klass
|
154
|
-
node = nil
|
157
|
+
|
158
|
+
raise_missing_macro(child, target_model) unless association.macro
|
155
159
|
|
156
|
-
if
|
157
|
-
|
158
|
-
|
159
|
-
|
160
|
-
|
160
|
+
if association_node?(association.macro)
|
161
|
+
class_name ||= association.klass
|
162
|
+
type = "#{association.macro}"
|
163
|
+
klass = Meta.const_get(type.camelize)
|
164
|
+
|
165
|
+
@foreign_keys << association.foreign_key.to_s
|
166
|
+
klass.new(parent, association, class_name, model)
|
161
167
|
else
|
162
|
-
|
168
|
+
raise_unsupported_type(association.macro, target_model)
|
163
169
|
end
|
164
|
-
|
170
|
+
end
|
171
|
+
|
172
|
+
def association_node?(type)
|
173
|
+
[:has_many, :belongs_to, :has_one].include?(type)
|
174
|
+
end
|
175
|
+
|
176
|
+
def raise_unsupported_type(type, model)
|
177
|
+
raise "association type '#{type}' with '#{model}' is not supported"
|
178
|
+
end
|
179
|
+
|
180
|
+
def raise_missing_macro(child, model)
|
181
|
+
raise "association with '#{child}' on #{model} is not found"
|
165
182
|
end
|
166
183
|
|
167
184
|
def columns_from_definition(definition)
|
@@ -176,7 +193,7 @@ module FlattenRecord
|
|
176
193
|
|
177
194
|
def allow_column?(col, definition)
|
178
195
|
return false if col.primary
|
179
|
-
return false if @
|
196
|
+
return false if @foreign_keys.include?(col.name.to_s)
|
180
197
|
|
181
198
|
if definition[:only].present?
|
182
199
|
definition[:only].include?(col.name.to_sym)
|
@@ -8,49 +8,51 @@ module FlattenRecord
|
|
8
8
|
@source_root ||= File.join(File.dirname(__FILE__), 'templates')
|
9
9
|
end
|
10
10
|
|
11
|
-
argument :name, :description => '
|
11
|
+
argument :name, :description => 'model'
|
12
12
|
|
13
13
|
def generate_files
|
14
|
-
|
15
|
-
|
16
|
-
|
17
|
-
|
18
|
-
|
19
|
-
|
20
|
-
|
21
|
-
@table_columns = denormalized_columns
|
22
|
-
|
23
|
-
case
|
24
|
-
when @klass.table_exists?
|
25
|
-
puts yellow("Warning. Table already exists: #{@table_name}")
|
26
|
-
diff_and_generate if meta
|
27
|
-
|
28
|
-
when meta
|
29
|
-
migration_template('migration.erb', "db/migrate/create_table_#{@table_name}.rb")
|
30
|
-
|
31
|
-
else
|
32
|
-
puts red("Error. No denormalization definition found in #{@table_name}")
|
33
|
-
end
|
14
|
+
return unless valid?
|
15
|
+
|
16
|
+
@table_name = klass.table_name
|
17
|
+
if klass.table_exists?
|
18
|
+
puts "Table already exists: #{@table_name}"
|
19
|
+
diff_and_generate
|
20
|
+
else
|
21
|
+
@table_columns = denormalized_columns
|
22
|
+
migration_template('migration.erb', "db/migrate/create_table_#{@table_name}.rb")
|
34
23
|
end
|
35
24
|
end
|
36
25
|
|
37
26
|
private
|
38
27
|
def meta
|
39
|
-
|
28
|
+
klass.flattener_meta
|
40
29
|
end
|
41
|
-
|
30
|
+
|
42
31
|
def denormalized_columns
|
43
|
-
|
32
|
+
meta.all_columns
|
44
33
|
end
|
45
|
-
|
34
|
+
|
35
|
+
def klass_name
|
36
|
+
name.camelize
|
37
|
+
end
|
38
|
+
|
39
|
+
def klass
|
40
|
+
klass_name.constantize
|
41
|
+
end
|
42
|
+
|
43
|
+
def table_name
|
44
|
+
klass.table_name
|
45
|
+
end
|
46
|
+
|
47
|
+
def flatten_klass_names
|
48
|
+
FlattenRecord::Config.included_models
|
49
|
+
end
|
50
|
+
|
46
51
|
def diff_and_generate
|
47
52
|
if any_diff?
|
48
|
-
puts
|
49
|
-
puts ""
|
50
|
-
puts "
|
51
|
-
puts ""
|
52
|
-
puts " #{red('Drop columns:')} #{drop_columns_names}" unless drop_columns.empty?
|
53
|
-
puts ""
|
53
|
+
puts "Generating migration based on the difference.."
|
54
|
+
puts " #{green('Add columns:')} #{add_columns_names}" unless add_columns.empty?
|
55
|
+
puts " #{red('Drop columns:')} #{drop_columns_names}" unless drop_columns.empty?
|
54
56
|
|
55
57
|
@migration = add_columns.empty? ?
|
56
58
|
"drop_#{drop_columns.first.name}_etc_from" :
|
@@ -60,15 +62,6 @@ module FlattenRecord
|
|
60
62
|
end
|
61
63
|
end
|
62
64
|
|
63
|
-
def colorize(text, color_code)
|
64
|
-
"\e[#{color_code}m#{text}\e[0m"
|
65
|
-
end
|
66
|
-
|
67
|
-
def red(text); colorize(text, 31); end
|
68
|
-
def green(text); colorize(text, 32); end
|
69
|
-
def yellow(text); colorize(text, 33); end
|
70
|
-
def blue(text); colorize(text, 34); end
|
71
|
-
|
72
65
|
def any_diff?
|
73
66
|
!add_columns.empty? || !drop_columns.empty?
|
74
67
|
end
|
@@ -81,38 +74,59 @@ module FlattenRecord
|
|
81
74
|
drop_columns.collect(&:name).join(', ')
|
82
75
|
end
|
83
76
|
|
84
|
-
def
|
85
|
-
|
86
|
-
cols ||= []
|
87
|
-
@klass.columns.collect(&:name).include?(col.name) ?
|
88
|
-
cols : cols << col
|
89
|
-
end
|
77
|
+
def columns
|
78
|
+
klass.columns
|
90
79
|
end
|
91
|
-
|
92
|
-
def
|
93
|
-
|
94
|
-
next if col.name == 'id'
|
95
|
-
cols ||= []
|
96
|
-
denormalized_columns.collect(&:name).include?(col.name) ?
|
97
|
-
cols : cols << col
|
98
|
-
end
|
80
|
+
|
81
|
+
def denormalized_column_names
|
82
|
+
denormalized_columns.map(&:name)
|
99
83
|
end
|
100
84
|
|
101
|
-
def
|
102
|
-
|
85
|
+
def column_names
|
86
|
+
columns.map(&:name)
|
103
87
|
end
|
104
88
|
|
105
|
-
def
|
106
|
-
@
|
107
|
-
|
89
|
+
def add_columns
|
90
|
+
@add_columns ||=
|
91
|
+
denormalized_columns.inject([]) do |cols, col|
|
92
|
+
if !column_names.include?(col.name)
|
93
|
+
cols << col
|
94
|
+
puts "#{col.name}"
|
95
|
+
end
|
96
|
+
cols
|
97
|
+
end
|
98
|
+
end
|
99
|
+
|
100
|
+
def drop_columns
|
101
|
+
@drop_columns ||=
|
102
|
+
columns.inject([]) do |cols, col|
|
103
|
+
if col.name != 'id' && !denormalized_column_names.include?(col.name)
|
104
|
+
cols << col
|
105
|
+
end
|
106
|
+
cols
|
107
|
+
end
|
108
108
|
end
|
109
109
|
|
110
110
|
def valid?
|
111
111
|
Rails.application.eager_load!
|
112
|
-
|
113
|
-
|
114
|
-
|
112
|
+
|
113
|
+
begin
|
114
|
+
klass && meta
|
115
|
+
rescue Exception => e
|
116
|
+
puts red(e.message)
|
117
|
+
false
|
118
|
+
end
|
119
|
+
end
|
120
|
+
|
121
|
+
def colorize(text, color_code)
|
122
|
+
"\e[#{color_code}m#{text}\e[0m"
|
115
123
|
end
|
124
|
+
|
125
|
+
def red(text); colorize(text, 31); end
|
126
|
+
def green(text); colorize(text, 32); end
|
127
|
+
def yellow(text); colorize(text, 33); end
|
128
|
+
def blue(text); colorize(text, 34); end
|
129
|
+
|
116
130
|
end
|
117
131
|
end
|
118
132
|
end
|