coupler 0.0.1-java
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.
- data/.document +5 -0
- data/.gitmodules +3 -0
- data/.rvmrc +1 -0
- data/.vimrc +40 -0
- data/Gemfile +27 -0
- data/Gemfile.lock +71 -0
- data/LICENSE +20 -0
- data/NOTES +6 -0
- data/README.rdoc +18 -0
- data/Rakefile +42 -0
- data/TODO +11 -0
- data/VERSION +1 -0
- data/bin/coupler +7 -0
- data/db/.gitignore +6 -0
- data/db/migrate/001_initial_schema.rb +166 -0
- data/db/migrate/002_stub.rb +4 -0
- data/db/migrate/003_stub.rb +4 -0
- data/db/migrate/004_create_comparisons.rb +28 -0
- data/db/migrate/005_move_database_name.rb +19 -0
- data/db/migrate/006_upgrade_comparisons.rb +34 -0
- data/db/migrate/007_add_which_to_comparisons.rb +23 -0
- data/db/migrate/008_add_result_field_to_transformations.rb +33 -0
- data/db/migrate/009_add_generated_flag_to_fields.rb +13 -0
- data/db/migrate/010_create_imports.rb +24 -0
- data/db/migrate/011_add_primary_key_type.rb +13 -0
- data/db/migrate/012_add_transformed_with_to_resources.rb +13 -0
- data/db/migrate/013_add_run_count_to_scenarios.rb +13 -0
- data/db/migrate/014_add_last_accessed_at_to_some_tables.rb +13 -0
- data/db/migrate/015_add_run_number_to_results.rb +15 -0
- data/db/migrate/016_fix_scenario_run_count.rb +27 -0
- data/db/migrate/017_rename_comparison_columns.rb +14 -0
- data/db/migrate/018_fix_scenario_linkage_type.rb +8 -0
- data/db/migrate/019_add_columns_to_imports.rb +24 -0
- data/db/migrate/020_rename_import_columns.rb +12 -0
- data/db/migrate/021_add_fields_to_connections.rb +15 -0
- data/db/migrate/022_remove_database_name_from_resources.rb +11 -0
- data/features/connections.feature +28 -0
- data/features/matchers.feature +35 -0
- data/features/projects.feature +11 -0
- data/features/resources.feature +62 -0
- data/features/scenarios.feature +45 -0
- data/features/step_definitions/coupler_steps.rb +145 -0
- data/features/step_definitions/matchers_steps.rb +26 -0
- data/features/step_definitions/resources_steps.rb +12 -0
- data/features/step_definitions/scenarios_steps.rb +7 -0
- data/features/step_definitions/transformations_steps.rb +3 -0
- data/features/support/env.rb +128 -0
- data/features/transformations.feature +22 -0
- data/features/wizard.feature +10 -0
- data/gfx/coupler-header.svg +213 -0
- data/gfx/coupler-sidebar.svg +656 -0
- data/gfx/coupler.svg +184 -0
- data/gfx/icon.svg +75 -0
- data/lib/coupler/base.rb +63 -0
- data/lib/coupler/config.rb +128 -0
- data/lib/coupler/data_uploader.rb +20 -0
- data/lib/coupler/database.rb +31 -0
- data/lib/coupler/extensions/connections.rb +57 -0
- data/lib/coupler/extensions/exceptions.rb +58 -0
- data/lib/coupler/extensions/imports.rb +43 -0
- data/lib/coupler/extensions/jobs.rb +21 -0
- data/lib/coupler/extensions/matchers.rb +64 -0
- data/lib/coupler/extensions/projects.rb +62 -0
- data/lib/coupler/extensions/resources.rb +89 -0
- data/lib/coupler/extensions/results.rb +100 -0
- data/lib/coupler/extensions/scenarios.rb +50 -0
- data/lib/coupler/extensions/transformations.rb +70 -0
- data/lib/coupler/extensions/transformers.rb +58 -0
- data/lib/coupler/extensions.rb +16 -0
- data/lib/coupler/helpers.rb +121 -0
- data/lib/coupler/import_buffer.rb +48 -0
- data/lib/coupler/logger.rb +16 -0
- data/lib/coupler/models/common_model.rb +104 -0
- data/lib/coupler/models/comparison.rb +166 -0
- data/lib/coupler/models/connection.rb +59 -0
- data/lib/coupler/models/field.rb +55 -0
- data/lib/coupler/models/import.rb +238 -0
- data/lib/coupler/models/job.rb +42 -0
- data/lib/coupler/models/jobify.rb +17 -0
- data/lib/coupler/models/matcher.rb +36 -0
- data/lib/coupler/models/project.rb +40 -0
- data/lib/coupler/models/resource.rb +287 -0
- data/lib/coupler/models/result.rb +92 -0
- data/lib/coupler/models/scenario/runner.rb +357 -0
- data/lib/coupler/models/scenario.rb +115 -0
- data/lib/coupler/models/transformation.rb +117 -0
- data/lib/coupler/models/transformer/runner.rb +28 -0
- data/lib/coupler/models/transformer.rb +110 -0
- data/lib/coupler/models.rb +30 -0
- data/lib/coupler/runner.rb +76 -0
- data/lib/coupler/scheduler.rb +56 -0
- data/lib/coupler.rb +34 -0
- data/log/.gitignore +1 -0
- data/misc/README +5 -0
- data/misc/jruby-json.license +57 -0
- data/misc/rack-flash.license +22 -0
- data/script/dbconsole.rb +5 -0
- data/src/edu/vanderbilt/coupler/Main.java +116 -0
- data/src/edu/vanderbilt/coupler/jruby.properties +1 -0
- data/tasks/annotations.rake +84 -0
- data/tasks/db.rake +120 -0
- data/tasks/environment.rake +12 -0
- data/tasks/jeweler.rake +43 -0
- data/tasks/package.rake +58 -0
- data/tasks/rdoc.rake +13 -0
- data/tasks/test.rake +63 -0
- data/tasks/vendor.rake +43 -0
- data/test/README.txt +6 -0
- data/test/config.yml +9 -0
- data/test/coupler/models/test_import.rb +221 -0
- data/test/factories.rb +91 -0
- data/test/fixtures/duplicate-keys.csv +5 -0
- data/test/fixtures/no-headers.csv +50 -0
- data/test/fixtures/people.csv +51 -0
- data/test/fixtures/varying-row-size.csv +4 -0
- data/test/helper.rb +156 -0
- data/test/integration/extensions/test_connections.rb +80 -0
- data/test/integration/extensions/test_imports.rb +94 -0
- data/test/integration/extensions/test_jobs.rb +52 -0
- data/test/integration/extensions/test_matchers.rb +134 -0
- data/test/integration/extensions/test_projects.rb +82 -0
- data/test/integration/extensions/test_resources.rb +150 -0
- data/test/integration/extensions/test_results.rb +89 -0
- data/test/integration/extensions/test_scenarios.rb +88 -0
- data/test/integration/extensions/test_transformations.rb +113 -0
- data/test/integration/extensions/test_transformers.rb +80 -0
- data/test/integration/test_field.rb +45 -0
- data/test/integration/test_import.rb +78 -0
- data/test/integration/test_running_scenarios.rb +379 -0
- data/test/integration/test_transformation.rb +56 -0
- data/test/integration/test_transforming.rb +154 -0
- data/test/table_sets.rb +76 -0
- data/test/unit/models/test_common_model.rb +130 -0
- data/test/unit/models/test_comparison.rb +619 -0
- data/test/unit/models/test_connection.rb +115 -0
- data/test/unit/models/test_field.rb +99 -0
- data/test/unit/models/test_import.rb +130 -0
- data/test/unit/models/test_job.rb +115 -0
- data/test/unit/models/test_matcher.rb +82 -0
- data/test/unit/models/test_project.rb +102 -0
- data/test/unit/models/test_resource.rb +564 -0
- data/test/unit/models/test_result.rb +90 -0
- data/test/unit/models/test_scenario.rb +199 -0
- data/test/unit/models/test_transformation.rb +193 -0
- data/test/unit/models/test_transformer.rb +188 -0
- data/test/unit/test_base.rb +60 -0
- data/test/unit/test_data_uploader.rb +27 -0
- data/test/unit/test_database.rb +23 -0
- data/test/unit/test_helpers.rb +58 -0
- data/test/unit/test_logger.rb +10 -0
- data/test/unit/test_models.rb +12 -0
- data/test/unit/test_runner.rb +76 -0
- data/test/unit/test_scheduler.rb +66 -0
- data/uploads/.gitignore +2 -0
- data/vendor/java/.gitignore +5 -0
- data/webroot/public/css/960.css +1 -0
- data/webroot/public/css/dataTables.css +1057 -0
- data/webroot/public/css/jquery-ui.css +572 -0
- data/webroot/public/css/jquery.treeview.css +68 -0
- data/webroot/public/css/reset.css +1 -0
- data/webroot/public/css/style.css +504 -0
- data/webroot/public/css/text.css +1 -0
- data/webroot/public/favicon.ico +0 -0
- data/webroot/public/images/12_col.gif +0 -0
- data/webroot/public/images/16_col.gif +0 -0
- data/webroot/public/images/add.png +0 -0
- data/webroot/public/images/ajax-loader.gif +0 -0
- data/webroot/public/images/cog.png +0 -0
- data/webroot/public/images/coupler.png +0 -0
- data/webroot/public/images/foo.png +0 -0
- data/webroot/public/images/hammer.png +0 -0
- data/webroot/public/images/header.png +0 -0
- data/webroot/public/images/home.gif +0 -0
- data/webroot/public/images/jobs.gif +0 -0
- data/webroot/public/images/sidebar-bottom.png +0 -0
- data/webroot/public/images/sidebar.png +0 -0
- data/webroot/public/images/treeview-default-line.gif +0 -0
- data/webroot/public/images/treeview-default.gif +0 -0
- data/webroot/public/images/ui-anim_basic_16x16.gif +0 -0
- data/webroot/public/images/ui-bg_flat_0_aaaaaa_40x100.png +0 -0
- data/webroot/public/images/ui-bg_flat_75_ffffff_40x100.png +0 -0
- data/webroot/public/images/ui-bg_glass_55_fbf9ee_1x400.png +0 -0
- data/webroot/public/images/ui-bg_glass_65_ffffff_1x400.png +0 -0
- data/webroot/public/images/ui-bg_glass_75_dadada_1x400.png +0 -0
- data/webroot/public/images/ui-bg_glass_75_e6e6e6_1x400.png +0 -0
- data/webroot/public/images/ui-bg_glass_95_fef1ec_1x400.png +0 -0
- data/webroot/public/images/ui-bg_highlight-hard_30_565356_1x100.png +0 -0
- data/webroot/public/images/ui-bg_highlight-hard_75_888588_1x100.png +0 -0
- data/webroot/public/images/ui-bg_highlight-soft_30_6e3b3a_1x100.png +0 -0
- data/webroot/public/images/ui-bg_highlight-soft_35_8e8b8e_1x100.png +0 -0
- data/webroot/public/images/ui-bg_highlight-soft_75_cccccc_1x100.png +0 -0
- data/webroot/public/images/ui-icons_222222_256x240.png +0 -0
- data/webroot/public/images/ui-icons_2e83ff_256x240.png +0 -0
- data/webroot/public/images/ui-icons_454545_256x240.png +0 -0
- data/webroot/public/images/ui-icons_888888_256x240.png +0 -0
- data/webroot/public/images/ui-icons_cd0a0a_256x240.png +0 -0
- data/webroot/public/images/ui-icons_ffffff_256x240.png +0 -0
- data/webroot/public/js/ajaxupload.js +673 -0
- data/webroot/public/js/application.js +40 -0
- data/webroot/public/js/jquery-ui.combobox.js +98 -0
- data/webroot/public/js/jquery-ui.js +9867 -0
- data/webroot/public/js/jquery-ui.min.js +559 -0
- data/webroot/public/js/jquery.dataTables.min.js +587 -0
- data/webroot/public/js/jquery.min.js +154 -0
- data/webroot/public/js/jquery.timeago.js +140 -0
- data/webroot/public/js/jquery.tooltip.min.js +19 -0
- data/webroot/public/js/jquery.treeview.min.js +15 -0
- data/webroot/public/js/resource.js +11 -0
- data/webroot/public/js/results.js +56 -0
- data/webroot/public/js/transformations.js +95 -0
- data/webroot/views/connections/index.erb +5 -0
- data/webroot/views/connections/list.erb +34 -0
- data/webroot/views/connections/new.erb +55 -0
- data/webroot/views/connections/show.erb +36 -0
- data/webroot/views/imports/edit.erb +60 -0
- data/webroot/views/imports/form.erb +81 -0
- data/webroot/views/imports/new.erb +89 -0
- data/webroot/views/index.erb +12 -0
- data/webroot/views/jobs/index.erb +7 -0
- data/webroot/views/jobs/list.erb +24 -0
- data/webroot/views/layout.erb +38 -0
- data/webroot/views/matchers/form.erb +250 -0
- data/webroot/views/matchers/list.erb +32 -0
- data/webroot/views/projects/form.erb +14 -0
- data/webroot/views/projects/index.erb +96 -0
- data/webroot/views/projects/show.erb +24 -0
- data/webroot/views/resources/edit.erb +88 -0
- data/webroot/views/resources/index.erb +5 -0
- data/webroot/views/resources/list.erb +27 -0
- data/webroot/views/resources/new.erb +121 -0
- data/webroot/views/resources/show.erb +86 -0
- data/webroot/views/resources/transform.erb +2 -0
- data/webroot/views/results/csv.erb +12 -0
- data/webroot/views/results/details.erb +15 -0
- data/webroot/views/results/index.erb +2 -0
- data/webroot/views/results/list.erb +22 -0
- data/webroot/views/results/record.erb +24 -0
- data/webroot/views/results/show.erb +68 -0
- data/webroot/views/scenarios/index.erb +5 -0
- data/webroot/views/scenarios/list.erb +20 -0
- data/webroot/views/scenarios/new.erb +99 -0
- data/webroot/views/scenarios/run.erb +2 -0
- data/webroot/views/scenarios/show.erb +50 -0
- data/webroot/views/sidebar.erb +106 -0
- data/webroot/views/transformations/create.erb +115 -0
- data/webroot/views/transformations/for.erb +16 -0
- data/webroot/views/transformations/index.erb +2 -0
- data/webroot/views/transformations/list.erb +29 -0
- data/webroot/views/transformations/new.erb +126 -0
- data/webroot/views/transformations/preview.erb +46 -0
- data/webroot/views/transformers/edit.erb +6 -0
- data/webroot/views/transformers/form.erb +58 -0
- data/webroot/views/transformers/index.erb +2 -0
- data/webroot/views/transformers/list.erb +25 -0
- data/webroot/views/transformers/new.erb +5 -0
- data/webroot/views/transformers/preview.erb +23 -0
- data/webroot/views/transformers/show.erb +0 -0
- metadata +558 -0
|
@@ -0,0 +1,110 @@
|
|
|
1
|
+
module Coupler
|
|
2
|
+
module Models
|
|
3
|
+
class Transformer < Sequel::Model
|
|
4
|
+
include CommonModel
|
|
5
|
+
|
|
6
|
+
plugin :serialization, :marshal, :allowed_types
|
|
7
|
+
|
|
8
|
+
TYPES = %w{string integer datetime}
|
|
9
|
+
EXAMPLES = {
|
|
10
|
+
'string' => 'Test',
|
|
11
|
+
'integer' => 123,
|
|
12
|
+
'datetime' => lambda { Time.now }
|
|
13
|
+
}
|
|
14
|
+
|
|
15
|
+
def accepts_type?(type)
|
|
16
|
+
allowed_types.is_a?(Array) && allowed_types.include?(type)
|
|
17
|
+
end
|
|
18
|
+
|
|
19
|
+
def transform(data, options)
|
|
20
|
+
input = data[options[:in]]
|
|
21
|
+
runner = Runner.new(code, input)
|
|
22
|
+
output = runner.run
|
|
23
|
+
data[options[:out]] = output
|
|
24
|
+
data
|
|
25
|
+
end
|
|
26
|
+
|
|
27
|
+
def preview
|
|
28
|
+
return nil if allowed_types.nil? || code.nil? || code == ""
|
|
29
|
+
|
|
30
|
+
result = {'success' => true}
|
|
31
|
+
examples = EXAMPLES.reject { |k, v| !allowed_types.include?(k) }
|
|
32
|
+
examples.each_pair do |type, obj|
|
|
33
|
+
obj = obj.call if obj.is_a?(Proc)
|
|
34
|
+
result[type] = { :in => obj }
|
|
35
|
+
begin
|
|
36
|
+
transform(result[type], {:in => :in, :out => :out})
|
|
37
|
+
|
|
38
|
+
expected_type = result_type == 'same' ? type : result_type
|
|
39
|
+
actual_type = case result[type][:out]
|
|
40
|
+
when String then "string"
|
|
41
|
+
when Fixnum then "integer"
|
|
42
|
+
when Time, Date, DateTime then "datetime"
|
|
43
|
+
when NilClass then "null"
|
|
44
|
+
end
|
|
45
|
+
|
|
46
|
+
if actual_type != "null" && expected_type != actual_type
|
|
47
|
+
raise TypeError, "expected #{expected_type}, got #{actual_type}"
|
|
48
|
+
end
|
|
49
|
+
|
|
50
|
+
rescue Exception => e
|
|
51
|
+
result[type][:out] = e
|
|
52
|
+
result['success'] = false
|
|
53
|
+
end
|
|
54
|
+
end
|
|
55
|
+
result
|
|
56
|
+
end
|
|
57
|
+
|
|
58
|
+
def field_changes(*fields)
|
|
59
|
+
fields.inject({}) do |result, field|
|
|
60
|
+
result[field.id] = hash = {}
|
|
61
|
+
if result_type != 'same'
|
|
62
|
+
hash[:type] = result_type.to_sym
|
|
63
|
+
|
|
64
|
+
# FIXME: don't use db_type anymore
|
|
65
|
+
hash[:db_type] =
|
|
66
|
+
case result_type
|
|
67
|
+
when 'integer' then 'int(11)'
|
|
68
|
+
when 'string' then 'varchar(255)'
|
|
69
|
+
when 'datetime' then 'datetime'
|
|
70
|
+
end
|
|
71
|
+
end
|
|
72
|
+
result
|
|
73
|
+
end
|
|
74
|
+
end
|
|
75
|
+
|
|
76
|
+
private
|
|
77
|
+
def validate
|
|
78
|
+
super
|
|
79
|
+
validates_presence [:name, :allowed_types, :result_type, :code]
|
|
80
|
+
validates_unique :name
|
|
81
|
+
validates_includes TYPES + ['same'], :result_type
|
|
82
|
+
|
|
83
|
+
if errors.on(:allowed_types).nil?
|
|
84
|
+
bad = (allowed_types - TYPES).uniq
|
|
85
|
+
if !bad.empty?
|
|
86
|
+
errors.add(:allowed_types, "has invalid type(s): #{bad.join(', ')}")
|
|
87
|
+
end
|
|
88
|
+
end
|
|
89
|
+
|
|
90
|
+
if errors.on(:code).nil?
|
|
91
|
+
io = java.io.ByteArrayInputStream.new(code.to_java_bytes)
|
|
92
|
+
begin
|
|
93
|
+
JRuby.runtime.parseInline(io, "line", nil)
|
|
94
|
+
rescue Exception => e
|
|
95
|
+
errors.add(:code, "has errors: #{e.to_s}")
|
|
96
|
+
end
|
|
97
|
+
end
|
|
98
|
+
|
|
99
|
+
if errors.empty?
|
|
100
|
+
result = preview
|
|
101
|
+
if !(result && result['success'])
|
|
102
|
+
errors.add(:code, "has errors")
|
|
103
|
+
end
|
|
104
|
+
end
|
|
105
|
+
end
|
|
106
|
+
end
|
|
107
|
+
end
|
|
108
|
+
end
|
|
109
|
+
|
|
110
|
+
require File.join(File.dirname(__FILE__), "transformer", "runner")
|
|
@@ -0,0 +1,30 @@
|
|
|
1
|
+
module Coupler
|
|
2
|
+
module Models
|
|
3
|
+
# NOTE: using autoload here would undoubtedly be more efficient, but
|
|
4
|
+
# I need to make sure the database connection is instantiated before
|
|
5
|
+
# loading these classes because of how Sequel::Model works.
|
|
6
|
+
#%w{connection project resource field transformer transformation scenario matcher job result comparison}.each do |name|
|
|
7
|
+
#autoload(name.capitalize.to_sym, File.dirname(__FILE__) + "/models/#{name}")
|
|
8
|
+
#end
|
|
9
|
+
|
|
10
|
+
NAMES = [
|
|
11
|
+
:Connection, :Project, :Resource, :Field, :Transformer,
|
|
12
|
+
:Transformation, :Scenario, :Matcher, :Job, :Result, :Comparison,
|
|
13
|
+
:Import
|
|
14
|
+
]
|
|
15
|
+
def self.const_missing(name)
|
|
16
|
+
name = name.to_sym
|
|
17
|
+
if NAMES.include?(name)
|
|
18
|
+
Database.instance
|
|
19
|
+
require File.dirname(__FILE__) + "/models/#{name.to_s.downcase}"
|
|
20
|
+
const_get(name)
|
|
21
|
+
else
|
|
22
|
+
puts "#{name.inspect} wasn't in #{NAMES.inspect}"
|
|
23
|
+
super
|
|
24
|
+
end
|
|
25
|
+
end
|
|
26
|
+
end
|
|
27
|
+
end
|
|
28
|
+
|
|
29
|
+
require File.dirname(__FILE__) + "/models/common_model"
|
|
30
|
+
require File.dirname(__FILE__) + "/models/jobify"
|
|
@@ -0,0 +1,76 @@
|
|
|
1
|
+
module Coupler
|
|
2
|
+
class Runner
|
|
3
|
+
def initialize(argv = ARGV)
|
|
4
|
+
irb = false
|
|
5
|
+
OptionParser.new do |opts|
|
|
6
|
+
opts.on("-p", "--port PORT", "Web server port") do |port|
|
|
7
|
+
Base.set(:port, port.to_i)
|
|
8
|
+
end
|
|
9
|
+
opts.on("--dir DIR", "Directory to use for Coupler's data") do |dir|
|
|
10
|
+
Base.set(:data_path, dir)
|
|
11
|
+
end
|
|
12
|
+
opts.on("-e", "--environment ENVIRONMENT", "Set the environment") do |env|
|
|
13
|
+
case env
|
|
14
|
+
when "production", "development", "test"
|
|
15
|
+
Base.set(:environment, env.to_sym)
|
|
16
|
+
else
|
|
17
|
+
raise "Invalid environment (must be production, development, or test)"
|
|
18
|
+
end
|
|
19
|
+
end
|
|
20
|
+
opts.on('-i', '--interactive', "Run an IRB session") do
|
|
21
|
+
irb = true
|
|
22
|
+
end
|
|
23
|
+
end.parse!(argv)
|
|
24
|
+
|
|
25
|
+
puts "Migrating database..."
|
|
26
|
+
Coupler::Database.instance.migrate!
|
|
27
|
+
|
|
28
|
+
puts "Starting scheduler..."
|
|
29
|
+
Coupler::Scheduler.instance.start
|
|
30
|
+
|
|
31
|
+
puts "Starting web server..."
|
|
32
|
+
handler = Rack::Handler.get('mongrel')
|
|
33
|
+
settings = Coupler::Base.settings
|
|
34
|
+
|
|
35
|
+
# See the Rack::Handler::Mongrel.run! method
|
|
36
|
+
# NOTE: I don't want to join the server immediately, which is why I'm
|
|
37
|
+
# doing this by hand.
|
|
38
|
+
@web_server = Mongrel::HttpServer.new(settings.bind, settings.port, 950, 0, 60)
|
|
39
|
+
@web_server.register('/', handler.new(Coupler::Base))
|
|
40
|
+
success = false
|
|
41
|
+
begin
|
|
42
|
+
@web_thread = @web_server.run
|
|
43
|
+
success = true
|
|
44
|
+
rescue Errno::EADDRINUSE => e
|
|
45
|
+
Scheduler.instance.shutdown
|
|
46
|
+
puts "Can't start web server, port already in use. Aborting..."
|
|
47
|
+
end
|
|
48
|
+
|
|
49
|
+
if success
|
|
50
|
+
Coupler::Base.set(:running, true)
|
|
51
|
+
trap("INT") do
|
|
52
|
+
shutdown
|
|
53
|
+
end
|
|
54
|
+
|
|
55
|
+
puts <<'EOF'
|
|
56
|
+
___
|
|
57
|
+
/\_ \
|
|
58
|
+
___ ___ __ __ _____\//\ \ __ _ __
|
|
59
|
+
/'___\ / __`\/\ \/\ \/\ '__`\\ \ \ /'__`\/\`'__\
|
|
60
|
+
/\ \__//\ \L\ \ \ \_\ \ \ \L\ \\_\ \_/\ __/\ \ \/
|
|
61
|
+
\ \____\ \____/\ \____/\ \ ,__//\____\ \____\\ \_\
|
|
62
|
+
\/____/\/___/ \/___/ \ \ \/ \/____/\/____/ \/_/
|
|
63
|
+
\ \_\
|
|
64
|
+
\/_/
|
|
65
|
+
EOF
|
|
66
|
+
@web_thread.join
|
|
67
|
+
end
|
|
68
|
+
end
|
|
69
|
+
|
|
70
|
+
def shutdown
|
|
71
|
+
puts "Shutting down..."
|
|
72
|
+
Scheduler.instance.shutdown
|
|
73
|
+
@web_server.stop
|
|
74
|
+
end
|
|
75
|
+
end
|
|
76
|
+
end
|
|
@@ -0,0 +1,56 @@
|
|
|
1
|
+
module Coupler
|
|
2
|
+
class Scheduler
|
|
3
|
+
include Singleton
|
|
4
|
+
|
|
5
|
+
def initialize
|
|
6
|
+
super
|
|
7
|
+
@mutex = Mutex.new
|
|
8
|
+
end
|
|
9
|
+
|
|
10
|
+
def schedule_transform_job(resource)
|
|
11
|
+
Models::Job.create({
|
|
12
|
+
:name => "transform",
|
|
13
|
+
:resource => resource,
|
|
14
|
+
:status => "scheduled"
|
|
15
|
+
})
|
|
16
|
+
end
|
|
17
|
+
|
|
18
|
+
def schedule_run_scenario_job(scenario)
|
|
19
|
+
Models::Job.create({
|
|
20
|
+
:name => "run_scenario",
|
|
21
|
+
:scenario => scenario,
|
|
22
|
+
:status => "scheduled"
|
|
23
|
+
})
|
|
24
|
+
end
|
|
25
|
+
|
|
26
|
+
def run_jobs
|
|
27
|
+
@mutex.synchronize do
|
|
28
|
+
count = Models::Job.filter(:status => 'running').count
|
|
29
|
+
if count == 0
|
|
30
|
+
job = Models::Job.filter(:status => 'scheduled').order(:created_at).first
|
|
31
|
+
Thread.new(job) { |j| j.execute } if job
|
|
32
|
+
end
|
|
33
|
+
end
|
|
34
|
+
end
|
|
35
|
+
|
|
36
|
+
def start
|
|
37
|
+
if !is_started?
|
|
38
|
+
@loop = Thread.new do
|
|
39
|
+
loop do
|
|
40
|
+
sleep 30
|
|
41
|
+
run_jobs
|
|
42
|
+
end
|
|
43
|
+
end
|
|
44
|
+
end
|
|
45
|
+
end
|
|
46
|
+
|
|
47
|
+
def shutdown
|
|
48
|
+
@loop.exit
|
|
49
|
+
@loop = nil
|
|
50
|
+
end
|
|
51
|
+
|
|
52
|
+
def is_started?
|
|
53
|
+
!@loop.nil?
|
|
54
|
+
end
|
|
55
|
+
end
|
|
56
|
+
end
|
data/lib/coupler.rb
ADDED
|
@@ -0,0 +1,34 @@
|
|
|
1
|
+
require 'java'
|
|
2
|
+
require 'jruby/core_ext'
|
|
3
|
+
|
|
4
|
+
require 'fileutils'
|
|
5
|
+
require 'erb'
|
|
6
|
+
require 'delegate'
|
|
7
|
+
require 'singleton'
|
|
8
|
+
require 'logger'
|
|
9
|
+
require 'optparse'
|
|
10
|
+
require 'thwait'
|
|
11
|
+
require 'digest'
|
|
12
|
+
require 'rack'
|
|
13
|
+
require 'rack/mime' # This is an attempt to avoid NameError exceptions
|
|
14
|
+
require 'sinatra/base'
|
|
15
|
+
require 'rack/flash'
|
|
16
|
+
require 'sequel'
|
|
17
|
+
require 'sequel/extensions/migration'
|
|
18
|
+
require 'json'
|
|
19
|
+
require 'fastercsv'
|
|
20
|
+
require 'carrierwave'
|
|
21
|
+
require 'mvn:com.h2database:h2'
|
|
22
|
+
require 'mongrel'
|
|
23
|
+
require 'jdbc/mysql' # FIXME: lazy load this
|
|
24
|
+
|
|
25
|
+
require File.dirname(__FILE__) + "/coupler/logger"
|
|
26
|
+
require File.dirname(__FILE__) + "/coupler/database"
|
|
27
|
+
require File.dirname(__FILE__) + "/coupler/scheduler"
|
|
28
|
+
require File.dirname(__FILE__) + "/coupler/data_uploader"
|
|
29
|
+
require File.dirname(__FILE__) + "/coupler/import_buffer"
|
|
30
|
+
require File.dirname(__FILE__) + "/coupler/models"
|
|
31
|
+
require File.dirname(__FILE__) + "/coupler/extensions"
|
|
32
|
+
require File.dirname(__FILE__) + "/coupler/helpers"
|
|
33
|
+
require File.dirname(__FILE__) + "/coupler/runner"
|
|
34
|
+
require File.dirname(__FILE__) + "/coupler/base"
|
data/log/.gitignore
ADDED
|
@@ -0,0 +1 @@
|
|
|
1
|
+
*.log
|
data/misc/README
ADDED
|
@@ -0,0 +1,57 @@
|
|
|
1
|
+
JSON-JRuby is copyrighted free software by Daniel Luz <mernen at gmail dot com>,
|
|
2
|
+
and is a derivative work of Florian Frank's json library <flori at ping dot de>.
|
|
3
|
+
You can redistribute it and/or modify it under either the terms of the GPL
|
|
4
|
+
version 2 (see the file GPL), or the conditions below:
|
|
5
|
+
|
|
6
|
+
1. You may make and give away verbatim copies of the source form of the
|
|
7
|
+
software without restriction, provided that you duplicate all of the
|
|
8
|
+
original copyright notices and associated disclaimers.
|
|
9
|
+
|
|
10
|
+
2. You may modify your copy of the software in any way, provided that
|
|
11
|
+
you do at least ONE of the following:
|
|
12
|
+
|
|
13
|
+
a) place your modifications in the Public Domain or otherwise
|
|
14
|
+
make them Freely Available, such as by posting said
|
|
15
|
+
modifications to Usenet or an equivalent medium, or by allowing
|
|
16
|
+
the author to include your modifications in the software.
|
|
17
|
+
|
|
18
|
+
b) use the modified software only within your corporation or
|
|
19
|
+
organization.
|
|
20
|
+
|
|
21
|
+
c) give non-standard binaries non-standard names, with
|
|
22
|
+
instructions on where to get the original software distribution.
|
|
23
|
+
|
|
24
|
+
d) make other distribution arrangements with the author.
|
|
25
|
+
|
|
26
|
+
3. You may distribute the software in object code or binary form,
|
|
27
|
+
provided that you do at least ONE of the following:
|
|
28
|
+
|
|
29
|
+
a) distribute the binaries and library files of the software,
|
|
30
|
+
together with instructions (in the manual page or equivalent)
|
|
31
|
+
on where to get the original distribution.
|
|
32
|
+
|
|
33
|
+
b) accompany the distribution with the machine-readable source of
|
|
34
|
+
the software.
|
|
35
|
+
|
|
36
|
+
c) give non-standard binaries non-standard names, with
|
|
37
|
+
instructions on where to get the original software distribution.
|
|
38
|
+
|
|
39
|
+
d) make other distribution arrangements with the author.
|
|
40
|
+
|
|
41
|
+
4. You may modify and include the part of the software into any other
|
|
42
|
+
software (possibly commercial). But some files in the distribution
|
|
43
|
+
are not written by the author, so that they are not under these terms.
|
|
44
|
+
|
|
45
|
+
For the list of those files and their copying conditions, see the
|
|
46
|
+
file LEGAL.
|
|
47
|
+
|
|
48
|
+
5. The scripts and library files supplied as input to or produced as
|
|
49
|
+
output from the software do not automatically fall under the
|
|
50
|
+
copyright of the software, but belong to whomever generated them,
|
|
51
|
+
and may be sold commercially, and may be aggregated with this
|
|
52
|
+
software.
|
|
53
|
+
|
|
54
|
+
6. THIS SOFTWARE IS PROVIDED "AS IS" AND WITHOUT ANY EXPRESS OR
|
|
55
|
+
IMPLIED WARRANTIES, INCLUDING, WITHOUT LIMITATION, THE IMPLIED
|
|
56
|
+
WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
|
|
57
|
+
PURPOSE.
|
|
@@ -0,0 +1,22 @@
|
|
|
1
|
+
Copyright (c) 2009 Pat Nakajima
|
|
2
|
+
|
|
3
|
+
Permission is hereby granted, free of charge, to any person
|
|
4
|
+
obtaining a copy of this software and associated documentation
|
|
5
|
+
files (the "Software"), to deal in the Software without
|
|
6
|
+
restriction, including without limitation the rights to use,
|
|
7
|
+
copy, modify, merge, publish, distribute, sublicense, and/or sell
|
|
8
|
+
copies of the Software, and to permit persons to whom the
|
|
9
|
+
Software is furnished to do so, subject to the following
|
|
10
|
+
conditions:
|
|
11
|
+
|
|
12
|
+
The above copyright notice and this permission notice shall be
|
|
13
|
+
included in all copies or substantial portions of the Software.
|
|
14
|
+
|
|
15
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
|
|
16
|
+
EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES
|
|
17
|
+
OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
|
|
18
|
+
NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT
|
|
19
|
+
HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY,
|
|
20
|
+
WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
|
|
21
|
+
FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR
|
|
22
|
+
OTHER DEALINGS IN THE SOFTWARE.
|
data/script/dbconsole.rb
ADDED
|
@@ -0,0 +1,116 @@
|
|
|
1
|
+
package edu.vanderbilt.coupler;
|
|
2
|
+
|
|
3
|
+
import java.io.IOException;
|
|
4
|
+
import java.io.InputStream;
|
|
5
|
+
import java.util.Properties;
|
|
6
|
+
import java.util.ArrayList;
|
|
7
|
+
import java.util.List;
|
|
8
|
+
import java.net.URISyntaxException;
|
|
9
|
+
import java.net.URL;
|
|
10
|
+
import java.util.regex.Matcher;
|
|
11
|
+
import java.util.regex.Pattern;
|
|
12
|
+
import org.jruby.embed.ScriptingContainer;
|
|
13
|
+
import org.jruby.Ruby;
|
|
14
|
+
import org.jruby.RubyArray;
|
|
15
|
+
import org.jruby.RubyString;
|
|
16
|
+
|
|
17
|
+
public class Main {
|
|
18
|
+
private static final Properties couplerProperties = new Properties();
|
|
19
|
+
public static final String COUPLER_PROPERTIES = "coupler.properties";
|
|
20
|
+
public static final String JRUBY_PROPERTIES = "jruby.properties";
|
|
21
|
+
|
|
22
|
+
static {
|
|
23
|
+
InputStream stream = null;
|
|
24
|
+
try {
|
|
25
|
+
stream = Main.class.getResourceAsStream(COUPLER_PROPERTIES);
|
|
26
|
+
if (stream == null) {
|
|
27
|
+
throw new RuntimeException("Resource not found: " + COUPLER_PROPERTIES);
|
|
28
|
+
}
|
|
29
|
+
couplerProperties.load(stream);
|
|
30
|
+
} catch (IOException ioe) {
|
|
31
|
+
ioe.printStackTrace();
|
|
32
|
+
} finally {
|
|
33
|
+
if (stream != null) {
|
|
34
|
+
try {
|
|
35
|
+
stream.close();
|
|
36
|
+
} catch (IOException e) {
|
|
37
|
+
// silently ignore
|
|
38
|
+
}
|
|
39
|
+
}
|
|
40
|
+
}
|
|
41
|
+
}
|
|
42
|
+
|
|
43
|
+
private Main(String[] args) {
|
|
44
|
+
System.out.printf("Coupler version: %s\nBuild date: %s\n\n",
|
|
45
|
+
couplerProperties.getProperty("coupler.version"),
|
|
46
|
+
couplerProperties.getProperty("build.timestamp"));
|
|
47
|
+
|
|
48
|
+
// Set JRuby runtime properties
|
|
49
|
+
Properties systemProperties;
|
|
50
|
+
InputStream stream = null;
|
|
51
|
+
try {
|
|
52
|
+
stream = Main.class.getResourceAsStream(JRUBY_PROPERTIES);
|
|
53
|
+
if (stream == null) {
|
|
54
|
+
throw new RuntimeException("Resource not found: " + JRUBY_PROPERTIES);
|
|
55
|
+
}
|
|
56
|
+
systemProperties = new Properties(System.getProperties());
|
|
57
|
+
systemProperties.load(stream);
|
|
58
|
+
System.setProperties(systemProperties);
|
|
59
|
+
} catch (IOException ioe) {
|
|
60
|
+
ioe.printStackTrace();
|
|
61
|
+
} finally {
|
|
62
|
+
if (stream != null) {
|
|
63
|
+
try {
|
|
64
|
+
stream.close();
|
|
65
|
+
} catch (IOException e) {
|
|
66
|
+
// silently ignore
|
|
67
|
+
}
|
|
68
|
+
}
|
|
69
|
+
}
|
|
70
|
+
|
|
71
|
+
String location = findCouplerPath();
|
|
72
|
+
List<String> loadPaths = new ArrayList();
|
|
73
|
+
loadPaths.add(location);
|
|
74
|
+
|
|
75
|
+
ScriptingContainer container = new ScriptingContainer();
|
|
76
|
+
container.setLoadPaths(loadPaths);
|
|
77
|
+
|
|
78
|
+
// FIXME: I feel like there's a simpler way of doing this.
|
|
79
|
+
Ruby ruby = container.getProvider().getRuntime();
|
|
80
|
+
RubyArray rbArray = ruby.newArray();
|
|
81
|
+
for (String string : args) {
|
|
82
|
+
rbArray.append(ruby.newString(string));
|
|
83
|
+
}
|
|
84
|
+
container.put("argv", rbArray);
|
|
85
|
+
|
|
86
|
+
// SystemExit gets thrown when someone runs the JAR with --help
|
|
87
|
+
String script =
|
|
88
|
+
"require 'coupler'\n" +
|
|
89
|
+
"begin\n" +
|
|
90
|
+
" Coupler::Runner.new(argv)\n" +
|
|
91
|
+
"rescue SystemExit\n" +
|
|
92
|
+
"end";
|
|
93
|
+
container.runScriptlet(script);
|
|
94
|
+
}
|
|
95
|
+
|
|
96
|
+
private String findCouplerPath() {
|
|
97
|
+
try {
|
|
98
|
+
URL resource = getClass().getResource("/META-INF/coupler.home/lib/coupler.rb");
|
|
99
|
+
String location = resource.toURI().getSchemeSpecificPart();
|
|
100
|
+
Pattern p = Pattern.compile("coupler\\.rb$");
|
|
101
|
+
Matcher m = p.matcher(location);
|
|
102
|
+
while(m.find()) {
|
|
103
|
+
location = location.substring(0, m.start() - 1);
|
|
104
|
+
return location;
|
|
105
|
+
}
|
|
106
|
+
return null;
|
|
107
|
+
}
|
|
108
|
+
catch (URISyntaxException e) {
|
|
109
|
+
return null;
|
|
110
|
+
}
|
|
111
|
+
}
|
|
112
|
+
|
|
113
|
+
public static void main(String[] args) {
|
|
114
|
+
new Main(args);
|
|
115
|
+
}
|
|
116
|
+
}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
jruby.thread.pooling=true
|
|
@@ -0,0 +1,84 @@
|
|
|
1
|
+
class SourceAnnotationExtractor
|
|
2
|
+
class Annotation < Struct.new(:line, :tag, :text)
|
|
3
|
+
def to_s(options={})
|
|
4
|
+
s = "[%3d] " % line
|
|
5
|
+
s << "[#{tag}] " if options[:tag]
|
|
6
|
+
s << text
|
|
7
|
+
end
|
|
8
|
+
end
|
|
9
|
+
|
|
10
|
+
def self.enumerate(tag, options={})
|
|
11
|
+
extractor = new(tag)
|
|
12
|
+
extractor.display(extractor.find, options)
|
|
13
|
+
end
|
|
14
|
+
|
|
15
|
+
attr_reader :tag
|
|
16
|
+
|
|
17
|
+
def initialize(tag)
|
|
18
|
+
@tag = tag
|
|
19
|
+
end
|
|
20
|
+
|
|
21
|
+
def find(dirs=%w(lib test))
|
|
22
|
+
dirs.inject({}) { |h, dir| h.update(find_in(dir)) }
|
|
23
|
+
end
|
|
24
|
+
|
|
25
|
+
def find_in(dir)
|
|
26
|
+
results = {}
|
|
27
|
+
|
|
28
|
+
Dir.glob("#{dir}/*") do |item|
|
|
29
|
+
next if File.basename(item)[0] == ?.
|
|
30
|
+
|
|
31
|
+
if File.directory?(item)
|
|
32
|
+
results.update(find_in(item))
|
|
33
|
+
elsif item =~ /\.(builder|(r(?:b|xml|js)))$/
|
|
34
|
+
results.update(extract_annotations_from(item, /#\s*(#{tag}):?\s*(.*)$/))
|
|
35
|
+
elsif item =~ /\.(rhtml|erb)$/
|
|
36
|
+
results.update(extract_annotations_from(item, /<%\s*#\s*(#{tag}):?\s*(.*?)\s*%>/))
|
|
37
|
+
end
|
|
38
|
+
end
|
|
39
|
+
|
|
40
|
+
results
|
|
41
|
+
end
|
|
42
|
+
|
|
43
|
+
def extract_annotations_from(file, pattern)
|
|
44
|
+
lineno = 0
|
|
45
|
+
result = File.readlines(file).inject([]) do |list, line|
|
|
46
|
+
lineno += 1
|
|
47
|
+
next list unless line =~ pattern
|
|
48
|
+
list << Annotation.new(lineno, $1, $2)
|
|
49
|
+
end
|
|
50
|
+
result.empty? ? {} : { file => result }
|
|
51
|
+
end
|
|
52
|
+
|
|
53
|
+
def display(results, options={})
|
|
54
|
+
results.keys.sort.each do |file|
|
|
55
|
+
puts "#{file}:"
|
|
56
|
+
results[file].each do |note|
|
|
57
|
+
puts " * #{note.to_s(options)}"
|
|
58
|
+
end
|
|
59
|
+
puts
|
|
60
|
+
end
|
|
61
|
+
end
|
|
62
|
+
end
|
|
63
|
+
|
|
64
|
+
desc "Enumerate all annotations"
|
|
65
|
+
task :notes do
|
|
66
|
+
SourceAnnotationExtractor.enumerate "OPTIMIZE|FIXME|TODO", :tag => true
|
|
67
|
+
end
|
|
68
|
+
|
|
69
|
+
namespace :notes do
|
|
70
|
+
desc "Enumerate all OPTIMIZE annotations"
|
|
71
|
+
task :optimize do
|
|
72
|
+
SourceAnnotationExtractor.enumerate "OPTIMIZE"
|
|
73
|
+
end
|
|
74
|
+
|
|
75
|
+
desc "Enumerate all FIXME annotations"
|
|
76
|
+
task :fixme do
|
|
77
|
+
SourceAnnotationExtractor.enumerate "FIXME"
|
|
78
|
+
end
|
|
79
|
+
|
|
80
|
+
desc "Enumerate all TODO annotations"
|
|
81
|
+
task :todo do
|
|
82
|
+
SourceAnnotationExtractor.enumerate "TODO"
|
|
83
|
+
end
|
|
84
|
+
end
|