qwirk 0.2.1 → 0.2.2

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 (50) hide show
  1. data/History.md +5 -0
  2. data/lib/qwirk.rb +3 -2
  3. data/lib/qwirk/adapter/base/expanding_worker_config.rb +1 -1
  4. data/lib/qwirk/adapter/base/worker_config.rb +1 -1
  5. data/lib/qwirk/adapter/in_memory/reply_queue.rb +1 -0
  6. data/lib/qwirk/adapter_factory.rb +9 -2
  7. data/lib/qwirk/base_worker.rb +1 -1
  8. data/lib/qwirk/manager.rb +1 -1
  9. data/lib/qwirk/publish_handle.rb +9 -4
  10. data/lib/qwirk/publisher.rb +1 -1
  11. data/lib/qwirk/remote.rb +62 -0
  12. data/lib/qwirk/remote/client.rb +71 -0
  13. data/lib/qwirk/remote/worker.rb +33 -0
  14. data/lib/qwirk/task.rb +2 -2
  15. data/test/dummy/README.rdoc +261 -0
  16. data/test/dummy/Rakefile +7 -0
  17. data/test/dummy/app/assets/javascripts/application.js +15 -0
  18. data/test/dummy/app/assets/stylesheets/application.css +13 -0
  19. data/test/dummy/app/controllers/application_controller.rb +3 -0
  20. data/test/dummy/app/helpers/application_helper.rb +2 -0
  21. data/test/dummy/app/views/layouts/application.html.erb +14 -0
  22. data/test/dummy/config.ru +4 -0
  23. data/test/dummy/config/application.rb +59 -0
  24. data/test/dummy/config/boot.rb +10 -0
  25. data/test/dummy/config/database.yml +25 -0
  26. data/test/dummy/config/environment.rb +5 -0
  27. data/test/dummy/config/environments/development.rb +37 -0
  28. data/test/dummy/config/environments/production.rb +67 -0
  29. data/test/dummy/config/environments/test.rb +37 -0
  30. data/test/dummy/config/initializers/backtrace_silencers.rb +7 -0
  31. data/test/dummy/config/initializers/inflections.rb +15 -0
  32. data/test/dummy/config/initializers/mime_types.rb +5 -0
  33. data/test/dummy/config/initializers/secret_token.rb +7 -0
  34. data/test/dummy/config/initializers/session_store.rb +8 -0
  35. data/test/dummy/config/initializers/wrap_parameters.rb +14 -0
  36. data/test/dummy/config/locales/en.yml +5 -0
  37. data/test/dummy/config/routes.rb +4 -0
  38. data/test/dummy/public/404.html +26 -0
  39. data/test/dummy/public/422.html +26 -0
  40. data/test/dummy/public/500.html +25 -0
  41. data/test/dummy/public/favicon.ico +0 -0
  42. data/test/dummy/script/rails +6 -0
  43. data/test/fixtures/qwirk/jobs.yml +11 -0
  44. data/test/functional/qwirk/jobs_controller_test.rb +9 -0
  45. data/test/integration/navigation_test.rb +7 -0
  46. data/test/support/integration_case.rb +5 -0
  47. data/test/test_helper.rbold +22 -0
  48. data/test/unit/helpers/qwirk/jobs_helper_test.rb +6 -0
  49. data/test/unit/qwirk/job_test.rb +9 -0
  50. metadata +77 -4
data/History.md CHANGED
@@ -1,6 +1,11 @@
1
1
  Qwirk Changelog
2
2
  =====================
3
3
 
4
+ 0.2.2
5
+ -----
6
+
7
+ - Fix bug that sends negative value for timeout to ConditionVariable#wait.
8
+
4
9
  0.2.1
5
10
  -----
6
11
 
@@ -12,7 +12,7 @@ module Qwirk
12
12
  @@hash = {}
13
13
 
14
14
  class MyBean
15
- include Rumx::Bean
15
+ include ::Rumx::Bean
16
16
 
17
17
  # These are actually AdapterFactory's but presenting as adapters to the user.
18
18
  bean_attr_reader :adapters, :hash, 'Adapters', :hash_type => :bean
