reactive 0.1.0

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.
Files changed (61) hide show
  1. data/History.txt +3 -0
  2. data/MIT-LICENSE +21 -0
  3. data/Manifest.txt +60 -0
  4. data/README.txt +130 -0
  5. data/Rakefile +14 -0
  6. data/app_generators/reactive/USAGE +11 -0
  7. data/app_generators/reactive/reactive_generator.rb +160 -0
  8. data/app_generators/reactive/templates/README +130 -0
  9. data/app_generators/reactive/templates/Rakefile +10 -0
  10. data/app_generators/reactive/templates/app/controllers/application_controller.rb +2 -0
  11. data/app_generators/reactive/templates/app/helpers/application_helper.rb +2 -0
  12. data/app_generators/reactive/templates/config/boot.rb +94 -0
  13. data/app_generators/reactive/templates/config/databases/frontbase.yml +28 -0
  14. data/app_generators/reactive/templates/config/databases/mysql.yml +54 -0
  15. data/app_generators/reactive/templates/config/databases/oracle.yml +39 -0
  16. data/app_generators/reactive/templates/config/databases/postgresql.yml +48 -0
  17. data/app_generators/reactive/templates/config/databases/sqlite2.yml +16 -0
  18. data/app_generators/reactive/templates/config/databases/sqlite3.yml +19 -0
  19. data/app_generators/reactive/templates/config/empty.log +0 -0
  20. data/app_generators/reactive/templates/config/environment.rb +11 -0
  21. data/app_generators/reactive/templates/script/destroy +12 -0
  22. data/app_generators/reactive/templates/script/generate +12 -0
  23. data/app_generators/reactive/templates/script/run +5 -0
  24. data/app_generators/reactive/templates/script/win_script.cmd +1 -0
  25. data/bin/reactive +16 -0
  26. data/lib/code_statistics.rb +107 -0
  27. data/lib/controller.rb +23 -0
  28. data/lib/controller/base.rb +442 -0
  29. data/lib/controller/filters.rb +767 -0
  30. data/lib/controller/flash.rb +161 -0
  31. data/lib/controller/helpers.rb +204 -0
  32. data/lib/controller/layout.rb +326 -0
  33. data/lib/dispatcher.rb +46 -0
  34. data/lib/generated_attribute.rb +40 -0
  35. data/lib/initializer.rb +425 -0
  36. data/lib/named_base_generator.rb +92 -0
  37. data/lib/reactive.rb +6 -0
  38. data/lib/request.rb +17 -0
  39. data/lib/source_annotation_extractor.rb +62 -0
  40. data/lib/tasks/annotations.rake +23 -0
  41. data/lib/tasks/databases.rake +347 -0
  42. data/lib/tasks/log.rake +9 -0
  43. data/lib/tasks/misc.rake +5 -0
  44. data/lib/tasks/reactive.rb +16 -0
  45. data/lib/tasks/statistics.rake +17 -0
  46. data/lib/tasks/testing.rake +118 -0
  47. data/lib/version.rb +9 -0
  48. data/lib/view.rb +1 -0
  49. data/lib/view/base.rb +33 -0
  50. data/reactive_generators/model/USAGE +27 -0
  51. data/reactive_generators/model/model_generator.rb +52 -0
  52. data/reactive_generators/model/templates/fixtures.yml +19 -0
  53. data/reactive_generators/model/templates/migration.rb +16 -0
  54. data/reactive_generators/model/templates/model.rb +2 -0
  55. data/reactive_generators/model/templates/unit_test.rb +8 -0
  56. data/reactive_generators/scaffold/USAGE +26 -0
  57. data/reactive_generators/scaffold/scaffold_generator.rb +75 -0
  58. data/reactive_generators/scaffold/templates/controller.rb +51 -0
  59. data/reactive_generators/scaffold/templates/functional_test.rb +49 -0
  60. data/reactive_generators/scaffold/templates/helper.rb +2 -0
  61. metadata +142 -0
