coupler 0.0.7-java → 0.0.8-java
Sign up to get free protection for your applications and to get access to all the features.
- data/VERSION +1 -1
- data/coupler.gemspec +11 -2
- data/db/migrate/024_add_error_msg_to_jobs.rb +5 -0
- data/db/migrate/025_add_notifications.rb +16 -0
- data/db/migrate/026_add_status_to_resources.rb +7 -0
- data/db/migrate/027_add_notification_id_to_jobs.rb +8 -0
- data/lib/coupler.rb +6 -2
- data/lib/coupler/base.rb +1 -0
- data/lib/coupler/extensions.rb +3 -1
- data/lib/coupler/extensions/imports.rb +11 -14
- data/lib/coupler/extensions/notifications.rb +26 -0
- data/lib/coupler/models.rb +1 -0
- data/lib/coupler/models/comparison.rb +0 -1
- data/lib/coupler/models/connection.rb +0 -1
- data/lib/coupler/models/field.rb +6 -6
- data/lib/coupler/models/import.rb +9 -3
- data/lib/coupler/models/job.rb +64 -20
- data/lib/coupler/models/matcher.rb +0 -1
- data/lib/coupler/models/notification.rb +7 -0
- data/lib/coupler/models/project.rb +0 -1
- data/lib/coupler/models/resource.rb +52 -31
- data/lib/coupler/models/result.rb +0 -1
- data/lib/coupler/models/scenario.rb +0 -1
- data/lib/coupler/models/transformation.rb +2 -3
- data/lib/coupler/models/transformer.rb +0 -1
- data/lib/coupler/scheduler.rb +8 -0
- data/tasks/db.rake +2 -2
- data/test/functional/test_imports.rb +21 -13
- data/test/functional/test_notifications.rb +38 -0
- data/test/integration/test_import.rb +25 -1
- data/test/unit/models/test_field.rb +2 -13
- data/test/unit/models/test_import.rb +4 -0
- data/test/unit/models/test_job.rb +59 -6
- data/test/unit/models/test_notification.rb +17 -0
- data/test/unit/models/test_resource.rb +114 -29
- data/test/unit/models/test_transformation.rb +4 -4
- data/test/unit/test_base.rb +1 -1
- data/test/unit/test_scheduler.rb +11 -0
- data/webroot/public/css/style.css +23 -0
- data/webroot/public/js/application.js +31 -10
- data/webroot/views/jobs/list.erb +2 -0
- data/webroot/views/layout.erb +3 -1
- data/webroot/views/notifications/index.erb +16 -0
- data/webroot/views/resources/list.erb +12 -7
- data/webroot/views/sidebar.erb +2 -0
- metadata +11 -2
data/VERSION
CHANGED
@@ -1 +1 @@
|
|
1
|
-
0.0.
|
1
|
+
0.0.8
|
data/coupler.gemspec
CHANGED
@@ -5,12 +5,12 @@
|
|
5
5
|
|
6
6
|
Gem::Specification.new do |s|
|
7
7
|
s.name = %q{coupler}
|
8
|
-
s.version = "0.0.
|
8
|
+
s.version = "0.0.8"
|
9
9
|
s.platform = %q{java}
|
10
10
|
|
11
11
|
s.required_rubygems_version = Gem::Requirement.new(">= 0") if s.respond_to? :required_rubygems_version=
|
12
12
|
s.authors = ["Jeremy Stephens"]
|
13
|
-
s.date = %q{2011-
|
13
|
+
s.date = %q{2011-08-16}
|
14
14
|
s.default_executable = %q{coupler}
|
15
15
|
s.description = %q{Coupler is a (JRuby) desktop application designed to link datasets together}
|
16
16
|
s.email = %q{jeremy.f.stephens@vanderbilt.edu}
|
@@ -59,6 +59,10 @@ Gem::Specification.new do |s|
|
|
59
59
|
"db/migrate/021_add_fields_to_connections.rb",
|
60
60
|
"db/migrate/022_remove_database_name_from_resources.rb",
|
61
61
|
"db/migrate/023_add_import_jobs.rb",
|
62
|
+
"db/migrate/024_add_error_msg_to_jobs.rb",
|
63
|
+
"db/migrate/025_add_notifications.rb",
|
64
|
+
"db/migrate/026_add_status_to_resources.rb",
|
65
|
+
"db/migrate/027_add_notification_id_to_jobs.rb",
|
62
66
|
"features/connections.feature",
|
63
67
|
"features/matchers.feature",
|
64
68
|
"features/projects.feature",
|
@@ -86,6 +90,7 @@ Gem::Specification.new do |s|
|
|
86
90
|
"lib/coupler/extensions/imports.rb",
|
87
91
|
"lib/coupler/extensions/jobs.rb",
|
88
92
|
"lib/coupler/extensions/matchers.rb",
|
93
|
+
"lib/coupler/extensions/notifications.rb",
|
89
94
|
"lib/coupler/extensions/projects.rb",
|
90
95
|
"lib/coupler/extensions/resources.rb",
|
91
96
|
"lib/coupler/extensions/results.rb",
|
@@ -104,6 +109,7 @@ Gem::Specification.new do |s|
|
|
104
109
|
"lib/coupler/models/job.rb",
|
105
110
|
"lib/coupler/models/jobify.rb",
|
106
111
|
"lib/coupler/models/matcher.rb",
|
112
|
+
"lib/coupler/models/notification.rb",
|
107
113
|
"lib/coupler/models/project.rb",
|
108
114
|
"lib/coupler/models/resource.rb",
|
109
115
|
"lib/coupler/models/result.rb",
|
@@ -138,6 +144,7 @@ Gem::Specification.new do |s|
|
|
138
144
|
"test/functional/test_imports.rb",
|
139
145
|
"test/functional/test_jobs.rb",
|
140
146
|
"test/functional/test_matchers.rb",
|
147
|
+
"test/functional/test_notifications.rb",
|
141
148
|
"test/functional/test_projects.rb",
|
142
149
|
"test/functional/test_resources.rb",
|
143
150
|
"test/functional/test_results.rb",
|
@@ -158,6 +165,7 @@ Gem::Specification.new do |s|
|
|
158
165
|
"test/unit/models/test_import.rb",
|
159
166
|
"test/unit/models/test_job.rb",
|
160
167
|
"test/unit/models/test_matcher.rb",
|
168
|
+
"test/unit/models/test_notification.rb",
|
161
169
|
"test/unit/models/test_project.rb",
|
162
170
|
"test/unit/models/test_resource.rb",
|
163
171
|
"test/unit/models/test_result.rb",
|
@@ -242,6 +250,7 @@ Gem::Specification.new do |s|
|
|
242
250
|
"webroot/views/layout.erb",
|
243
251
|
"webroot/views/matchers/form.erb",
|
244
252
|
"webroot/views/matchers/list.erb",
|
253
|
+
"webroot/views/notifications/index.erb",
|
245
254
|
"webroot/views/projects/form.erb",
|
246
255
|
"webroot/views/projects/index.erb",
|
247
256
|
"webroot/views/projects/show.erb",
|
@@ -0,0 +1,16 @@
|
|
1
|
+
Sequel.migration do
|
2
|
+
up do
|
3
|
+
create_table(:notifications) do
|
4
|
+
primary_key :id
|
5
|
+
String :message
|
6
|
+
String :url
|
7
|
+
TrueClass :seen
|
8
|
+
Integer :import_id
|
9
|
+
Time :created_at
|
10
|
+
Time :updated_at
|
11
|
+
end
|
12
|
+
end
|
13
|
+
down do
|
14
|
+
drop_table(:notifications)
|
15
|
+
end
|
16
|
+
end
|
data/lib/coupler.rb
CHANGED
@@ -21,8 +21,12 @@ require 'json'
|
|
21
21
|
require 'fastercsv'
|
22
22
|
require 'carrierwave/sequel'
|
23
23
|
require 'mongrel'
|
24
|
-
|
25
|
-
|
24
|
+
|
25
|
+
=begin
|
26
|
+
# Sequel will automatically include these as needed
|
27
|
+
require 'jdbc/mysql'
|
28
|
+
require 'jdbc/h2'
|
29
|
+
=end
|
26
30
|
|
27
31
|
module Coupler
|
28
32
|
def self.environment
|
data/lib/coupler/base.rb
CHANGED
data/lib/coupler/extensions.rb
CHANGED
@@ -3,6 +3,8 @@ module Coupler
|
|
3
3
|
end
|
4
4
|
end
|
5
5
|
|
6
|
+
require File.dirname(__FILE__) + "/extensions/exceptions"
|
7
|
+
|
6
8
|
require File.dirname(__FILE__) + "/extensions/connections"
|
7
9
|
require File.dirname(__FILE__) + "/extensions/projects"
|
8
10
|
require File.dirname(__FILE__) + "/extensions/resources"
|
@@ -13,4 +15,4 @@ require File.dirname(__FILE__) + "/extensions/results"
|
|
13
15
|
require File.dirname(__FILE__) + "/extensions/jobs"
|
14
16
|
require File.dirname(__FILE__) + "/extensions/transformers"
|
15
17
|
require File.dirname(__FILE__) + "/extensions/imports"
|
16
|
-
require File.dirname(__FILE__) + "/extensions/
|
18
|
+
require File.dirname(__FILE__) + "/extensions/notifications"
|
@@ -9,19 +9,16 @@ module Coupler
|
|
9
9
|
|
10
10
|
app.post "/projects/:project_id/imports" do
|
11
11
|
@import = Models::Import.new(params[:import].merge(:project_id => @project.id))
|
12
|
-
|
13
|
-
if @import.
|
14
|
-
@
|
15
|
-
|
16
|
-
|
17
|
-
|
18
|
-
|
19
|
-
|
20
|
-
|
21
|
-
end
|
22
|
-
end
|
12
|
+
@resource = Models::Resource.new(:name => @import.name, :project_id => @project.id, :status => 'pending')
|
13
|
+
if @import.valid? && @resource.valid?
|
14
|
+
@import.save
|
15
|
+
@resource.import = @import
|
16
|
+
@resource.save
|
17
|
+
Scheduler.instance.schedule_import_job(@import)
|
18
|
+
redirect("/projects/#{@project.id}/resources")
|
19
|
+
else
|
20
|
+
erb(:'imports/new')
|
23
21
|
end
|
24
|
-
erb(:'imports/new')
|
25
22
|
end
|
26
23
|
|
27
24
|
app.get "/projects/:project_id/imports/:id/edit" do
|
@@ -34,8 +31,8 @@ module Coupler
|
|
34
31
|
@import = Models::Import[:id => params[:id], :project_id => @project.id]
|
35
32
|
raise ImportNotFound unless @import
|
36
33
|
@import.repair_duplicate_keys!(params[:delete])
|
37
|
-
@resource
|
38
|
-
redirect("/projects/#{@project.id}/resources/#{@resource.id}")
|
34
|
+
@import.resource.activate!
|
35
|
+
redirect("/projects/#{@project.id}/resources/#{@import.resource.id}")
|
39
36
|
end
|
40
37
|
end
|
41
38
|
end
|
@@ -0,0 +1,26 @@
|
|
1
|
+
module Coupler
|
2
|
+
module Extensions
|
3
|
+
module Notifications
|
4
|
+
include Models
|
5
|
+
|
6
|
+
def self.registered(app)
|
7
|
+
app.before do
|
8
|
+
Notification.filter(~{:seen => true}, {:url => request.path_info}).update(:seen => true)
|
9
|
+
end
|
10
|
+
|
11
|
+
app.get "/notifications" do
|
12
|
+
@notifications = Notification.order(:created_at).all
|
13
|
+
erb :"notifications/index"
|
14
|
+
end
|
15
|
+
|
16
|
+
app.get "/notifications/unseen.json" do
|
17
|
+
content_type 'application/json'
|
18
|
+
notifications = Notification.filter(~{:seen => true}).order(:created_at).all
|
19
|
+
notifications.collect do |n|
|
20
|
+
{ 'id' => n.id, 'message' => n.message, 'url' => n.url, 'created_at' => n.created_at.iso8601 }
|
21
|
+
end.to_json
|
22
|
+
end
|
23
|
+
end
|
24
|
+
end
|
25
|
+
end
|
26
|
+
end
|
data/lib/coupler/models.rb
CHANGED
@@ -16,6 +16,7 @@ module Coupler
|
|
16
16
|
autoload :Scenario, File.dirname(__FILE__) + "/models/scenario"
|
17
17
|
autoload :Transformation, File.dirname(__FILE__) + "/models/transformation"
|
18
18
|
autoload :Transformer, File.dirname(__FILE__) + "/models/transformer"
|
19
|
+
autoload :Notification, File.dirname(__FILE__) + "/models/notification"
|
19
20
|
end
|
20
21
|
end
|
21
22
|
|
data/lib/coupler/models/field.rb
CHANGED
@@ -1,4 +1,3 @@
|
|
1
|
-
pp caller
|
2
1
|
module Coupler
|
3
2
|
module Models
|
4
3
|
class Field < Sequel::Model
|
@@ -6,13 +5,14 @@ module Coupler
|
|
6
5
|
many_to_one :resource
|
7
6
|
one_to_many :transformations, :key => :source_field_id
|
8
7
|
|
9
|
-
|
10
|
-
|
11
|
-
|
8
|
+
TYPES = {
|
9
|
+
'integer' => {:type => Integer},
|
10
|
+
'string' => {:type => String, :size => 255},
|
11
|
+
'datetime' => {:type => DateTime}
|
12
|
+
}
|
12
13
|
|
13
14
|
def local_column_options
|
14
|
-
{ :name => name, :
|
15
|
-
:primary_key => is_primary_key }
|
15
|
+
{ :name => name, :primary_key => is_primary_key }.merge!(TYPES[final_type])
|
16
16
|
end
|
17
17
|
|
18
18
|
def final_type
|
@@ -1,4 +1,3 @@
|
|
1
|
-
pp caller
|
2
1
|
module Coupler
|
3
2
|
module Models
|
4
3
|
class Import < Sequel::Model
|
@@ -15,6 +14,7 @@ module Coupler
|
|
15
14
|
\d{4}-\d{2}-\d{2}\s\d{2}:\d{2}:\d{2} )\z /x
|
16
15
|
|
17
16
|
many_to_one :project
|
17
|
+
one_to_one :resource
|
18
18
|
plugin :serialization
|
19
19
|
serialize_attributes :marshal, :field_types, :field_names
|
20
20
|
mount_uploader :data, DataUploader
|
@@ -50,7 +50,7 @@ module Coupler
|
|
50
50
|
@preview
|
51
51
|
end
|
52
52
|
|
53
|
-
def import!
|
53
|
+
def import!(&progress)
|
54
54
|
project.local_database do |db|
|
55
55
|
column_info = []
|
56
56
|
column_names = []
|
@@ -79,7 +79,9 @@ module Coupler
|
|
79
79
|
buffer = ImportBuffer.new(column_names, ds)
|
80
80
|
skip = has_headers
|
81
81
|
primary_key_index = field_names.index(primary_key_name)
|
82
|
-
|
82
|
+
io = File.open(data.file.file, 'rb')
|
83
|
+
csv = FasterCSV.new(io)
|
84
|
+
csv.each do |row|
|
83
85
|
if skip
|
84
86
|
# skip header if necessary
|
85
87
|
skip = false
|
@@ -97,6 +99,10 @@ module Coupler
|
|
97
99
|
self.has_duplicate_keys = true if num > 1
|
98
100
|
|
99
101
|
buffer.add(row)
|
102
|
+
|
103
|
+
if block_given?
|
104
|
+
yield io.pos
|
105
|
+
end
|
100
106
|
end
|
101
107
|
buffer.flush
|
102
108
|
|
data/lib/coupler/models/job.rb
CHANGED
@@ -1,4 +1,3 @@
|
|
1
|
-
pp caller
|
2
1
|
module Coupler
|
3
2
|
module Models
|
4
3
|
class Job < Sequel::Model
|
@@ -6,6 +5,8 @@ module Coupler
|
|
6
5
|
|
7
6
|
many_to_one :resource
|
8
7
|
many_to_one :scenario
|
8
|
+
many_to_one :import
|
9
|
+
many_to_one :notification
|
9
10
|
|
10
11
|
def percent_completed
|
11
12
|
total > 0 ? completed * 100 / total : 0
|
@@ -13,31 +14,74 @@ module Coupler
|
|
13
14
|
|
14
15
|
def execute
|
15
16
|
Logger.instance.info("Starting job #{id} (#{name})")
|
17
|
+
|
18
|
+
opts = {}
|
16
19
|
case name
|
17
20
|
when 'transform'
|
18
|
-
|
19
|
-
|
20
|
-
|
21
|
-
|
22
|
-
resource.transform! { |n| update(:completed => completed + n) }
|
23
|
-
new_status = 'done'
|
24
|
-
ensure
|
25
|
-
update(:status => new_status, :completed_at => Time.now)
|
26
|
-
end
|
21
|
+
opts[:total] = resource.source_dataset_count
|
22
|
+
when 'import'
|
23
|
+
opts[:total] = import.data.file.size
|
24
|
+
end
|
27
25
|
|
28
|
-
|
29
|
-
|
26
|
+
begin
|
27
|
+
opts[:status] = 'running'
|
28
|
+
opts[:started_at] = Time.now
|
29
|
+
update(opts)
|
30
|
+
send("execute_#{name}")
|
31
|
+
rescue Exception => e
|
32
|
+
message = "%s: %s\n %s" % [e.class.to_s, e.to_s, e.backtrace.join("\n ")]
|
33
|
+
update({
|
34
|
+
:status => 'failed',
|
35
|
+
:completed_at => Time.now,
|
36
|
+
:error_msg => message
|
37
|
+
})
|
38
|
+
Logger.instance.error("Job #{id} (#{name}) crashed: #{message}")
|
39
|
+
raise e
|
40
|
+
end
|
41
|
+
self.status = 'done'
|
42
|
+
self.completed_at = Time.now
|
43
|
+
save
|
44
|
+
Logger.instance.info("Job #{id} (#{name}) finished successfully")
|
45
|
+
end
|
46
|
+
|
47
|
+
private
|
30
48
|
|
31
|
-
|
32
|
-
|
33
|
-
|
34
|
-
|
35
|
-
|
36
|
-
|
49
|
+
def execute_transform
|
50
|
+
resource.transform! { |n| update(:completed => completed + n) }
|
51
|
+
end
|
52
|
+
|
53
|
+
def execute_run_scenario
|
54
|
+
scenario.run!
|
55
|
+
end
|
56
|
+
|
57
|
+
def execute_import
|
58
|
+
last = Time.now # don't slam the database
|
59
|
+
result = import.import! do |pos|
|
60
|
+
now = Time.now
|
61
|
+
if now - last >= 1
|
62
|
+
last = now
|
63
|
+
update(:completed => pos)
|
64
|
+
end
|
65
|
+
end
|
66
|
+
if result
|
67
|
+
# NOTE: This is a bug waiting to happen. Import doesn't verify
|
68
|
+
# that it has a resource, but supposedly, it will always have
|
69
|
+
# one. The resource gets created at the same time in the
|
70
|
+
# controller.
|
71
|
+
|
72
|
+
resource = import.resource
|
73
|
+
resource.activate!
|
74
|
+
self.notification = Notification.create({
|
75
|
+
:message => "Import finished successfully",
|
76
|
+
:url => "/projects/#{import.project_id}/resources/#{resource.id}"
|
77
|
+
})
|
78
|
+
else
|
79
|
+
self.notification = Notification.create({
|
80
|
+
:message => "Import finished, but with errors",
|
81
|
+
:url => "/projects/#{import.project_id}/imports/#{import.id}/edit"
|
82
|
+
})
|
37
83
|
end
|
38
84
|
end
|
39
|
-
Logger.instance.info("Job #{id} (#{name}) finished")
|
40
|
-
end
|
41
85
|
end
|
42
86
|
end
|
43
87
|
end
|
@@ -1,4 +1,3 @@
|
|
1
|
-
pp caller
|
2
1
|
module Coupler
|
3
2
|
module Models
|
4
3
|
class Resource < Sequel::Model
|
@@ -30,8 +29,6 @@ module Coupler
|
|
30
29
|
def import=(*args)
|
31
30
|
result = super
|
32
31
|
if new?
|
33
|
-
self.project = import.project
|
34
|
-
self.name = import.name
|
35
32
|
self.table_name = "import_#{import.id}"
|
36
33
|
end
|
37
34
|
result
|
@@ -106,21 +103,20 @@ module Coupler
|
|
106
103
|
end
|
107
104
|
end
|
108
105
|
|
109
|
-
def status
|
110
|
-
if transformed_with.to_s != transformation_ids.join(",") || transformations_dataset.filter("updated_at > ?", transformed_at).count > 0
|
111
|
-
"out_of_date"
|
112
|
-
else
|
113
|
-
"ok"
|
114
|
-
end
|
115
|
-
end
|
116
|
-
|
117
106
|
def scenarios
|
118
107
|
Scenario.filter(["resource_1_id = ? OR resource_2_id = ?", id, id]).all
|
119
108
|
end
|
120
109
|
|
121
|
-
def
|
110
|
+
def transformations_updated!
|
111
|
+
last_updated_at = transformed_at
|
112
|
+
transformation_ids = []
|
113
|
+
|
122
114
|
fields_dataset.update(:local_db_type => nil, :local_type => nil)
|
123
115
|
transformations_dataset.order(:position).each do |transformation|
|
116
|
+
transformation_ids << transformation.id
|
117
|
+
if last_updated_at.nil? || transformation.updated_at > last_updated_at
|
118
|
+
last_updated_at = transformation.updated_at
|
119
|
+
end
|
124
120
|
if transformation.source_field_id == transformation.result_field_id
|
125
121
|
source_field = transformation.source_field
|
126
122
|
changes = transformation.field_changes[source_field.id]
|
@@ -130,6 +126,12 @@ module Coupler
|
|
130
126
|
})
|
131
127
|
end
|
132
128
|
end
|
129
|
+
|
130
|
+
if transformed_with.to_s != transformation_ids.join(",") || (last_updated_at && last_updated_at > transformed_at)
|
131
|
+
update(:status => "out_of_date")
|
132
|
+
else
|
133
|
+
update(:status => "ok")
|
134
|
+
end
|
133
135
|
end
|
134
136
|
|
135
137
|
def transform!(&progress)
|
@@ -137,6 +139,7 @@ module Coupler
|
|
137
139
|
create_local_table!
|
138
140
|
_transform(&progress)
|
139
141
|
self.update({
|
142
|
+
:status => 'ok',
|
140
143
|
:transformed_at => Time.now,
|
141
144
|
:transformed_with => t_ids
|
142
145
|
})
|
@@ -161,6 +164,13 @@ module Coupler
|
|
161
164
|
primary_key_name.to_sym
|
162
165
|
end
|
163
166
|
|
167
|
+
# Activate resource that was pending until import was completed
|
168
|
+
def activate!
|
169
|
+
set_primary_key
|
170
|
+
create_fields
|
171
|
+
update(:status => "ok")
|
172
|
+
end
|
173
|
+
|
164
174
|
private
|
165
175
|
def transformation_ids
|
166
176
|
transformations_dataset.select(:id).order(:id).all.collect(&:id)
|
@@ -170,6 +180,15 @@ module Coupler
|
|
170
180
|
Coupler.connection_string("project_#{project.id}")
|
171
181
|
end
|
172
182
|
|
183
|
+
def set_primary_key
|
184
|
+
source_database do |db|
|
185
|
+
schema = db.schema(table_name.to_sym)
|
186
|
+
info = schema.detect { |x| x[1][:primary_key] }
|
187
|
+
self.primary_key_name = info[0].to_s
|
188
|
+
self.primary_key_type = info[1][:type].to_s
|
189
|
+
end
|
190
|
+
end
|
191
|
+
|
173
192
|
def create_fields
|
174
193
|
source_schema.each do |(name, info)|
|
175
194
|
add_field({
|
@@ -233,19 +252,21 @@ module Coupler
|
|
233
252
|
validates_presence [:project_id, :name]
|
234
253
|
validates_presence :slug
|
235
254
|
validates_unique [:name, :project_id], [:slug, :project_id]
|
236
|
-
|
237
|
-
|
238
|
-
|
239
|
-
|
240
|
-
|
241
|
-
|
242
|
-
|
243
|
-
|
244
|
-
|
245
|
-
|
246
|
-
|
247
|
-
|
248
|
-
|
255
|
+
|
256
|
+
if status != 'pending'
|
257
|
+
validates_presence [:table_name]
|
258
|
+
if errors.on(:table_name).nil?
|
259
|
+
source_database do |db|
|
260
|
+
sym = self.table_name.to_sym
|
261
|
+
if !db.tables.include?(sym)
|
262
|
+
errors.add(:table_name, "is invalid")
|
263
|
+
else
|
264
|
+
keys = db.schema(sym).select { |info| info[1][:primary_key] }
|
265
|
+
if keys.empty?
|
266
|
+
errors.add(:table_name, "doesn't have a primary key")
|
267
|
+
elsif keys.length > 1
|
268
|
+
errors.add(:table_name, "has too many primary keys")
|
269
|
+
end
|
249
270
|
end
|
250
271
|
end
|
251
272
|
end
|
@@ -257,11 +278,9 @@ module Coupler
|
|
257
278
|
# NOTE: I'm doing this instead of using before_create because
|
258
279
|
# serialization happens in before_save, which gets called before
|
259
280
|
# the before_create hook
|
260
|
-
|
261
|
-
|
262
|
-
|
263
|
-
self.primary_key_name = info[0].to_s
|
264
|
-
self.primary_key_type = info[1][:type].to_s
|
281
|
+
if status != "pending"
|
282
|
+
set_primary_key
|
283
|
+
self.status = "ok"
|
265
284
|
end
|
266
285
|
end
|
267
286
|
super
|
@@ -269,7 +288,9 @@ module Coupler
|
|
269
288
|
|
270
289
|
def after_create
|
271
290
|
super
|
272
|
-
|
291
|
+
if status != "pending"
|
292
|
+
create_fields
|
293
|
+
end
|
273
294
|
end
|
274
295
|
|
275
296
|
def after_destroy
|