hobo 0.5.3 → 0.6
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/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
|
+
|