@@ -0,0 +1,9 @@
1
+ namespace :log do
2
+ desc "Truncates all *.log files in log/ to zero bytes"
3
+ task :clear do
4
+ FileList["log/*.log"].each do |log_file|
5
+ f = File.open(log_file, "w")
6
+ f.close
7
+ end
8
+ end
9
+ end
@@ -0,0 +1,5 @@
1
+ task :default => :test
2
+ task :environment do
3
+ Reactive.boot
4
+ end
5
+
@@ -0,0 +1,16 @@
1
+ $VERBOSE = nil
2
+
3
+ REACTIVE_ROOT = Dir.pwd
4
+ require "#{REACTIVE_ROOT}/config/boot.rb"
5
+ Reactive.boot
6
+
7
+ # Load Reactive rakefile extensions
8
+ Dir["#{File.dirname(__FILE__)}/*.rake"].each { |ext| load ext }
9
+
10
+ # Load any custom rakefile extensions
11
+ Dir["#{REACTIVE_ROOT}/lib/tasks/**/*.rake"].sort.each { |ext| load ext }
12
+
13
+ # Load also the ones from the plugins
14
+ Gem.loaded_specs.values.each do |spec|
15
+ Dir["#{spec.full_gem_path}/**/tasks/**/*.rake"].sort.each { |ext| load ext }
16
+ end
@@ -0,0 +1,17 @@
1
+ STATS_DIRECTORIES = [
2
+ %w(Controllers app/controllers),
3
+ %w(Helpers app/helpers),
4
+ %w(Models app/models),
5
+ %w(Views app/models),
6
+ %w(Libraries lib/),
7
+ %w(Integration\ tests test/integration),
8
+ %w(Functional\ tests test/functional),
9
+ %w(Unit\ tests test/unit)
10
+
11
+ ].collect { |name, dir| [ name, "#{REACTIVE_ROOT}/#{dir}" ] }.select { |name, dir| File.directory?(dir) }
12
+
13
+ desc "Report code statistics (KLOCs, etc) from the application"
14
+ task :stats do
15
+ require 'code_statistics'
16
+ CodeStatistics.new(*STATS_DIRECTORIES).to_s
17
+ end
@@ -0,0 +1,118 @@
1
+ TEST_CHANGES_SINCE = Time.now - 600
2
+
3
+ # Look up tests for recently modified sources.
4
+ def recent_tests(source_pattern, test_path, touched_since = 10.minutes.ago)
5
+ FileList[source_pattern].map do |path|
6
+ if File.mtime(path) > touched_since
7
+ tests = []
8
+ source_dir = File.dirname(path).split("/")
9
+ source_file = File.basename(path, '.rb')
10
+
11
+ # Support subdirs in app/models and app/controllers
12
+ modified_test_path = source_dir.length > 2 ? "#{test_path}/" << source_dir[1..source_dir.length].join('/') : test_path
13
+
14
+ # For modified files in app/ run the tests for it. ex. /test/functional/account_controller.rb
15
+ test = "#{modified_test_path}/#{source_file}_test.rb"
16
+ tests.push test if File.exists?(test)
17
+
18
+ # For modified files in app, run tests in subdirs too. ex. /test/functional/account/*_test.rb
19
+ test = "#{modified_test_path}/#{File.basename(path, '.rb').sub("_controller","")}"
20
+ FileList["#{test}/*_test.rb"].each { |f| tests.push f } if File.exists?(test)
21
+
22
+ return tests
23
+
24
+ end
25
+ end.flatten.compact
26
+ end
27
+
28
+
29
+ # Recreated here from ActiveSupport because :uncommitted needs it before Reactive is available
30
+ module Kernel
31
+ def silence_stderr
32
+ old_stderr = STDERR.dup
33
+ STDERR.reopen(RUBY_PLATFORM =~ /(:?mswin|mingw)/ ? 'NUL:' : '/dev/null')
34
+ STDERR.sync = true
35
+ yield
36
+ ensure
37
+ STDERR.reopen(old_stderr)
38
+ end
39
+ end
40
+
41
+ desc 'Test all units and functionals'
42
+ task :test do
43
+ errors = %w(test:units test:functionals test:integration).collect do |task|
44
+ begin
45
+ Rake::Task[task].invoke
46
+ nil
47
+ rescue => e
48
+ task
49
+ end
50
+ end.compact
51
+ abort "Errors running #{errors.to_sentence}!" if errors.any?
52
+ end
53
+
54
+ namespace :test do
55
+ Rake::TestTask.new(:recent => "db:test:prepare") do |t|
56
+ since = TEST_CHANGES_SINCE
57
+ touched = FileList['test/**/*_test.rb'].select { |path| File.mtime(path) > since } +
58
+ recent_tests('app/models/**/*.rb', 'test/unit', since) +
59
+ recent_tests('app/controllers/**/*.rb', 'test/functional', since)
60
+
61
+ t.libs << 'test'
62
+ t.verbose = true
63
+ t.test_files = touched.uniq
64
+ end
65
+ Rake::Task['test:recent'].comment = "Test recent changes"
66
+
67
+ Rake::TestTask.new(:uncommitted => "db:test:prepare") do |t|
68
+ def t.file_list
69
+ changed_since_checkin = silence_stderr { `svn status` }.map { |path| path.chomp[7 .. -1] }
70
+
71
+ models = changed_since_checkin.select { |path| path =~ /app[\\\/]models[\\\/].*\.rb/ }
72
+ controllers = changed_since_checkin.select { |path| path =~ /app[\\\/]controllers[\\\/].*\.rb/ }
73
+
74
+ unit_tests = models.map { |model| "test/unit/#{File.basename(model, '.rb')}_test.rb" }
75
+ functional_tests = controllers.map { |controller| "test/functional/#{File.basename(controller, '.rb')}_test.rb" }
76
+
77
+ unit_tests.uniq + functional_tests.uniq
78
+ end
79
+
80
+ t.libs << 'test'
81
+ t.verbose = true
82
+ end
83
+ Rake::Task['test:uncommitted'].comment = "Test changes since last checkin (only Subversion)"
84
+
85
+ Rake::TestTask.new(:units => "db:test:prepare") do |t|
86
+ t.libs << "test"
87
+ t.pattern = 'test/unit/**/*_test.rb'
88
+ t.verbose = true
89
+ end
90
+ Rake::Task['test:units'].comment = "Run the unit tests in test/unit"
91
+
92
+ Rake::TestTask.new(:functionals => "db:test:prepare") do |t|
93
+ t.libs << "test"
94
+ t.pattern = 'test/functional/**/*_test.rb'
95
+ t.verbose = true
96
+ end
97
+ Rake::Task['test:functionals'].comment = "Run the functional tests in test/functional"
98
+
99
+ Rake::TestTask.new(:integration => "db:test:prepare") do |t|
100
+ t.libs << "test"
101
+ t.pattern = 'test/integration/**/*_test.rb'
102
+ t.verbose = true
103
+ end
104
+ Rake::Task['test:integration'].comment = "Run the integration tests in test/integration"
105
+
106
+ Rake::TestTask.new(:plugins => :environment) do |t|
107
+ t.libs << "test"
108
+
109
+ if ENV['PLUGIN']
110
+ t.pattern = "vendor/plugins/#{ENV['PLUGIN']}/test/**/*_test.rb"
111
+ else
112
+ t.pattern = 'vendor/plugins/*/**/test/**/*_test.rb'
113
+ end
114
+
115
+ t.verbose = true
116
+ end
117
+ Rake::Task['test:plugins'].comment = "Run the plugin tests in vendor/plugins/*/**/test (or specify with PLUGIN=name)"
118
+ end
@@ -0,0 +1,9 @@
1
+ module Reactive
2
+ module VERSION #:nodoc:
3
+ MAJOR = 0
4
+ MINOR = 1
5
+ TINY = 0
6
+
7
+ STRING = [MAJOR, MINOR, TINY].join('.')
8
+ end
9
+ end
@@ -0,0 +1 @@
1
+ require 'view/base'
@@ -0,0 +1,33 @@
1
+ module Reactive
2
+ module View
3
+
4
+ class Error < StandardError; end
5
+
6
+ class Base
7
+ class << self
8
+ attr_writer :view_paths
9
+ def view_paths
10
+ @view_paths ||= []
11
+ end
12
+
13
+ attr_writer :asset_paths
14
+ def asset_paths
15
+ @asset_paths ||= []
16
+ end
17
+
18
+ def handle_request(request)
19
+ Dispatcher.dispatch(request)
20
+ end
21
+
22
+ def asset_pathname_for(filename)
23
+ asset_paths.each do |path|
24
+ pathname = File.join(path, filename)
25
+ return pathname if File.file?(pathname)
26
+ end
27
+ end
28
+
29
+ end
30
+ end
31
+
32
+ end
33
+ end
@@ -0,0 +1,27 @@
1
+ Description:
2
+ Stubs out a new model. Pass the model name, either CamelCased or
3
+ under_scored, and an optional list of attribute pairs as arguments.
4
+
5
+ Attribute pairs are column_name:sql_type arguments specifying the
6
+ model's attributes. Timestamps are added by default, so you don't have to
7
+ specify them by hand as 'created_at:datetime updated_at:datetime'.
8
+
9
+ You don't have to think up every attribute up front, but it helps to
10
+ sketch out a few so you can start working with the model immediately.
11
+
12
+ This generates a model class in app/models, a unit test in test/unit,
13
+ a test fixture in test/fixtures/singular_name.yml, and a migration in
14
+ db/migrate.
15
+
16
+ Examples:
17
+ `./script/generate model account`
18
+
19
+ creates an Account model, test, fixture, and migration:
20
+ Model: app/models/account.rb
21
+ Test: test/unit/account_test.rb
22
+ Fixtures: test/fixtures/accounts.yml
23
+ Migration: db/migrate/XXX_add_accounts.rb
24
+
25
+ `./script/generate model post title:string body:text published:boolean`
26
+
27
+ creates a Post model with a string title, text body, and published flag.
@@ -0,0 +1,52 @@
1
+ require 'named_base_generator'
2
+
3
+ class ModelGenerator < Reactive::NamedBaseGenerator
4
+ default_options :skip_timestamps => false, :skip_migration => false, :skip_fixture => false
5
+
6
+ def initialize(runtime_args, runtime_options = {})
7
+ usage "Sorry, you disabled ActiveRecord in your config (see environment.rb). This generator is thus also disabled." unless defined? ActiveRecord
8
+ super
9
+ end
10
+
11
+ def manifest
12
+ record do |m|
13
+ # Check for class naming collisions.
14
+ m.class_collisions class_path, class_name, "#{class_name}Test"
15
+
16
+ # Model, test, and fixture directories.
17
+ m.directory File.join('app/models', class_path)
18
+ m.directory File.join('test/unit', class_path)
19
+ m.directory File.join('test/fixtures', class_path)
20
+
21
+ # Model class, unit test, and fixtures.
22
+ m.template 'model.rb', File.join('app/models', class_path, "#{file_name}.rb")
23
+ m.template 'unit_test.rb', File.join('test/unit', class_path, "#{file_name}_test.rb")
24
+
25
+ unless options[:skip_fixture]
26
+ m.template 'fixtures.yml', File.join('test/fixtures', "#{table_name}.yml")
27
+ end
28
+
29
+ unless options[:skip_migration]
30
+ m.migration_template 'migration.rb', 'db/migrate', :assigns => {
31
+ :migration_name => "Create#{class_name.pluralize.gsub(/::/, '')}"
32
+ }, :migration_file_name => "create_#{file_path.gsub(/\//, '_').pluralize}"
33
+ end
34
+ end
35
+ end
36
+
37
+ protected
38
+ def banner
39
+ "Usage: #{$0} #{spec.name} ModelName [field:type, field:type]"
40
+ end
41
+
42
+ def add_options!(opt)
43
+ opt.separator ''
44
+ opt.separator 'Options:'
45
+ opt.on("--skip-timestamps",
46
+ "Don't add timestamps to the migration file for this model") { |v| options[:skip_timestamps] = v }
47
+ opt.on("--skip-migration",
48
+ "Don't generate a migration file for this model") { |v| options[:skip_migration] = v }
49
+ opt.on("--skip-fixture",
50
+ "Don't generation a fixture file for this model") { |v| options[:skip_fixture] = v}
51
+ end
52
+ end
@@ -0,0 +1,19 @@
1
+ # Read about fixtures at http://ar.rubyonrails.org/classes/Fixtures.html
2
+
3
+ <% unless attributes.empty? -%>
4
+ one:
5
+ <% for attribute in attributes -%>
6
+ <%= attribute.name %>: <%= attribute.default %>
7
+ <% end -%>
8
+
9
+ two:
10
+ <% for attribute in attributes -%>
11
+ <%= attribute.name %>: <%= attribute.default %>
12
+ <% end -%>
13
+ <% else -%>
14
+ # one:
15
+ # column: value
16
+ #
17
+ # two:
18
+ # column: value
19
+ <% end -%>
@@ -0,0 +1,16 @@
1
+ class <%= migration_name %> < ActiveRecord::Migration
2
+ def self.up
3
+ create_table :<%= table_name %> do |t|
4
+ <% for attribute in attributes -%>
5
+ t.<%= attribute.type %> :<%= attribute.name %>
6
+ <% end -%>
7
+ <% unless options[:skip_timestamps] %>
8
+ t.timestamps
9
+ <% end -%>
10
+ end
11
+ end
12
+
13
+ def self.down
14
+ drop_table :<%= table_name %>
15
+ end
16
+ end
@@ -0,0 +1,2 @@
1
+ class <%= class_name %> < ActiveRecord::Base
2
+ end
@@ -0,0 +1,8 @@
1
+ require File.dirname(__FILE__) + '<%= '/..' * class_nesting_depth %>/../test_helper'
2
+
3
+ class <%= class_name %>Test < ActiveSupport::TestCase
4
+ # Replace this with your real tests.
5
+ def test_truth
6
+ assert true
7
+ end
8
+ end
@@ -0,0 +1,26 @@
1
+ Description:
2
+ Scaffolds an entire resource, from model and migration to controller and
3
+ views, along with a full test suite. The resource is ready to use as a
4
+ starting point for your CRUD resource-oriented application.
5
+ The generated actions are:
6
+ index, show, new, create, edit, update, delete, destroy
7
+
8
+ Pass the name of the model, either CamelCased or under_scored, as the first
9
+ argument, and an optional list of attribute pairs.
10
+
11
+ Attribute pairs are column_name:sql_type arguments specifying the
12
+ model's attributes. Timestamps are added by default, so you don't have to
13
+ specify them by hand as 'created_at:datetime updated_at:datetime'.
14
+
15
+ You don't have to think up every attribute up front, but it helps to
16
+ sketch out a few so you can start working with the resource immediately.
17
+
18
+ For example, `scaffold product name:string description:text price:decimal`
19
+ gives you a model with those three attributes, a controller that handles
20
+ the create/show/update/destroy, forms to create and edit your products,
21
+ and an index that lists them all.
22
+
23
+ Examples:
24
+ `./script/generate scaffold product` # no attributes, view will be anemic
25
+ `./script/generate scaffold product name:string description:text price:decimal`
26
+ `./script/generate scaffold purchase order_id:integer amount:decimal`
@@ -0,0 +1,75 @@
1
+ require 'named_base_generator'
2
+
3
+ class ScaffoldGenerator < Reactive::NamedBaseGenerator
4
+ default_options :skip_timestamps => false, :skip_migration => false
5
+
6
+ attr_reader :controller_name,
7
+ :controller_class_path,
8
+ :controller_file_path,
9
+ :controller_class_nesting,
10
+ :controller_class_nesting_depth,
11
+ :controller_class_name,
12
+ :controller_underscore_name,
13
+ :controller_singular_name,
14
+ :controller_plural_name
15
+ alias_method :controller_file_name, :controller_underscore_name
16
+ alias_method :controller_table_name, :controller_plural_name
17
+
18
+ def initialize(runtime_args, runtime_options = {})
19
+ super
20
+
21
+ @controller_name = @name.pluralize
22
+
23
+ base_name, @controller_class_path, @controller_file_path, @controller_class_nesting, @controller_class_nesting_depth = extract_modules(@controller_name)
24
+ @controller_class_name_without_nesting, @controller_underscore_name, @controller_plural_name = inflect_names(base_name)
25
+ @controller_singular_name=base_name.singularize
26
+ if @controller_class_nesting.empty?
27
+ @controller_class_name = @controller_class_name_without_nesting
28
+ else
29
+ @controller_class_name = "#{@controller_class_nesting}::#{@controller_class_name_without_nesting}"
30
+ end
31
+ end
32
+
33
+ def manifest
34
+ record do |m|
35
+ # Check for class naming collisions.
36
+ m.class_collisions(controller_class_path, "#{controller_class_name}Controller", "#{controller_class_name}Helper")
37
+ m.class_collisions(class_path, "#{class_name}")
38
+
39
+ # Controller, helper, views, and test directories.
40
+ m.directory(File.join('app/models', class_path))
41
+ m.directory(File.join('app/controllers', controller_class_path))
42
+ m.directory(File.join('app/helpers', controller_class_path))
43
+ m.directory(File.join('test/functional', controller_class_path))
44
+ m.directory(File.join('test/unit', class_path))
45
+
46
+ self.class.prepend_sources(Gem.loaded_specs.values.collect{|spec| RubiGen::PathFilteredSource.new(:GemPlugins, spec.full_gem_path, [:reactive, :test_unit]) })
47
+ m.dependency('model', [name] + @args, :collision => :skip) if defined? ActiveRecord
48
+ m.dependency('view', [name] + @args, :collision => :ask)
49
+
50
+ m.template('controller.rb', File.join('app/controllers', controller_class_path, "#{controller_file_name}_controller.rb"))
51
+ m.template('functional_test.rb', File.join('test/functional', controller_class_path, "#{controller_file_name}_controller_test.rb"))
52
+ m.template('helper.rb', File.join('app/helpers', controller_class_path, "#{controller_file_name}_helper.rb"))
53
+
54
+ end
55
+ end
56
+
57
+ protected
58
+ # Override with your own usage banner.
59
+ def banner
60
+ "Usage: #{$0} scaffold ModelName [field:type, field:type]"
61
+ end
62
+
63
+ def add_options!(opt)
64
+ opt.separator ''
65
+ opt.separator 'Options:'
66
+ opt.on("--skip-timestamps",
67
+ "Don't add timestamps to the migration file for this model") { |v| options[:skip_timestamps] = v }
68
+ opt.on("--skip-migration",
69
+ "Don't generate a migration file for this model") { |v| options[:skip_migration] = v }
70
+ end
71
+
72
+ def model_name
73
+ class_name.demodulize
74
+ end
75
+ end