@@ -25,7 +25,7 @@ module Qwirk
25
25
  def self.config=(config)
26
26
  #if config.has_key?(:adapter)
27
27
  @@config = config
28
- Rumx::Bean.root.bean_add_child(DEFAULT_NAME, MyBean.new(@@hash))
28
+ ::Rumx::Bean.root.bean_add_child(DEFAULT_NAME, MyBean.new(@@hash))
29
29
  end
30
30
 
31
31
  def self.environment=(environment)
@@ -109,5 +109,6 @@ require 'qwirk/remote_exception'
109
109
  require 'qwirk/task'
110
110
  require 'qwirk/worker'
111
111
  require 'qwirk/reply_worker'
112
+ require 'qwirk/remote'
112
113
 
113
114
  require 'qwirk/engine' if defined?(Rails)
@@ -48,7 +48,7 @@ module Qwirk
48
48
  @min_count = 1 if @min_count == 0 && new_max_count > 0
49
49
  deleted_workers = []
50
50
  @worker_mutex.synchronize do
51
- @timer ||= Rumx::Beans::TimerAndError.new
51
+ @timer ||= ::Rumx::Beans::TimerAndError.new
52
52
  if @workers.size > new_max_count
53
53
  deleted_workers = @workers[new_max_count..-1]
54
54
  deleted_workers.each { |worker| worker.stop }
@@ -4,7 +4,7 @@ module Qwirk
4
4
  module Adapter
5
5
  module Base
6
6
  class WorkerConfig
7
- include Rumx::Bean
7
+ include ::Rumx::Bean
8
8
 
9
9
  # Make explicit the instance variables available to the derived adapter classes
10
10
  attr_reader :adapter_factory, :name, :manager, :worker_class, :default_options, :options,
@@ -13,6 +13,7 @@ module Qwirk
13
13
  def timeout_read(timeout)
14
14
  @outstanding_hash_mutex.synchronize do
15
15
  return @array.shift unless @array.empty?
16
+ return nil unless timeout > 0
16
17
  timed_read_condition_wait(timeout)
17
18
  return @array.shift
18
19
  end
@@ -2,7 +2,7 @@ module Qwirk
2
2
 
3
3
  # Defines the queuing adapter. Currently, only JMS and InMemory.
4
4
  class AdapterFactory
5
- include Rumx::Bean
5
+ include ::Rumx::Bean
6
6
 
7
7
  attr_reader :key, :config, :log_times, :adapter_info, :worker_config_class, :manager
8
8
 
@@ -25,11 +25,12 @@ module Qwirk
25
25
  key = key.to_sym
26
26
  @publisher_class, @worker_config_class, block = @@adapter_hash[key]
27
27
  raise "No adapter matching #{key}" unless @publisher_class
28
+ self.remote = config.delete(:remote)
28
29
  @adapter_info = block.call(config) if block
29
30
  end
30
31
 
31
32
  def create_publisher(options={})
32
- @publisher_parent ||= Rumx::Beans::Folder.new
33
+ @publisher_parent ||= ::Rumx::Beans::Folder.new
33
34
  publisher = Publisher.new(self, @config.merge(options))
34
35
  @publisher_parent.bean_add_child(publisher.to_s, publisher)
35
36
  return publisher
@@ -48,5 +49,11 @@ module Qwirk
48
49
  def in_process?
49
50
  @worker_config_class.in_process?(@config)
50
51
  end
52
+
53
+ def remote=(remote_options)
54
+ # If remote was just set to true, then create an empty options hash for it
55
+ remote_options = {} if remote_options == true
56
+ Remote.setup(self, remote_options) if remote_options
57
+ end
51
58
  end
52
59
  end
@@ -55,7 +55,7 @@ module Qwirk
55
55
  end
56
56
 
57
57
  def self.included(base)
58
- Rumx::Bean.included(base)
58
+ ::Rumx::Bean.included(base)
59
59
  base.extend(ClassMethods)
60
60
  if base.kind_of?(Class)
61
61
  @worker_classes ||= []
@@ -5,7 +5,7 @@ require 'rumx'
5
5
 
6
6
  module Qwirk
7
7
  class Manager
