hobo 0.5.3 → 0.6
Sign up to get free protection for your applications and to get access to all the features.
- data/bin/hobo +18 -4
- data/hobo_files/plugin/CHANGES.txt +511 -0
- data/hobo_files/plugin/README +8 -3
- data/hobo_files/plugin/Rakefile +81 -0
- data/hobo_files/plugin/generators/hobo/hobo_generator.rb +4 -4
- data/hobo_files/plugin/generators/hobo/templates/guest.rb +1 -1
- data/hobo_files/plugin/generators/hobo_front_controller/hobo_front_controller_generator.rb +1 -1
- data/hobo_files/plugin/generators/hobo_front_controller/templates/index.dryml +16 -22
- data/hobo_files/plugin/generators/hobo_front_controller/templates/login.dryml +4 -6
- data/hobo_files/plugin/generators/hobo_front_controller/templates/search.dryml +6 -5
- data/hobo_files/plugin/generators/hobo_front_controller/templates/signup.dryml +4 -6
- data/hobo_files/plugin/generators/hobo_migration/hobo_migration_generator.rb +237 -0
- data/hobo_files/plugin/generators/hobo_migration/templates/migration.rb +9 -0
- data/hobo_files/plugin/generators/hobo_model/USAGE +2 -3
- data/hobo_files/plugin/generators/hobo_model/hobo_model_generator.rb +1 -14
- data/hobo_files/plugin/generators/hobo_model/templates/fixtures.yml +1 -6
- data/hobo_files/plugin/generators/hobo_model/templates/model.rb +10 -4
- data/hobo_files/plugin/generators/hobo_rapid/hobo_rapid_generator.rb +7 -6
- data/hobo_files/plugin/generators/hobo_rapid/templates/hobo_base.css +68 -0
- data/hobo_files/plugin/generators/hobo_rapid/templates/hobo_rapid.css +93 -0
- data/hobo_files/plugin/generators/hobo_rapid/templates/hobo_rapid.js +11 -6
- data/hobo_files/plugin/generators/hobo_rapid/templates/themes/default/public/images/plus.png +0 -0
- data/hobo_files/plugin/generators/hobo_rapid/templates/themes/default/public/stylesheets/application.css +24 -14
- data/hobo_files/plugin/generators/hobo_rapid/templates/themes/default/views/application.dryml +28 -44
- data/hobo_files/plugin/generators/hobo_user_model/USAGE +2 -12
- data/hobo_files/plugin/generators/hobo_user_model/hobo_user_model_generator.rb +1 -14
- data/hobo_files/plugin/generators/hobo_user_model/templates/fixtures.yml +0 -6
- data/hobo_files/plugin/generators/hobo_user_model/templates/model.rb +8 -1
- data/hobo_files/plugin/init.rb +6 -2
- data/hobo_files/plugin/lib/active_record/has_many_association.rb +23 -12
- data/hobo_files/plugin/lib/extensions.rb +134 -40
- data/hobo_files/plugin/lib/extensions/test_case.rb +0 -1
- data/hobo_files/plugin/lib/hobo.rb +77 -46
- data/hobo_files/plugin/lib/hobo/authenticated_user.rb +24 -2
- data/hobo_files/plugin/lib/hobo/authentication_support.rb +2 -1
- data/hobo_files/plugin/lib/hobo/controller.rb +35 -12
- data/hobo_files/plugin/lib/hobo/define_tags.rb +4 -4
- data/hobo_files/plugin/lib/hobo/dryml.rb +33 -51
- data/hobo_files/plugin/lib/hobo/dryml/dryml_builder.rb +47 -34
- data/hobo_files/plugin/lib/hobo/dryml/scoped_variables.rb +37 -0
- data/hobo_files/plugin/lib/hobo/dryml/taglib.rb +27 -5
- data/hobo_files/plugin/lib/hobo/dryml/template.rb +545 -302
- data/hobo_files/plugin/lib/hobo/dryml/template_environment.rb +305 -135
- data/hobo_files/plugin/lib/hobo/email_address.rb +5 -0
- data/hobo_files/plugin/lib/hobo/field_spec.rb +66 -0
- data/hobo_files/plugin/lib/hobo/hobo_helper.rb +325 -0
- data/hobo_files/plugin/lib/hobo/html_string.rb +2 -0
- data/hobo_files/plugin/lib/hobo/lazy_hash.rb +13 -1
- data/hobo_files/plugin/lib/hobo/markdown_string.rb +3 -1
- data/hobo_files/plugin/lib/hobo/model.rb +185 -66
- data/hobo_files/plugin/lib/hobo/model_controller.rb +56 -49
- data/hobo_files/plugin/lib/hobo/password_string.rb +2 -0
- data/hobo_files/plugin/lib/hobo/plugins.rb +75 -0
- data/hobo_files/plugin/lib/hobo/rapid_helper.rb +98 -0
- data/hobo_files/plugin/lib/hobo/static_tags +0 -3
- data/hobo_files/plugin/lib/hobo/textile_string.rb +11 -1
- data/hobo_files/plugin/lib/hobo/undefined.rb +1 -1
- data/hobo_files/plugin/lib/rexml.rb +166 -75
- data/hobo_files/plugin/spec/fixtures/users.yml +9 -0
- data/hobo_files/plugin/spec/spec.opts +6 -0
- data/hobo_files/plugin/spec/spec_helper.rb +28 -0
- data/hobo_files/plugin/spec/unit/hobo/dryml/template_spec.rb +650 -0
- data/hobo_files/plugin/tags/core.dryml +58 -4
- data/hobo_files/plugin/tags/rapid.dryml +289 -135
- data/hobo_files/plugin/tags/rapid_document_tags.dryml +49 -0
- data/hobo_files/plugin/tags/rapid_editing.dryml +92 -69
- data/hobo_files/plugin/tags/rapid_forms.dryml +242 -0
- data/hobo_files/plugin/tags/rapid_navigation.dryml +65 -65
- data/hobo_files/plugin/tags/rapid_pages.dryml +197 -124
- data/hobo_files/plugin/tags/rapid_support.dryml +23 -0
- metadata +29 -22
- data/hobo_files/plugin/generators/hobo_model/templates/migration.rb +0 -13
- data/hobo_files/plugin/generators/hobo_rapid/templates/themes/default/default_mapping.rb +0 -11
- data/hobo_files/plugin/generators/hobo_user_model/templates/migration.rb +0 -15
- data/hobo_files/plugin/lib/hobo/HtmlString +0 -3
- data/hobo_files/plugin/lib/hobo/controller_helpers.rb +0 -135
- data/hobo_files/plugin/lib/hobo/core.rb +0 -475
- data/hobo_files/plugin/lib/hobo/rapid.rb +0 -447
- data/hobo_files/plugin/test/hobo_dryml_template_test.rb +0 -7
- data/hobo_files/plugin/test/hobo_test.rb +0 -7
data/hobo_files/plugin/README
CHANGED
@@ -1,4 +1,9 @@
|
|
1
|
-
Hobo
|
2
|
-
|
1
|
+
== Welcome to Hobo
|
2
|
+
Hobo is an Open Source extension to Ruby on Rails which helps you build full blown web applications incredibly quickly and easily. Available as a Gem or Rails plugin, Hobo provides a simple, clean and elegant development framework which allows for rapid prototyping or production of the most sophisticated web applications.
|
3
|
+
|
4
|
+
== Main Features
|
5
|
+
|
6
|
+
* Rapid implementation of dynamic Ajax interfaces in your application with no extra programming.
|
7
|
+
* Switchable themes. Customise and tweak your application structure and layout to meet any design goals.
|
8
|
+
* Powerful mark-up language, DRYML, combines rapid development with ultimate design flexibility. The end of the cookie cutter blues!
|
3
9
|
|
4
|
-
Description goes here
|
data/hobo_files/plugin/Rakefile
CHANGED
@@ -1,5 +1,9 @@
|
|
1
1
|
require 'rake'
|
2
2
|
require 'rake/rdoctask'
|
3
|
+
require 'rake/testtask'
|
4
|
+
|
5
|
+
desc 'Default: run unit tests.'
|
6
|
+
task :default => :spec
|
3
7
|
|
4
8
|
desc 'Generate documentation for the Hobo plugin.'
|
5
9
|
Rake::RDocTask.new(:rdoc) do |rdoc|
|
@@ -9,3 +13,80 @@ Rake::RDocTask.new(:rdoc) do |rdoc|
|
|
9
13
|
rdoc.rdoc_files.include('README')
|
10
14
|
rdoc.rdoc_files.include('lib/**/*.rb')
|
11
15
|
end
|
16
|
+
|
17
|
+
|
18
|
+
|
19
|
+
# --- RSpec --- #
|
20
|
+
|
21
|
+
# In rails 1.2, plugins aren't available in the path until they're loaded.
|
22
|
+
# Check to see if the rspec plugin is installed first and require
|
23
|
+
# it if it is. If not, use the gem version.
|
24
|
+
PLUGIN_DIR = File.dirname(__FILE__)
|
25
|
+
|
26
|
+
rspec_base = File.expand_path(PLUGIN_DIR + '/spec/rails_root/vendor/plugins/rspec/lib')
|
27
|
+
$LOAD_PATH.unshift(rspec_base) if File.exist?(rspec_base)
|
28
|
+
require 'spec/rake/spectask'
|
29
|
+
require 'spec/translator'
|
30
|
+
|
31
|
+
spec_prereq = :noop # File.exist?(File.join(PLUGIN_DIR, 'config', 'database.yml')) ? "db:test:prepare" : :noop
|
32
|
+
task :noop do
|
33
|
+
end
|
34
|
+
|
35
|
+
task :stats => "spec:statsetup"
|
36
|
+
|
37
|
+
desc "Run all specs in spec directory (excluding plugin specs)"
|
38
|
+
Spec::Rake::SpecTask.new(:spec => spec_prereq) do |t|
|
39
|
+
t.spec_opts = ['--options', "\"#{PLUGIN_DIR}/spec/spec.opts\""]
|
40
|
+
t.spec_files = FileList['spec/unit/**/*_spec.rb']
|
41
|
+
end
|
42
|
+
|
43
|
+
namespace :spec do
|
44
|
+
desc "Run all specs in spec directory with RCov (excluding plugin specs)"
|
45
|
+
Spec::Rake::SpecTask.new(:rcov) do |t|
|
46
|
+
t.spec_opts = ['--options', "\"#{PLUGIN_DIR}/spec/spec.opts\""]
|
47
|
+
t.spec_files = FileList['spec/unit/**/*_spec.rb']
|
48
|
+
t.rcov = true
|
49
|
+
t.rcov_opts = ['--exclude', 'spec', '--rails']
|
50
|
+
end
|
51
|
+
|
52
|
+
desc "Print Specdoc for all specs (excluding plugin specs)"
|
53
|
+
Spec::Rake::SpecTask.new(:doc) do |t|
|
54
|
+
t.spec_opts = ["--format", "specdoc", "--dry-run"]
|
55
|
+
t.spec_files = FileList['spec/unit/**/*_spec.rb']
|
56
|
+
end
|
57
|
+
|
58
|
+
[:models, :controllers, :views, :helpers].each do |sub|
|
59
|
+
desc "Run the specs under spec/#{sub}"
|
60
|
+
Spec::Rake::SpecTask.new(sub => spec_prereq) do |t|
|
61
|
+
t.spec_opts = ['--options', "\"#{PLUGIN_DIR}/spec/spec.opts\""]
|
62
|
+
t.spec_files = FileList["spec/#{sub}/**/*_spec.rb"]
|
63
|
+
end
|
64
|
+
end
|
65
|
+
|
66
|
+
# Setup specs for stats
|
67
|
+
task :statsetup do
|
68
|
+
require 'code_statistics'
|
69
|
+
::STATS_DIRECTORIES << %w(Model\ specs spec/models)
|
70
|
+
::STATS_DIRECTORIES << %w(View\ specs spec/views)
|
71
|
+
::STATS_DIRECTORIES << %w(Controller\ specs spec/controllers)
|
72
|
+
::STATS_DIRECTORIES << %w(Helper\ specs spec/views)
|
73
|
+
::CodeStatistics::TEST_TYPES << "Model specs"
|
74
|
+
::CodeStatistics::TEST_TYPES << "View specs"
|
75
|
+
::CodeStatistics::TEST_TYPES << "Controller specs"
|
76
|
+
::CodeStatistics::TEST_TYPES << "Helper specs"
|
77
|
+
::STATS_DIRECTORIES.delete_if {|a| a[0] =~ /test/}
|
78
|
+
end
|
79
|
+
|
80
|
+
namespace :db do
|
81
|
+
namespace :fixtures do
|
82
|
+
desc "Load fixtures (from spec/fixtures) into the current environment's database. Load specific fixtures using FIXTURES=x,y"
|
83
|
+
task :load => :environment do
|
84
|
+
require 'active_record/fixtures'
|
85
|
+
ActiveRecord::Base.establish_connection(RAILS_ENV.to_sym)
|
86
|
+
(ENV['FIXTURES'] ? ENV['FIXTURES'].split(/,/) : Dir.glob(File.join(PLUGIN_DIR, 'spec', 'fixtures', '*.{yml,csv}'))).each do |fixture_file|
|
87
|
+
Fixtures.create_fixtures('spec/fixtures', File.basename(fixture_file, '.*'))
|
88
|
+
end
|
89
|
+
end
|
90
|
+
end
|
91
|
+
end
|
92
|
+
end
|
@@ -15,17 +15,17 @@ class HoboGenerator < Rails::Generator::Base
|
|
15
15
|
end
|
16
16
|
|
17
17
|
record do |m|
|
18
|
-
m.directory File.join("app/views/
|
19
|
-
m.directory File.join("app/views/
|
18
|
+
m.directory File.join("app/views/taglibs")
|
19
|
+
m.directory File.join("app/views/taglibs/themes")
|
20
20
|
m.directory File.join("public/hobothemes")
|
21
|
-
m.file "application.dryml", File.join("app/views/
|
21
|
+
m.file "application.dryml", File.join("app/views/taglibs/application.dryml")
|
22
22
|
m.file "guest.rb", File.join("app/models/guest.rb")
|
23
23
|
end
|
24
24
|
end
|
25
25
|
|
26
26
|
protected
|
27
27
|
def banner
|
28
|
-
"Usage: #{$0}
|
28
|
+
"Usage: #{$0} #{spec.name} [--add-routes]"
|
29
29
|
end
|
30
30
|
|
31
31
|
def add_options!(opt)
|
@@ -73,7 +73,7 @@ class HoboFrontControllerGenerator < Rails::Generator::NamedBase
|
|
73
73
|
|
74
74
|
protected
|
75
75
|
def banner
|
76
|
-
"Usage: #{$0}
|
76
|
+
"Usage: #{$0} #{spec.name} <controller-name> [--add-routes] [--no-user] [--delete-index]"
|
77
77
|
end
|
78
78
|
|
79
79
|
def add_options!(opt)
|
@@ -1,43 +1,37 @@
|
|
1
|
-
<
|
1
|
+
<Page title="<%= app_name %>">
|
2
2
|
|
3
|
-
|
4
|
-
<
|
5
|
-
|
6
|
-
|
7
|
-
<:main>
|
3
|
+
<main>
|
4
|
+
<header>
|
5
|
+
<h1 class="front_page_title"><%= app_name %></h1>
|
6
|
+
</header>
|
8
7
|
<panel>
|
9
8
|
<div style="margin-left: 40px; ">
|
10
9
|
<h3>Congratulations! Your Hobo Rails App is up and running</h3>
|
11
10
|
<ul>
|
12
11
|
<li>To customise this page: edit app/views/<%= file_name %>/index.dryml </li>
|
13
|
-
|
14
|
-
<li>To change the nav-bar: define <application_nav><br/>
|
15
|
-
See vendor/plugins/hobo/tags/rapid.dryml for an example</li>
|
16
|
-
|
17
|
-
<li>To change the logo: define <application_logo>.</li>
|
18
12
|
</ul>
|
19
13
|
</div>
|
20
14
|
</panel>
|
21
15
|
|
22
|
-
<repeat
|
16
|
+
<repeat with="&Hobo.models">
|
23
17
|
<panel>
|
24
|
-
<h2><%%= this.name.titlecase.pluralize %></h2>
|
18
|
+
<header><h2><%%= this.name.titlecase.pluralize %></h2></header>
|
25
19
|
<section>
|
26
|
-
<if
|
20
|
+
<if test="&this.count == 0">
|
27
21
|
<p>There are no <%%= this.name.titleize.pluralize %></p>
|
28
|
-
<if
|
29
|
-
<p>Create a <
|
22
|
+
<if test="&can_create?(this) and this != Hobo.user_model">
|
23
|
+
<p>Create a <a to="&this.new"/>.</p>
|
30
24
|
</if>
|
31
25
|
</if>
|
32
26
|
<else>
|
33
|
-
<repeat
|
34
|
-
<
|
27
|
+
<repeat with="&this.find(:all, :limit => 3)">
|
28
|
+
<card if="&can_view?"/>
|
35
29
|
</repeat>
|
36
|
-
<p><
|
30
|
+
<p><a>More</a> (<count/>)</p>
|
37
31
|
</else>
|
38
32
|
</section>
|
39
33
|
</panel>
|
40
34
|
</repeat>
|
41
|
-
|
42
|
-
|
43
|
-
</
|
35
|
+
</main>
|
36
|
+
|
37
|
+
</Page>
|
@@ -1,10 +1,8 @@
|
|
1
|
-
<
|
1
|
+
<Page title="<%= app_name %>">
|
2
2
|
|
3
|
-
|
3
|
+
<main>
|
4
4
|
<h1 class="front_page_title"><%= app_name %></h1>
|
5
|
-
</:intro>
|
6
5
|
|
7
|
-
<:main>
|
8
6
|
<panel>
|
9
7
|
<h2>Login to <%= app_name %></h2>
|
10
8
|
<section>
|
@@ -39,6 +37,6 @@
|
|
39
37
|
Edit app/views/<%= full_class_path %>/login.dryml to customise this page
|
40
38
|
</div>
|
41
39
|
</panel>
|
42
|
-
|
40
|
+
</main>
|
43
41
|
|
44
|
-
</
|
42
|
+
</Page>
|
@@ -1,6 +1,7 @@
|
|
1
|
-
<
|
1
|
+
<Page title="Search">
|
2
|
+
<body onload="Hobo.applyEvents(); Hobo.doSearch('search_field')"/>
|
2
3
|
|
3
|
-
|
4
|
+
<main>
|
4
5
|
<panel class="red" style="margin-top: 40px">
|
5
6
|
<h2>Search</h2>
|
6
7
|
<div class='search' style='margin:10px'>
|
@@ -11,8 +12,8 @@
|
|
11
12
|
|
12
13
|
<panel class="hidden" id="search_results_panel">
|
13
14
|
<h2>Results</h2>
|
14
|
-
<section id="search_results"
|
15
|
+
<section id="search_results"> </section>
|
15
16
|
</panel>
|
16
|
-
|
17
|
+
</main>
|
17
18
|
|
18
|
-
</
|
19
|
+
</Page>
|
@@ -1,10 +1,8 @@
|
|
1
|
-
<
|
1
|
+
<Page title="<%= app_name %>">
|
2
2
|
|
3
|
-
|
3
|
+
<main>
|
4
4
|
<h1 class="front_page_title"><%= app_name %></h1>
|
5
|
-
</:intro>
|
6
5
|
|
7
|
-
<:main>
|
8
6
|
<panel>
|
9
7
|
<h2>Sign Up</h2>
|
10
8
|
<section>
|
@@ -39,7 +37,7 @@
|
|
39
37
|
Edit app/views/<%= full_class_path %>/signup.dryml to customise this page
|
40
38
|
</div>
|
41
39
|
</panel>
|
42
|
-
|
40
|
+
</main>
|
43
41
|
|
44
|
-
</
|
42
|
+
</Page>
|
45
43
|
|
@@ -0,0 +1,237 @@
|
|
1
|
+
class HoboMigrationGenerator < Rails::Generator::Base
|
2
|
+
|
3
|
+
def initialize(runtime_args, runtime_options = {})
|
4
|
+
super
|
5
|
+
@migration_name = runtime_args.first || begin
|
6
|
+
i = Dir["#{RAILS_ROOT}/db/migrate/*hobo_migration*"].length
|
7
|
+
"hobo_migration_#{i+1}"
|
8
|
+
end
|
9
|
+
end
|
10
|
+
|
11
|
+
def manifest
|
12
|
+
connection = ActiveRecord::Base.connection
|
13
|
+
@types = connection.native_database_types
|
14
|
+
|
15
|
+
# Force load of hobo models
|
16
|
+
Hobo.models
|
17
|
+
|
18
|
+
models = ActiveRecord::Base.send(:subclasses).reject {|c| c.name.starts_with?("CGI::") }
|
19
|
+
table_models = models.index_by {|m| m.table_name}
|
20
|
+
model_table_names = models.every(:table_name)
|
21
|
+
|
22
|
+
to_create = model_table_names - connection.tables
|
23
|
+
to_drop = connection.tables - model_table_names - ['schema_info']
|
24
|
+
to_change = connection.tables & model_table_names
|
25
|
+
|
26
|
+
to_rename = rename_or_drop!(to_create, to_drop, "table")
|
27
|
+
|
28
|
+
renames = to_rename.map do |old_name, new_name|
|
29
|
+
"rename_table :#{old_name}, :#{new_name}"
|
30
|
+
end * "\n"
|
31
|
+
undo_renames = to_rename.map do |old_name, new_name|
|
32
|
+
"rename_table :#{new_name}, :#{old_name}"
|
33
|
+
end * "\n"
|
34
|
+
|
35
|
+
drops = to_drop.map do |t|
|
36
|
+
"drop_table :#{t}"
|
37
|
+
end * "\n"
|
38
|
+
undo_drops = to_drop.map do |t|
|
39
|
+
revert_table(t)
|
40
|
+
end * "\n\n"
|
41
|
+
|
42
|
+
creates = to_create.map do |t|
|
43
|
+
create_table(table_models[t])
|
44
|
+
end * "\n\n"
|
45
|
+
undo_creates = to_create.map do |t|
|
46
|
+
"drop_table :#{t}"
|
47
|
+
end * "\n"
|
48
|
+
|
49
|
+
changes = []
|
50
|
+
undo_changes = []
|
51
|
+
to_change.each do |t|
|
52
|
+
change, undo = change_table(table_models[t])
|
53
|
+
changes << change
|
54
|
+
undo_changes << undo
|
55
|
+
end
|
56
|
+
|
57
|
+
up = [renames, drops, creates, changes * "\n\n"].select{|s|!s.blank?} * "\n\n"
|
58
|
+
down = [undo_renames, undo_drops, undo_creates, undo_changes * "\n\n"].select{|s|!s.blank?} * "\n\n"
|
59
|
+
|
60
|
+
puts "\n---------- Up Migration ----------", up, "----------------------------------"
|
61
|
+
puts "\n---------- Down Migration --------", down, "----------------------------------"
|
62
|
+
|
63
|
+
return record {|m| } if up.blank?
|
64
|
+
|
65
|
+
up.gsub!("\n", "\n ")
|
66
|
+
down.gsub!("\n", "\n ")
|
67
|
+
|
68
|
+
action = input("What now: [g]enerate migrations, generate and [m]igrate now or [c]ancel?", %w(g m c))
|
69
|
+
|
70
|
+
if action == 'c'
|
71
|
+
# record nothing to keep the generator happy
|
72
|
+
record {|m| }
|
73
|
+
else
|
74
|
+
at_exit { system "rake db:migrate" } if action == 'm'
|
75
|
+
|
76
|
+
up.gsub!("\n", "\n ")
|
77
|
+
down.gsub!("\n", "\n ")
|
78
|
+
|
79
|
+
record do |m|
|
80
|
+
m.migration_template 'migration.rb', 'db/migrate',
|
81
|
+
:assigns => { :up => up, :down => down, :migration_name => @migration_name.camelize },
|
82
|
+
:migration_file_name => @migration_name
|
83
|
+
end
|
84
|
+
end
|
85
|
+
end
|
86
|
+
|
87
|
+
def rename_or_drop!(to_create, to_drop, kind_str, name_prefix="")
|
88
|
+
to_rename = {}
|
89
|
+
rename_to_choices = to_create
|
90
|
+
to_drop.dup.each do |t|
|
91
|
+
if rename_to_choices.empty?
|
92
|
+
puts "\nCONFIRM DROP! #{kind_str} #{name_prefix}#{t}"
|
93
|
+
resp = input("Enter 'drop #{t}' to confirm:")
|
94
|
+
if resp.strip != "drop " + t.to_s
|
95
|
+
to_drop.delete(t)
|
96
|
+
end
|
97
|
+
else
|
98
|
+
puts "\nDROP or RENAME?: #{kind_str} #{name_prefix}#{t}"
|
99
|
+
puts "Rename choices: #{to_create * ', '}"
|
100
|
+
resp = input("Enter either 'drop #{t}' or one of the rename choices:")
|
101
|
+
resp.strip!
|
102
|
+
|
103
|
+
if resp == "drop " + t
|
104
|
+
# Leave things as they are
|
105
|
+
else
|
106
|
+
to_drop.delete(t)
|
107
|
+
if resp.in?(rename_to_choices)
|
108
|
+
to_rename[t] = resp
|
109
|
+
to_create.delete(resp)
|
110
|
+
rename_to_choices.delete(resp)
|
111
|
+
end
|
112
|
+
end
|
113
|
+
end
|
114
|
+
end
|
115
|
+
to_rename
|
116
|
+
end
|
117
|
+
|
118
|
+
def create_table(model)
|
119
|
+
longest_field_name = model.field_specs.values.map { |f| f.sql_type.to_s.length }.max
|
120
|
+
(["create_table :#{model.table_name} do |t|"] +
|
121
|
+
model.field_specs.values.sort_by{|f| f.position}.map {|f| create_field(f, longest_field_name)} +
|
122
|
+
["end"]) * "\n"
|
123
|
+
end
|
124
|
+
|
125
|
+
def create_field(field_spec, field_name_width)
|
126
|
+
args = [field_spec.name.inspect] + format_options(field_spec.options, field_spec.sql_type)
|
127
|
+
" t.%-*s %s" % [field_name_width, field_spec.sql_type, args.join(', ')]
|
128
|
+
end
|
129
|
+
|
130
|
+
def change_table(model)
|
131
|
+
table_name = model.table_name
|
132
|
+
db_columns = model.connection.columns(model.table_name).index_by{|c|c.name} - [model.primary_key]
|
133
|
+
model_column_names = model.field_specs.keys.every(:to_s)
|
134
|
+
db_column_names = db_columns.keys.every(:to_s)
|
135
|
+
|
136
|
+
to_add = model_column_names - db_column_names
|
137
|
+
to_remove = db_column_names - model_column_names - [model.primary_key.to_sym]
|
138
|
+
|
139
|
+
to_rename = rename_or_drop!(to_add, to_remove, "column", "#{table_name}.")
|
140
|
+
|
141
|
+
db_column_names -= to_rename.keys
|
142
|
+
db_column_names |= to_rename.values
|
143
|
+
to_change = db_column_names & model_column_names
|
144
|
+
|
145
|
+
renames = to_rename.map do |old_name, new_name|
|
146
|
+
"rename_column :#{table_name}, :#{old_name}, :#{new_name}"
|
147
|
+
end
|
148
|
+
undo_renames = to_rename.map do |old_name, new_name|
|
149
|
+
"rename_column :#{table_name}, :#{new_name}, :#{old_name}"
|
150
|
+
end
|
151
|
+
|
152
|
+
to_add = to_add.sort_by{|c| model.field_specs[c].position }
|
153
|
+
adds = to_add.map do |c|
|
154
|
+
spec = model.field_specs[c]
|
155
|
+
args = [":#{spec.sql_type}"] + format_options(spec.options, spec.sql_type)
|
156
|
+
"add_column :#{table_name}, :#{c}, #{args * ', '}"
|
157
|
+
end
|
158
|
+
undo_adds = to_add.map do |c|
|
159
|
+
"remove_column :#{table_name}, :#{c}"
|
160
|
+
end
|
161
|
+
|
162
|
+
removes = to_remove.map do |c|
|
163
|
+
"remove_column :#{table_name}, :#{c}"
|
164
|
+
end
|
165
|
+
undo_removes = to_remove.map do |c|
|
166
|
+
revert_column(table_name, c)
|
167
|
+
end
|
168
|
+
|
169
|
+
old_names = to_rename.invert
|
170
|
+
changes = []
|
171
|
+
undo_changes = []
|
172
|
+
to_change.each do |c|
|
173
|
+
col_name = old_names[c] || c
|
174
|
+
col = db_columns[col_name]
|
175
|
+
spec = model.field_specs[c]
|
176
|
+
if spec.different_to?(col)
|
177
|
+
change_spec = {}
|
178
|
+
change_spec[:limit] = spec.limit if !spec.limit.nil?
|
179
|
+
change_spec[:precision] = spec.precision if !spec.precision.nil?
|
180
|
+
change_spec[:scale] = spec.scale if !spec.scale.nil?
|
181
|
+
change_spec[:null] = false unless spec.null
|
182
|
+
change_spec[:default] = spec.default if !spec.default.nil?
|
183
|
+
|
184
|
+
changes << "change_column :#{table_name}, :#{c}, " +
|
185
|
+
([":#{spec.sql_type}"] + format_options(change_spec, spec.sql_type)).join(", ")
|
186
|
+
back = change_column_back(table_name, c)
|
187
|
+
undo_changes << back unless back.blank?
|
188
|
+
else
|
189
|
+
nil
|
190
|
+
end
|
191
|
+
end.compact
|
192
|
+
|
193
|
+
[(renames + adds + removes + changes) * "\n",
|
194
|
+
(undo_renames + undo_adds + undo_removes + undo_changes) * "\n"]
|
195
|
+
end
|
196
|
+
|
197
|
+
|
198
|
+
def format_options(options, type)
|
199
|
+
options.map do |k, v|
|
200
|
+
next if k == :limit && (type == :decimal || v == @types[type][:limit])
|
201
|
+
next if k == :null && v == true
|
202
|
+
"#{k.inspect} => #{v.inspect}"
|
203
|
+
end.compact
|
204
|
+
end
|
205
|
+
|
206
|
+
|
207
|
+
def revert_table(table)
|
208
|
+
res = StringIO.new
|
209
|
+
ActiveRecord::SchemaDumper.send(:new, ActiveRecord::Base.connection).send(:table, table, res)
|
210
|
+
res.string.strip.gsub("\n ", "\n")
|
211
|
+
end
|
212
|
+
|
213
|
+
|
214
|
+
def change_column_back(table, column)
|
215
|
+
_, type, options = *revert_table(table).match(/\s*t\.column\s+"#{column}",\s+(:[a-zA-Z0-9_]+)(?:,\s+(.*?)$)?/m)
|
216
|
+
"change_column :#{table}, :#{column}, #{type}#{', ' + options.strip if options}"
|
217
|
+
end
|
218
|
+
|
219
|
+
def revert_column(table, column)
|
220
|
+
"add_column :#{table}, :#{column}, " + revert_table(table).match(/\s*t\.column\s+"#{column}",\s+(.*?)$/m)[1].strip
|
221
|
+
end
|
222
|
+
|
223
|
+
|
224
|
+
def input(prompt, options=nil)
|
225
|
+
print(prompt + " ")
|
226
|
+
if options
|
227
|
+
while !(response = STDIN.readline.strip.downcase).in?(options);
|
228
|
+
print(prompt + " ")
|
229
|
+
end
|
230
|
+
response
|
231
|
+
else
|
232
|
+
STDIN.readline
|
233
|
+
end
|
234
|
+
end
|
235
|
+
|
236
|
+
end
|
237
|
+
|