reactive 0.1.0

Sign up to get free protection for your applications and to get access to all the features.
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,46 @@
1
+
2
+ module Reactive
3
+
4
+ class Dispatcher
5
+ class << self
6
+ def dispatch(request)
7
+ if request == :init
8
+ new.initial_dispatch
9
+ else
10
+ new.dispatch(request)
11
+ end
12
+ end
13
+ end
14
+
15
+ def dispatch(request)
16
+ controller = recognize(request.params)
17
+ response = Response.new
18
+ controller.process(request, response)
19
+
20
+ handle_response(response)
21
+ end
22
+
23
+ def recognize(params)
24
+ "#{params[:controller].camelize}Controller".constantize
25
+ end
26
+
27
+ def handle_response(response)
28
+ if response.redirected_to
29
+ # This recursive call is intended, do not refactor in a loop. This way when a redirect cycle occurs, it will end up in a stack overflow
30
+ dispatch(Request.new(response.redirected_to))
31
+ else
32
+ response.result # This does nothing, Should we treat the result in some ways? We already have Exceptions for handling errors, so... what should the result be?
33
+ end
34
+ end
35
+
36
+ def initial_dispatch
37
+ controller_class = ApplicationController
38
+ # controller_class.class_eval { helper :application } Not need, already done in initializer.rb
39
+ response = Response.new
40
+ controller_class.process(Request.new, response, :perform_init)
41
+
42
+ handle_response(response)
43
+ end
44
+ end
45
+
46
+ end
@@ -0,0 +1,40 @@
1
+ require 'optparse'
2
+
3
+ module Reactive
4
+ class GeneratedAttribute
5
+ attr_accessor :name, :type, :column
6
+
7
+ def initialize(name, type)
8
+ @name, @type = name, type.to_sym
9
+ @column = ActiveRecord::ConnectionAdapters::Column.new(name, nil, @type)
10
+ end
11
+
12
+ def field_type
13
+ @field_type ||= case type
14
+ when :integer, :float, :decimal then :text_field
15
+ when :datetime, :timestamp, :time then :datetime_select
16
+ when :date then :date_select
17
+ when :string then :text_field
18
+ when :text then :text_area
19
+ when :boolean then :check_box
20
+ else
21
+ :text_field
22
+ end
23
+ end
24
+
25
+ def default
26
+ @default ||= case type
27
+ when :integer then 1
28
+ when :float then 1.5
29
+ when :decimal then "9.99"
30
+ when :datetime, :timestamp, :time then Time.now.to_s(:db)
31
+ when :date then Date.today.to_s(:db)
32
+ when :string then "MyString"
33
+ when :text then "MyText"
34
+ when :boolean then false
35
+ else
36
+ ""
37
+ end
38
+ end
39
+ end
40
+ end
@@ -0,0 +1,425 @@
1
+ require 'activesupport'
2
+
3
+ REACTIVE_ENV = (ENV['REACTIVE_ENV'] || 'development').dup unless defined?(REACTIVE_ENV)
4
+
5
+ module Reactive
6
+ class NoViewProvider < StandardError; end
7
+
8
+ class Initializer
9
+ attr_reader :configuration
10
+
11
+ def self.run(command = :process, configuration = Configuration.new)
12
+ yield configuration if block_given?
13
+ initializer = new(configuration)
14
+ initializer.send(command)
15
+ initializer
16
+ end
17
+
18
+ def initialize(config)
19
+ @configuration = config
20
+ end
21
+
22
+ def process
23
+ set_load_paths
24
+
25
+ set_autoload_paths
26
+ load_environment
27
+ require_frameworks
28
+ require_view_provider
29
+
30
+ #load_or_require_plugins
31
+
32
+ #initialize_encoding
33
+ initialize_database
34
+ initialize_logger
35
+ initialize_framework_logging
36
+ initialize_framework_views
37
+ initialize_dependency_mechanism
38
+ #initialize_whiny_nils
39
+ initialize_framework_settings
40
+
41
+ after_initialize
42
+ load_application_initializers
43
+
44
+ # Observers are loaded after plugins in case Observers or observed models are modified by plugins.
45
+ load_observers
46
+ end
47
+
48
+ # Set the <tt>$LOAD_PATH</tt> based on the value of
49
+ # Configuration#load_paths. Duplicates are removed.
50
+ def set_load_paths
51
+ load_paths = configuration.load_paths + configuration.framework_paths
52
+ load_paths.reverse_each { |dir| $LOAD_PATH.unshift(dir) if File.directory?(dir) }
53
+ $LOAD_PATH.uniq!
54
+ end
55
+
56
+ # Set the paths from which Reactive will automatically load source files, and
57
+ # the load_once paths.
58
+ def set_autoload_paths
59
+ Dependencies.load_paths = configuration.load_paths.uniq
60
+ Dependencies.load_once_paths = configuration.load_once_paths.uniq
61
+
62
+ extra = Dependencies.load_once_paths - Dependencies.load_paths
63
+ unless extra.empty?
64
+ abort <<-end_error
65
+ load_once_paths must be a subset of the load_paths.
66
+ Extra items in load_once_paths: #{extra * ','}
67
+ end_error
68
+ end
69
+
70
+ # Freeze the arrays so future modifications will fail rather than do nothing mysteriously
71
+ configuration.load_once_paths.freeze
72
+ end
73
+
74
+ # Requires all frameworks specified by the Configuration#frameworks list
75
+ # By default, Controller, View, Dispatcher and ActiveRecord are loaded.
76
+ def require_frameworks
77
+ configuration.frameworks.each { |framework| require(framework.to_s) }
78
+ end
79
+
80
+ def require_view_provider
81
+ provider = configuration.view_provider
82
+ raise NoViewProvider, "You need to setup a view provider. This is usually done in config/environment.rb with config.view_provider = :your_view_provider" unless provider
83
+ if provider.is_a?(Class)
84
+ @view_provider = provider
85
+ else
86
+ provider_name = provider.to_s
87
+ long_name = (provider_name =~ /^reactive_view_/i ? provider_name : "reactive_view_#{provider_name}")
88
+ short_name = /^reactive_view_(.+)/i.match(long_name)[1]
89
+
90
+ require long_name
91
+ @view_provider = "Reactive::View::#{short_name.camelize}".constantize
92
+ end
93
+ end
94
+
95
+ # Loads the environment specified by Configuration#environment_path, which
96
+ # is typically one of development, test, or production.
97
+ def load_environment
98
+ return if @environment_loaded
99
+ @environment_loaded = true
100
+
101
+ config = configuration
102
+ constants = self.class.constants
103
+
104
+ eval(IO.read(configuration.global_environment_path), binding, configuration.global_environment_path) if File.exists?(configuration.global_environment_path)
105
+ eval(IO.read(configuration.environment_path), binding, configuration.environment_path) if File.exists?(configuration.environment_path)
106
+
107
+ (self.class.constants - constants).each do |const|
108
+ Object.const_set(const, self.class.const_get(const))
109
+ end
110
+ end
111
+
112
+ def load_observers
113
+ if configuration.frameworks.include?(:active_record)
114
+ ActiveRecord::Base.instantiate_observers
115
+ end
116
+ end
117
+
118
+ # TODO: initialize_encoding
119
+
120
+ # This initialization routine does nothing unless <tt>:active_record</tt>
121
+ # is one of the frameworks to load (Configuration#frameworks). If it is,
122
+ # this sets the database configuration from Configuration#database_configuration
123
+ # and then establishes the connection.
124
+ def initialize_database
125
+ if configuration.frameworks.include?(:active_record)
126
+ ActiveRecord::Base.configurations = configuration.database_configuration
127
+ ActiveRecord::Base.establish_connection(configuration.environment)
128
+ end
129
+ end
130
+
131
+ # If the +REACTIVE_DEFAULT_LOGGER+ constant is already set, this initialization
132
+ # routine does nothing. If the constant is not set, and Configuration#logger
133
+ # is not +nil+, this also does nothing. Otherwise, a new logger instance
134
+ # is created at Configuration#log_path, with a default log level of
135
+ # Configuration#log_level.
136
+ #
137
+ # If the log could not be created, the log will be set to output to
138
+ # +STDERR+, with a log level of +WARN+.
139
+ def initialize_logger
140
+ # if the environment has explicitly defined a logger, use it
141
+ return if defined?(REACTIVE_DEFAULT_LOGGER)
142
+
143
+ unless logger = configuration.logger
144
+ begin
145
+ logger = ActiveSupport::BufferedLogger.new(configuration.log_path)
146
+ logger.level = ActiveSupport::BufferedLogger.const_get(configuration.log_level.to_s.upcase)
147
+ logger.auto_flushing = false if configuration.environment == "production"
148
+ rescue StandardError =>e
149
+ logger = ActiveSupport::BufferedLogger.new(STDERR)
150
+ logger.level = ActiveSupport::BufferedLogger::WARN
151
+ logger.warn(
152
+ "Reactive Error: Unable to access log file. Please ensure that #{configuration.log_path} exists and is chmod 0666. " +
153
+ "The log level has been raised to WARN and the output directed to STDERR until the problem is fixed."
154
+ )
155
+ end
156
+ end
157
+
158
+ silence_warnings { Object.const_set "REACTIVE_DEFAULT_LOGGER", logger }
159
+ end
160
+
161
+ # Sets the logger for ActiveRecord and ActionMailer
162
+ # (but only for those frameworks that are to be loaded). If the framework's
163
+ # logger is already set, it is not changed, otherwise it is set to use
164
+ # +REACTIVE_DEFAULT_LOGGER+.
165
+ def initialize_framework_logging
166
+ for framework in ([:controller, :active_record, :action_mailer ] & configuration.frameworks)
167
+ framework_constantize(framework).const_get("Base").logger ||= REACTIVE_DEFAULT_LOGGER
168
+ end
169
+ end
170
+
171
+ # Sets +Reactive::View::Base#view_paths+ and +ActionMailer::Base#template_root+
172
+ # (but only for those frameworks that are to be loaded). If the framework's
173
+ # paths have already been set, it is not changed, otherwise it is
174
+ # set to use Configuration#view_path.
175
+ def initialize_framework_views
176
+ ActionMailer::Base.template_root ||= configuration.view_path if configuration.frameworks.include?(:action_mailer)
177
+
178
+ raise NoViewProvider, "You need to setup a view provider. This is usually done in config/environment.rb with config.view_provider = :your_view_provider" unless @view_provider
179
+ # initialize_framework_settings([@view_provider])
180
+
181
+ Controller::Base.template_class = @view_provider::Base.template_class
182
+ Controller::Base.view_paths = View::Base.view_paths = [configuration.view_path] if View::Base.view_paths.empty?
183
+ Controller::Base.class_eval { helper :application }
184
+ View::Base.asset_paths = [configuration.asset_path] if View::Base.asset_paths.empty?
185
+ end
186
+
187
+ # Sets the dependency loading mechanism based on the value of
188
+ # Configuration#cache_classes.
189
+ def initialize_dependency_mechanism
190
+ Dependencies.mechanism = configuration.cache_classes ? :require : :load
191
+ end
192
+
193
+ # Initializes framework-specific settings for each of the loaded frameworks
194
+ # (Configuration#frameworks). The available settings map to the accessors
195
+ # on each of the corresponding Base classes.
196
+ def initialize_framework_settings(frameworks = configuration.frameworks)
197
+ (frameworks - [:dispatcher]).each do |framework|
198
+ base_class = (framework.is_a?(Module) ? framework : framework_constantize(framework)).const_get("Base")
199
+
200
+ configuration.send(framework).each do |setting, value|
201
+ base_class.send("#{setting}=", value)
202
+ end
203
+ end
204
+ end
205
+
206
+ # Fires the user-supplied after_initialize block (Configuration#after_initialize)
207
+ def after_initialize
208
+ configuration.after_initialize_blocks.each do |block|
209
+ block.call
210
+ end
211
+ end
212
+
213
+ def load_application_initializers
214
+ Dir["#{configuration.root_path}/config/initializers/**/*.rb"].sort.each do |initializer|
215
+ load(initializer)
216
+ end
217
+ end
218
+
219
+ protected
220
+ def framework_constantize(framework)
221
+ framework.to_s.camelize.constantize
222
+ rescue NameError
223
+ "reactive/#{framework}".camelize.constantize
224
+ end
225
+
226
+ end
227
+
228
+ class Configuration
229
+ # The application's base directory.
230
+ attr_reader :root_path
231
+
232
+ # A stub for setting options on Reactive::Controller::Base
233
+ attr_accessor :controller
234
+
235
+ # A stub for setting options on Reactive::View::Base
236
+ attr_accessor :view
237
+
238
+ # A stub for setting options on ActionMailer::Base
239
+ attr_accessor :action_mailer
240
+
241
+ # A stub for setting options on ActiveRecord::Base
242
+ attr_accessor :active_record
243
+
244
+ # A stub for setting options on ActiveRecord::Base
245
+ attr_accessor :active_resource
246
+
247
+ # Whether or not classes should be cached (set to false if you want
248
+ # application classes to be reloaded on each event)
249
+ attr_accessor :cache_classes
250
+
251
+ # The view provider name
252
+ attr_accessor :view_provider
253
+
254
+ # The path to the database configuration file to use. (Defaults to
255
+ # <tt>config/database.yml</tt>.)
256
+ attr_accessor :database_configuration_file
257
+
258
+ # The list of framework components that should be loaded. (Defaults
259
+ # to <tt>:active_record</tt>, <tt>:action_mailer</tt>, and
260
+ # <tt>:active_resource</tt>).
261
+ attr_accessor :frameworks
262
+
263
+ # An array of additional paths to prepend to the load path. By default,
264
+ # all +app+, +lib+, +vendor+ and mock paths are included in this list.
265
+ attr_accessor :load_paths
266
+
267
+ # An array of paths from which Reactive will automatically load from only once.
268
+ # All elements of this array must also be in +load_paths+.
269
+ attr_accessor :load_once_paths
270
+
271
+ # The log level to use for the default Reactive logger. In production mode,
272
+ # this defaults to <tt>:info</tt>. In development mode, it defaults to
273
+ # <tt>:debug</tt>.
274
+ attr_accessor :log_level
275
+
276
+ # The path to the log file to use. Defaults to log/#{environment}.log
277
+ # (e.g. log/development.log or log/production.log).
278
+ attr_accessor :log_path
279
+
280
+ # The specific logger to use. By default, a logger will be created and
281
+ # initialized using #log_path and #log_level, but a programmer may
282
+ # specifically set the logger to use via this accessor and it will be
283
+ # used directly.
284
+ attr_accessor :logger
285
+
286
+ # Some paths
287
+ attr_accessor :controller_path
288
+ attr_accessor :model_path
289
+ attr_accessor :view_path
290
+ attr_accessor :asset_path
291
+
292
+
293
+ def initialize
294
+ set_root_path!
295
+
296
+ self.frameworks = default_frameworks
297
+ self.load_paths = default_load_paths
298
+ self.load_once_paths = []
299
+ self.log_path = default_log_path
300
+ self.log_level = default_log_level
301
+ self.view_path = default_view_path
302
+ self.controller_path = default_controller_path
303
+ self.model_path = default_model_path
304
+ self.asset_path = default_asset_path
305
+ self.database_configuration_file = default_database_configuration_file
306
+ self.cache_classes = default_cache_classes
307
+
308
+ for framework in default_frameworks
309
+ self.send("#{framework}=", OrderedOptions.new)
310
+ end
311
+ end
312
+
313
+ def set_root_path!
314
+ @root_path = File.expand_path(::REACTIVE_ROOT)
315
+ end
316
+
317
+ # Loads and returns the contents of the #database_configuration_file.
318
+ def database_configuration
319
+ YAML::load(IO.read(database_configuration_file))
320
+ end
321
+
322
+ # The path to the current environment's file (development.rb, etc.). By
323
+ # default the file is at <tt>config/environments/#{environment}.rb</tt>.
324
+ def environment_path
325
+ "#{root_path}/config/environments/#{environment}.rb"
326
+ end
327
+
328
+ # The path to the global environment's file. By
329
+ # default the file is at <tt>config/environment.rb</tt>.
330
+ def global_environment_path
331
+ "#{root_path}/config/environment.rb"
332
+ end
333
+
334
+ # Return the currently selected environment. By default, it returns the
335
+ # value of the +REACTIVE_ENV+ constant.
336
+ def environment
337
+ ::REACTIVE_ENV
338
+ end
339
+
340
+ # Adds a block which will be executed after rails has been fully initialized.
341
+ # Useful for per-environment configuration which depends on the framework being
342
+ # fully initialized.
343
+ def after_initialize(&after_initialize_block)
344
+ after_initialize_blocks << after_initialize_block if after_initialize_block
345
+ end
346
+
347
+ # Returns the blocks added with Configuration#after_initialize
348
+ def after_initialize_blocks
349
+ @after_initialize_blocks ||= []
350
+ end
351
+
352
+ def framework_paths
353
+ paths = %w(activesupport/lib)
354
+ # TODO: Add the provider path here
355
+
356
+ [:active_record, :action_mailer, :active_resource, :action_web_service].each do |framework|
357
+ paths << "#{framework.to_s.gsub('_', '')}/lib" if frameworks.include? framework
358
+ end
359
+
360
+ paths.select { |dir| File.directory?(dir) }
361
+ end
362
+
363
+ protected
364
+ # Dummy setter. Because dispatcher is in the frameworks list but has no Base class and thus no configuration (at least for now ;-)
365
+ def dispatcher=(v)
366
+ end
367
+
368
+ def default_log_path
369
+ File.join(root_path, 'log', "#{environment}.log")
370
+ end
371
+
372
+ def default_log_level
373
+ environment == 'production' ? :info : :debug
374
+ end
375
+
376
+ def default_frameworks
377
+ [ :controller, :view, :dispatcher, :active_record ]
378
+ end
379
+
380
+ def default_load_paths
381
+ %w(
382
+ app
383
+ app/models
384
+ app/controllers
385
+ app/helpers
386
+ app/services
387
+ config
388
+ lib
389
+ ).map { |dir| "#{root_path}/#{dir}" }.select { |dir| File.directory?(dir) }
390
+ end
391
+
392
+ def default_database_configuration_file
393
+ File.join(root_path, 'config', 'database.yml')
394
+ end
395
+
396
+ # Default controller files load path. This can be changed in configuration.
397
+ def default_controller_path
398
+ @controller_path ||= File.join(root_path, 'app', 'controllers')
399
+ end
400
+
401
+ # Default model files load path. This can be changed in configuration.
402
+ def default_model_path
403
+ @model_path ||= File.join(root_path, 'app', 'models')
404
+ end
405
+
406
+ # Default view files load path. This can be changed in configuration.
407
+ def default_view_path
408
+ @view_path ||= File.join(root_path, 'app', 'views')
409
+ end
410
+
411
+ # Default asset files load path. This can be changed in configuration.
412
+ def default_asset_path
413
+ @asset_path ||= File.join(root_path, 'assets')
414
+ end
415
+
416
+ def default_dependency_mechanism
417
+ :load
418
+ end
419
+
420
+ def default_cache_classes
421
+ false
422
+ end
423
+
424
+ end
425
+ end