8
- include Rumx::Bean
8
+ include ::Rumx::Bean
9
9
  attr_reader :env, :worker_configs, :name
10
10
 
11
11
  bean_attr_accessor :poll_time, :float, 'How often the manager should poll the workers for their status for use by :idle_worker_timeout and :max_read_threshold'
@@ -82,6 +82,7 @@ module Qwirk
82
82
 
83
83
  def read_single_response(consumer, timeout)
84
84
  leftover_timeout = @start + timeout - Time.now
85
+ leftover_timeout = 0 if leftover_timeout < 0
85
86
  return consumer.timeout_read(leftover_timeout)
86
87
  end
87
88
 
@@ -112,6 +113,7 @@ module Qwirk
112
113
  @message_hash = {}
113
114
  @timeout_hash = {}
114
115
  @exception_hash = {}
116
+ @default_message_block = nil
115
117
  @default_timeout_block = nil
116
118
  @default_exception_block = nil
117
119
  @done_array = []
@@ -123,8 +125,11 @@ module Qwirk
123
125
  end
124
126
 
125
127
  def on_message(*names, &block)
126
- raise 'Must explicitly define all message handlers so we know that we\'re done' if names.empty?
127
- names.each {|name| @message_hash[name] = block}
128
+ if names.empty?
129
+ @default_message_block = block
130
+ else
131
+ names.each {|name| @message_hash[name] = block}
132
+ end
128
133
  end
129
134
 
130
135
  def on_timeout(*names, &block)
@@ -147,13 +152,13 @@ module Qwirk
147
152
  def make_message_call(name, obj)
148
153
  # Give the client access to the name
149
154
  @name = name
150
- block = @message_hash[name]
155
+ block = @message_hash[name] || @default_message_block
151
156
  block.call(obj) if block
152
157
  @done_array << name
153
158
  end
154
159
 
155
160
  def done?
156
- (@message_hash.keys - @done_array).empty?
161
+ !@default_message_block && (@message_hash.keys - @done_array).empty?
157
162
  end
158
163
 
159
164
  def make_timeout_calls
@@ -1,7 +1,7 @@
1
1
  # Protocol independent class to handle Publishing
2
2
  module Qwirk
3
3
  class Publisher
4
- include Rumx::Bean
4
+ include ::Rumx::Bean
5
5
 
6
6
  #attr_reader :producer_options, :persistent, :reply_queue
7
7
  attr_reader :response_options, :impl, :marshaler
