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,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