@@ -0,0 +1,62 @@
1
+ module Qwirk
2
+ module Remote
3
+
4
+ # Setup the remote worker with a unique name with which to identify this particular process.
5
+ # This defaults to the simple hostname (minus the domain name) which can be used if their is only
6
+ # one qwirk_manager running on this host, otherwise it should be some combination of the hostname
7
+ # and process name or anything that would uniquely identify the process/host combination.
8
+ # Options:
9
+ # name - overrides the default name for this process/host combination.
10
+ # topic_name - name of the topic. This should be the same for all process/host combinations and
11
+ # defaults to 'remote'
12
+ def self.setup(adapter_factory, options={})
13
+ require 'qwirk/remote/client'
14
+ require 'qwirk/remote/worker'
15
+
16
+ options = options.dup
17
+ @@adapter_factory = adapter_factory
18
+ @@name = options.delete(:name) || default_name
19
+ @@topic_name = options.delete(:topic_name) || 'remote'
20
+ @@queue_name = self.queue_name(@@name)
21
+ @@root_bean_name = options.delete(:root_bean_name) || @@topic_name
22
+ default_options = {:min_count => 1, :max_count => 1}
23
+ Worker.define_configs(
24
+ "TRemote_#{@@name}" => default_options.merge(:topic_name => @@topic_name).merge(options),
25
+ "QRemote_#{@@name}" => default_options.merge(:queue_name => @@queue_name).merge(options)
26
+ )
27
+ client = Client.new(adapter_factory, options)
28
+ @@root_bean = Rumx::Bean.add_root(@@root_bean_name, ::Rumx::Beans::Folder.new)
29
+ @@root_bean.bean_add_child(:client, client)
30
+ end
31
+
32
+ def self.adapter_factory
33
+ @@adapter_factory
34
+ end
35
+
36
+ def self.name
37
+ @@name
38
+ end
39
+
40
+ def self.topic_name
41
+ @@topic_name
42
+ end
43
+
44
+ def self.queue_name(name)
45
+ "Remote_#{name}"
46
+ end
47
+
48
+ def self.root_bean
49
+ @@root_bean
50
+ end
51
+
52
+ def self.default_name
53
+ require 'socket'
54
+ name = Socket.gethostname
55
+ name.sub(/\..*/, '')
56
+ end
57
+
58
+ def self.remote_name(worker_name)
59
+ worker_name.sub(/^[QT]Remote_/, '')
60
+ end
61
+ end
62
+ end
@@ -0,0 +1,71 @@
1
+ module Qwirk
2
+ module Remote
3
+ class Client
4
+ include ::Rumx::Bean
5
+
6
+ bean_attr_reader :servers, :hash, 'Remote servers', :hash_type => :bean
7
+ bean_attr_accessor :timeout, :float, 'Timeout for individual calls to remote servers'
8
+
9
+ bean_operation :refresh, :string, 'Query all the remote servers for their current state', [
10
+ [ :timeout, :float, 'How long to wait for all the servers to respond', 10.0]
11
+ ]
12
+
13
+ def initialize(adapter_factory, options={})
14
+ @adapter_factory = adapter_factory
15
+ @options = options
16
+ @servers = {}
17
+ @timeout = 10.0
18
+ end
19
+
20
+ def refresh(timeout)
21
+ new_servers = {}
22
+ success_servers = []
23
+ failure_servers = []
24
+ publisher = Qwirk::Publisher.new(@adapter_factory, :topic_name => Qwirk::Remote.topic_name, :marshal => :bson, :ttl => timeout, :response => true)
25
+ publisher.publish(:command => 'serialize').read_response(timeout) do |response|
26
+ response.on_message do |hash|
27
+ remote_name = Remote.remote_name(response.name)
28
+ new_servers[remote_name] = ::Rumx::RemoteBean.new(hash, self, remote_name)
29
+ success_servers << remote_name
30
+ end
31
+ response.on_remote_exception do |e|
32
+ remote_name = Remote.remote_name(response.name)
33
+ new_servers[remote_name] = ::Rumx::Beans::Message.new(e.message)
34
+ failure_servers << remote_name
35
+ end
36
+ end
37
+ @servers = new_servers
38
+ answer = ''
39
+ answer = "Success for #{success_servers.inspect}" unless success_servers.empty?
40
+ unless failure_servers.empty?
41
+ answer += ', ' unless answer.empty?
42
+ answer += 'Failure for #{failure_servers.inspect}'
43
+ end
44
+ return answer
45
+ end
46
+
47
+ def run_operation(ancestry, operation, argument_hash, remote_name)
48
+ puts "In run_operation"
49
+ value = remote_call(remote_name, :command => 'operation', :operation_name => operation.name, :ancestry => ancestry, :argument_hash => argument_hash)
50
+ return operation.type.string_to_value(value['value'])
51
+ end
52
+
53
+ def set_attributes(ancestry, params, remote_name)
54
+ puts "In set_attributes"
55
+ remote_call(remote_name, :command => 'attributes', :ancestry => ancestry, :params => params)
56
+ end
57
+
58
+ #######
59
+ private
60
+ #######
61
+
62
+ def remote_call(remote_name, request)
63
+ publisher = Qwirk::Publisher.new(@adapter_factory, :queue_name => Remote.queue_name(remote_name), :marshal => :bson, :ttl => @timeout, :response => true)
64
+ handle = publisher.publish(request)
65
+ response = handle.read_response(timeout)
66
+ raise Timeout::Error if handle.timeout?
67
+ return response
68
+ end
69
+ end
70
+ end
71
+ end
@@ -0,0 +1,33 @@
1
+ module Qwirk
2
+ module Remote
3
+ class Worker
4
+ include Qwirk::ReplyWorker
5
+
6
+ response :marshal => :bson
7
+
8
+ #config_accessor :update_threshold, :integer, 'Threshold age in seconds where a new call will be made'
9
+
10
+ # Process incoming inquiries
11
+ def request(hash)
12
+ case command = hash['command']
13
+ when 'serialize'
14
+ ::Rumx::Bean.root.bean_to_remote_hash
15
+ when 'operation'
16
+ bean, operation, value = ::Rumx::Bean.run_operation(hash['ancestry'], hash['operation_name'], hash['argument_hash'])
17
+ puts "operation returned #{value}"
18
+ raise "Invalid operation ancestry = #{hash['ancestry'].inspect} operation=#{hash['operation_name'].inspect}" unless bean
19
+ # Allow bson to handle it
20
+ { :value => value }
21
+ when 'attributes'
22
+ bean = ::Rumx::Bean.find(hash['ancestry'])
23
+ raise "Invalid bean ancestry #{hash['ancestry'].inspect}" unless bean
24
+ attributes = bean.bean_set_and_get_attributes(hash['params'])
25
+ puts "attributes returned #{attributes.inspect}"
26
+ attributes
27
+ else
28
+ raise "Invalid command: #{command.inspect}"
29
+ end
30
+ end
31
+ end
32
+ end
33
+ end
@@ -6,7 +6,7 @@ module Qwirk
6
6
  # :
7
7
  module Task
8
8
  #include Qwirk::BaseWorker
9
- include Rumx::Bean
9
+ include ::Rumx::Bean
10
10
 
11
11
  bean_attr_accessor :max_pending_records, :integer, 'The max number of records that can be published without having been responded to (publishing blocks at this point).'
12
12
  bean_attr_reader :task_id, :string, 'The ID for this task'
@@ -22,7 +22,7 @@ module Qwirk
22
22
 
23
23
  def self.included(base)
24
24
  #Qwirk::BaseWorker.included(base)
25
- Rumx::Bean.included(base)
25
+ ::Rumx::Bean.included(base)
26
26
  base.extend(ClassMethods)
27
27
  end
28
28
 
@@ -0,0 +1,261 @@
1
+ == Welcome to Rails
2
+
3
+ Rails is a web-application framework that includes everything needed to create
4
+ database-backed web applications according to the Model-View-Control pattern.
5
+
6
+ This pattern splits the view (also called the presentation) into "dumb"
7
+ templates that are primarily responsible for inserting pre-built data in between
8
+ HTML tags. The model contains the "smart" domain objects (such as Account,
9
+ Product, Person, Post) that holds all the business logic and knows how to
10
+ persist themselves to a database. The controller handles the incoming requests
11
+ (such as Save New Account, Update Product, Show Post) by manipulating the model
12
+ and directing data to the view.
13
+
14
+ In Rails, the model is handled by what's called an object-relational mapping
15
+ layer entitled Active Record. This layer allows you to present the data from
16
+ database rows as objects and embellish these data objects with business logic
17
+ methods. You can read more about Active Record in
18
+ link:files/vendor/rails/activerecord/README.html.
19
+
20
+ The controller and view are handled by the Action Pack, which handles both
21
+ layers by its two parts: Action View and Action Controller. These two layers
22
+ are bundled in a single package due to their heavy interdependence. This is
23
+ unlike the relationship between the Active Record and Action Pack that is much
24
+ more separate. Each of these packages can be used independently outside of
25
+ Rails. You can read more about Action Pack in
26
+ link:files/vendor/rails/actionpack/README.html.
27
+
28
+
29
+ == Getting Started
30
+
31
+ 1. At the command prompt, create a new Rails application:
32
+ <tt>rails new myapp</tt> (where <tt>myapp</tt> is the application name)
33
+
34
+ 2. Change directory to <tt>myapp</tt> and start the web server:
35
+ <tt>cd myapp; rails server</tt> (run with --help for options)
36
+
37
+ 3. Go to http://localhost:3000/ and you'll see:
38
+ "Welcome aboard: You're riding Ruby on Rails!"
39
+
40
+ 4. Follow the guidelines to start developing your application. You can find
41
+ the following resources handy:
42
+
43
+ * The Getting Started Guide: http://guides.rubyonrails.org/getting_started.html
44
+ * Ruby on Rails Tutorial Book: http://www.railstutorial.org/
45
+
46
+
47
+ == Debugging Rails
48
+
49
+ Sometimes your application goes wrong. Fortunately there are a lot of tools that
50
+ will help you debug it and get it back on the rails.
51
+
52
+ First area to check is the application log files. Have "tail -f" commands
53
+ running on the server.log and development.log. Rails will automatically display
54
+ debugging and runtime information to these files. Debugging info will also be
55
+ shown in the browser on requests from 127.0.0.1.
56
+
57
+ You can also log your own messages directly into the log file from your code
58
+ using the Ruby logger class from inside your controllers. Example:
59
+
60
+ class WeblogController < ActionController::Base
61
+ def destroy
62
+ @weblog = Weblog.find(params[:id])
63
+ @weblog.destroy
64
+ logger.info("#{Time.now} Destroyed Weblog ID ##{@weblog.id}!")
65
+ end
66
+ end
67
+
68
+ The result will be a message in your log file along the lines of:
69
+
70
+ Mon Oct 08 14:22:29 +1000 2007 Destroyed Weblog ID #1!
71
+
72
+ More information on how to use the logger is at http://www.ruby-doc.org/core/
73
+
74
+ Also, Ruby documentation can be found at http://www.ruby-lang.org/. There are
75
+ several books available online as well:
76
+
77
+ * Programming Ruby: http://www.ruby-doc.org/docs/ProgrammingRuby/ (Pickaxe)
78
+ * Learn to Program: http://pine.fm/LearnToProgram/ (a beginners guide)
79
+
80
+ These two books will bring you up to speed on the Ruby language and also on
81
+ programming in general.
82
+
83
+
84
+ == Debugger
85
+
86
+ Debugger support is available through the debugger command when you start your
87
+ Mongrel or WEBrick server with --debugger. This means that you can break out of
88
+ execution at any point in the code, investigate and change the model, and then,
89
+ resume execution! You need to install ruby-debug to run the server in debugging
90
+ mode. With gems, use <tt>sudo gem install ruby-debug</tt>. Example:
91
+
92
+ class WeblogController < ActionController::Base
93
+ def index
94
+ @posts = Post.all
95
+ debugger
96
+ end
97
+ end
98
+
99
+ So the controller will accept the action, run the first line, then present you
100
+ with a IRB prompt in the server window. Here you can do things like:
101
+
102
+ >> @posts.inspect
103
+ => "[#<Post:0x14a6be8
104
+ @attributes={"title"=>nil, "body"=>nil, "id"=>"1"}>,
105
+ #<Post:0x14a6620
106
+ @attributes={"title"=>"Rails", "body"=>"Only ten..", "id"=>"2"}>]"
107
+ >> @posts.first.title = "hello from a debugger"
108
+ => "hello from a debugger"
109
+
110
+ ...and even better, you can examine how your runtime objects actually work:
111
+
112
+ >> f = @posts.first
113
+ => #<Post:0x13630c4 @attributes={"title"=>nil, "body"=>nil, "id"=>"1"}>
114
+ >> f.
115
+ Display all 152 possibilities? (y or n)
116
+
117
+ Finally, when you're ready to resume execution, you can enter "cont".
118
+
119
+
120
+ == Console
121
+
122
+ The console is a Ruby shell, which allows you to interact with your
123
+ application's domain model. Here you'll have all parts of the application
124
+ configured, just like it is when the application is running. You can inspect
125
+ domain models, change values, and save to the database. Starting the script
126
+ without arguments will launch it in the development environment.
127
+
128
+ To start the console, run <tt>rails console</tt> from the application
129
+ directory.
130
+
131
+ Options:
132
+
133
+ * Passing the <tt>-s, --sandbox</tt> argument will rollback any modifications
134
+ made to the database.
135
+ * Passing an environment name as an argument will load the corresponding
136
+ environment. Example: <tt>rails console production</tt>.
137
+
138
+ To reload your controllers and models after launching the console run
139
+ <tt>reload!</tt>
140
+
141
+ More information about irb can be found at:
142
+ link:http://www.rubycentral.org/pickaxe/irb.html
143
+
144
+
145
+ == dbconsole
146
+
147
+ You can go to the command line of your database directly through <tt>rails
148
+ dbconsole</tt>. You would be connected to the database with the credentials
149
+ defined in database.yml. Starting the script without arguments will connect you
150
+ to the development database. Passing an argument will connect you to a different
151
+ database, like <tt>rails dbconsole production</tt>. Currently works for MySQL,
152
+ PostgreSQL and SQLite 3.
153
+
154
+ == Description of Contents
155
+
156
+ The default directory structure of a generated Ruby on Rails application:
157
+
158
+ |-- app
159
+ | |-- assets
160
+ | |-- images
161
+ | |-- javascripts
162
+ | `-- stylesheets
163
+ | |-- controllers
164
+ | |-- helpers
165
+ | |-- mailers
166
+ | |-- models
167
+ | `-- views
168
+ | `-- layouts
169
+ |-- config
170
+ | |-- environments
171
+ | |-- initializers
172
+ | `-- locales
173
+ |-- db
174
+ |-- doc
175
+ |-- lib
176
+ | `-- tasks
177
+ |-- log
178
+ |-- public
179
+ |-- script
180
+ |-- test
181
+ | |-- fixtures
182
+ | |-- functional
183
+ | |-- integration
184
+ | |-- performance
185
+ | `-- unit
186
+ |-- tmp
187
+ | |-- cache
188
+ | |-- pids
189
+ | |-- sessions
190
+ | `-- sockets
191
+ `-- vendor
192
+ |-- assets
193
+ `-- stylesheets
194
+ `-- plugins
195
+
196
+ app
197
+ Holds all the code that's specific to this particular application.
198
+
199
+ app/assets
200
+ Contains subdirectories for images, stylesheets, and JavaScript files.
201
+
202
+ app/controllers
203
+ Holds controllers that should be named like weblogs_controller.rb for
204
+ automated URL mapping. All controllers should descend from
205
+ ApplicationController which itself descends from ActionController::Base.
206
+
207
+ app/models
208
+ Holds models that should be named like post.rb. Models descend from
209
+ ActiveRecord::Base by default.
210
+
211
+ app/views
212
+ Holds the template files for the view that should be named like
213
+ weblogs/index.html.erb for the WeblogsController#index action. All views use
214
+ eRuby syntax by default.
215
+
216
+ app/views/layouts
217
+ Holds the template files for layouts to be used with views. This models the
218
+ common header/footer method of wrapping views. In your views, define a layout
219
+ using the <tt>layout :default</tt> and create a file named default.html.erb.
220
+ Inside default.html.erb, call <% yield %> to render the view using this
221
+ layout.
222
+
223
+ app/helpers
224
+ Holds view helpers that should be named like weblogs_helper.rb. These are
225
+ generated for you automatically when using generators for controllers.
226
+ Helpers can be used to wrap functionality for your views into methods.
227
+
228
+ config
229
+ Configuration files for the Rails environment, the routing map, the database,
230
+ and other dependencies.
231
+
232
+ db
233
+ Contains the database schema in schema.rb. db/migrate contains all the
234
+ sequence of Migrations for your schema.
235
+
236
+ doc
237
+ This directory is where your application documentation will be stored when
238
+ generated using <tt>rake doc:app</tt>
239
+
240
+ lib
241
+ Application specific libraries. Basically, any kind of custom code that
242
+ doesn't belong under controllers, models, or helpers. This directory is in
243
+ the load path.
244
+
245
+ public
246
+ The directory available for the web server. Also contains the dispatchers and the
247
+ default HTML files. This should be set as the DOCUMENT_ROOT of your web
248
+ server.
249
+
250
+ script
251
+ Helper scripts for automation and generation.
252
+
253
+ test
254
+ Unit and functional tests along with fixtures. When using the rails generate
255
+ command, template test files will be generated for you and placed in this
256
+ directory.
257
+
258
+ vendor
259
+ External libraries that the application depends on. Also includes the plugins
260
+ subdirectory. If the app has frozen rails, those gems also go here, under
261
+ vendor/rails/. This directory is in the load path.