actioncable 5.0.1 → 6.1.3

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 (67) hide show
  1. checksums.yaml +5 -5
  2. data/CHANGELOG.md +31 -117
  3. data/MIT-LICENSE +1 -1
  4. data/README.md +4 -535
  5. data/app/assets/javascripts/action_cable.js +517 -0
  6. data/lib/action_cable.rb +20 -10
  7. data/lib/action_cable/channel.rb +3 -0
  8. data/lib/action_cable/channel/base.rb +31 -23
  9. data/lib/action_cable/channel/broadcasting.rb +22 -10
  10. data/lib/action_cable/channel/callbacks.rb +4 -2
  11. data/lib/action_cable/channel/naming.rb +5 -2
  12. data/lib/action_cable/channel/periodic_timers.rb +4 -3
  13. data/lib/action_cable/channel/streams.rb +39 -11
  14. data/lib/action_cable/channel/test_case.rb +310 -0
  15. data/lib/action_cable/connection.rb +3 -2
  16. data/lib/action_cable/connection/authorization.rb +8 -6
  17. data/lib/action_cable/connection/base.rb +34 -26
  18. data/lib/action_cable/connection/client_socket.rb +20 -18
  19. data/lib/action_cable/connection/identification.rb +5 -4
  20. data/lib/action_cable/connection/internal_channel.rb +4 -2
  21. data/lib/action_cable/connection/message_buffer.rb +3 -2
  22. data/lib/action_cable/connection/stream.rb +9 -5
  23. data/lib/action_cable/connection/stream_event_loop.rb +4 -2
  24. data/lib/action_cable/connection/subscriptions.rb +14 -13
  25. data/lib/action_cable/connection/tagged_logger_proxy.rb +4 -2
  26. data/lib/action_cable/connection/test_case.rb +234 -0
  27. data/lib/action_cable/connection/web_socket.rb +7 -5
  28. data/lib/action_cable/engine.rb +7 -5
  29. data/lib/action_cable/gem_version.rb +5 -3
  30. data/lib/action_cable/helpers/action_cable_helper.rb +6 -4
  31. data/lib/action_cable/remote_connections.rb +9 -4
  32. data/lib/action_cable/server.rb +2 -1
  33. data/lib/action_cable/server/base.rb +17 -10
  34. data/lib/action_cable/server/broadcasting.rb +9 -3
  35. data/lib/action_cable/server/configuration.rb +21 -22
  36. data/lib/action_cable/server/connections.rb +2 -0
  37. data/lib/action_cable/server/worker.rb +11 -11
  38. data/lib/action_cable/server/worker/active_record_connection_management.rb +2 -0
  39. data/lib/action_cable/subscription_adapter.rb +4 -0
  40. data/lib/action_cable/subscription_adapter/async.rb +3 -1
  41. data/lib/action_cable/subscription_adapter/base.rb +6 -0
  42. data/lib/action_cable/subscription_adapter/channel_prefix.rb +28 -0
  43. data/lib/action_cable/subscription_adapter/inline.rb +2 -0
  44. data/lib/action_cable/subscription_adapter/postgresql.rb +40 -14
  45. data/lib/action_cable/subscription_adapter/redis.rb +19 -11
  46. data/lib/action_cable/subscription_adapter/subscriber_map.rb +3 -1
  47. data/lib/action_cable/subscription_adapter/test.rb +40 -0
  48. data/lib/action_cable/test_case.rb +11 -0
  49. data/lib/action_cable/test_helper.rb +133 -0
  50. data/lib/action_cable/version.rb +3 -1
  51. data/lib/rails/generators/channel/USAGE +5 -6
  52. data/lib/rails/generators/channel/channel_generator.rb +16 -11
  53. data/lib/rails/generators/channel/templates/application_cable/{channel.rb → channel.rb.tt} +0 -0
  54. data/lib/rails/generators/channel/templates/application_cable/{connection.rb → connection.rb.tt} +0 -0
  55. data/lib/rails/generators/channel/templates/{channel.rb → channel.rb.tt} +0 -0
  56. data/lib/rails/generators/channel/templates/{assets/channel.js → javascript/channel.js.tt} +6 -4
  57. data/lib/rails/generators/channel/templates/javascript/consumer.js.tt +6 -0
  58. data/lib/rails/generators/channel/templates/javascript/index.js.tt +5 -0
  59. data/lib/rails/generators/test_unit/channel_generator.rb +20 -0
  60. data/lib/rails/generators/test_unit/templates/channel_test.rb.tt +8 -0
  61. metadata +46 -38
  62. data/lib/action_cable/connection/faye_client_socket.rb +0 -48
  63. data/lib/action_cable/connection/faye_event_loop.rb +0 -44
  64. data/lib/action_cable/subscription_adapter/evented_redis.rb +0 -79
  65. data/lib/assets/compiled/action_cable.js +0 -597
  66. data/lib/rails/generators/channel/templates/assets/cable.js +0 -13
  67. data/lib/rails/generators/channel/templates/assets/channel.coffee +0 -14
@@ -0,0 +1,11 @@
1
+ # frozen_string_literal: true
2
+
3
+ require "active_support/test_case"
4
+
5
+ module ActionCable
6
+ class TestCase < ActiveSupport::TestCase
7
+ include ActionCable::TestHelper
8
+
9
+ ActiveSupport.run_load_hooks(:action_cable_test_case, self)
10
+ end
11
+ end
@@ -0,0 +1,133 @@
1
+ # frozen_string_literal: true
2
+
3
+ module ActionCable
4
+ # Provides helper methods for testing Action Cable broadcasting
5
+ module TestHelper
6
+ def before_setup # :nodoc:
7
+ server = ActionCable.server
8
+ test_adapter = ActionCable::SubscriptionAdapter::Test.new(server)
9
+
10
+ @old_pubsub_adapter = server.pubsub
11
+
12
+ server.instance_variable_set(:@pubsub, test_adapter)
13
+ super
14
+ end
15
+
16
+ def after_teardown # :nodoc:
17
+ super
18
+ ActionCable.server.instance_variable_set(:@pubsub, @old_pubsub_adapter)
19
+ end
20
+
21
+ # Asserts that the number of broadcasted messages to the stream matches the given number.
22
+ #
23
+ # def test_broadcasts
24
+ # assert_broadcasts 'messages', 0
25
+ # ActionCable.server.broadcast 'messages', { text: 'hello' }
26
+ # assert_broadcasts 'messages', 1
27
+ # ActionCable.server.broadcast 'messages', { text: 'world' }
28
+ # assert_broadcasts 'messages', 2
29
+ # end
30
+ #
31
+ # If a block is passed, that block should cause the specified number of
32
+ # messages to be broadcasted.
33
+ #
34
+ # def test_broadcasts_again
35
+ # assert_broadcasts('messages', 1) do
36
+ # ActionCable.server.broadcast 'messages', { text: 'hello' }
37
+ # end
38
+ #
39
+ # assert_broadcasts('messages', 2) do
40
+ # ActionCable.server.broadcast 'messages', { text: 'hi' }
41
+ # ActionCable.server.broadcast 'messages', { text: 'how are you?' }
42
+ # end
43
+ # end
44
+ #
45
+ def assert_broadcasts(stream, number, &block)
46
+ if block_given?
47
+ original_count = broadcasts_size(stream)
48
+ assert_nothing_raised(&block)
49
+ new_count = broadcasts_size(stream)
50
+ actual_count = new_count - original_count
51
+ else
52
+ actual_count = broadcasts_size(stream)
53
+ end
54
+
55
+ assert_equal number, actual_count, "#{number} broadcasts to #{stream} expected, but #{actual_count} were sent"
56
+ end
57
+
58
+ # Asserts that no messages have been sent to the stream.
59
+ #
60
+ # def test_no_broadcasts
61
+ # assert_no_broadcasts 'messages'
62
+ # ActionCable.server.broadcast 'messages', { text: 'hi' }
63
+ # assert_broadcasts 'messages', 1
64
+ # end
65
+ #
66
+ # If a block is passed, that block should not cause any message to be sent.
67
+ #
68
+ # def test_broadcasts_again
69
+ # assert_no_broadcasts 'messages' do
70
+ # # No job messages should be sent from this block
71
+ # end
72
+ # end
73
+ #
74
+ # Note: This assertion is simply a shortcut for:
75
+ #
76
+ # assert_broadcasts 'messages', 0, &block
77
+ #
78
+ def assert_no_broadcasts(stream, &block)
79
+ assert_broadcasts stream, 0, &block
80
+ end
81
+
82
+ # Asserts that the specified message has been sent to the stream.
83
+ #
84
+ # def test_assert_transmitted_message
85
+ # ActionCable.server.broadcast 'messages', text: 'hello'
86
+ # assert_broadcast_on('messages', text: 'hello')
87
+ # end
88
+ #
89
+ # If a block is passed, that block should cause a message with the specified data to be sent.
90
+ #
91
+ # def test_assert_broadcast_on_again
92
+ # assert_broadcast_on('messages', text: 'hello') do
93
+ # ActionCable.server.broadcast 'messages', text: 'hello'
94
+ # end
95
+ # end
96
+ #
97
+ def assert_broadcast_on(stream, data, &block)
98
+ # Encode to JSON and back–we want to use this value to compare
99
+ # with decoded JSON.
100
+ # Comparing JSON strings doesn't work due to the order if the keys.
101
+ serialized_msg =
102
+ ActiveSupport::JSON.decode(ActiveSupport::JSON.encode(data))
103
+
104
+ new_messages = broadcasts(stream)
105
+ if block_given?
106
+ old_messages = new_messages
107
+ clear_messages(stream)
108
+
109
+ assert_nothing_raised(&block)
110
+ new_messages = broadcasts(stream)
111
+ clear_messages(stream)
112
+
113
+ # Restore all sent messages
114
+ (old_messages + new_messages).each { |m| pubsub_adapter.broadcast(stream, m) }
115
+ end
116
+
117
+ message = new_messages.find { |msg| ActiveSupport::JSON.decode(msg) == serialized_msg }
118
+
119
+ assert message, "No messages sent with #{data} to #{stream}"
120
+ end
121
+
122
+ def pubsub_adapter # :nodoc:
123
+ ActionCable.server.pubsub
124
+ end
125
+
126
+ delegate :broadcasts, :clear_messages, to: :pubsub_adapter
127
+
128
+ private
129
+ def broadcasts_size(channel)
130
+ broadcasts(channel).size
131
+ end
132
+ end
133
+ end
@@ -1,4 +1,6 @@
1
- require_relative 'gem_version'
1
+ # frozen_string_literal: true
2
+
3
+ require_relative "gem_version"
2
4
 
3
5
  module ActionCable
4
6
  # Returns the version of the currently loaded Action Cable as a <tt>Gem::Version</tt>
@@ -1,14 +1,13 @@
1
1
  Description:
2
2
  ============
3
- Stubs out a new cable channel for the server (in Ruby) and client (in CoffeeScript).
3
+ Generates a new cable channel for the server (in Ruby) and client (in JavaScript).
4
4
  Pass the channel name, either CamelCased or under_scored, and an optional list of channel actions as arguments.
5
5
 
6
- Note: Turn on the cable connection in app/assets/javascript/cable.js after generating any channels.
7
-
8
6
  Example:
9
7
  ========
10
- rails generate channel Chat speak
8
+ bin/rails generate channel Chat speak
11
9
 
12
- creates a Chat channel class and CoffeeScript asset:
10
+ creates a Chat channel class, test and JavaScript asset:
13
11
  Channel: app/channels/chat_channel.rb
14
- Assets: app/assets/javascript/channels/chat.coffee
12
+ Test: test/channels/chat_channel_test.rb
13
+ Assets: app/javascript/channels/chat_channel.js
@@ -1,7 +1,9 @@
1
+ # frozen_string_literal: true
2
+
1
3
  module Rails
2
4
  module Generators
3
5
  class ChannelGenerator < NamedBase
4
- source_root File.expand_path("../templates", __FILE__)
6
+ source_root File.expand_path("templates", __dir__)
5
7
 
6
8
  argument :actions, type: :array, default: [], banner: "method method"
7
9
 
@@ -9,36 +11,39 @@ module Rails
9
11
 
10
12
  check_class_collision suffix: "Channel"
11
13
 
14
+ hook_for :test_framework
15
+
12
16
  def create_channel_file
13
- template "channel.rb", File.join('app/channels', class_path, "#{file_name}_channel.rb")
17
+ template "channel.rb", File.join("app/channels", class_path, "#{file_name}_channel.rb")
14
18
 
15
19
  if options[:assets]
16
- if self.behavior == :invoke
17
- template "assets/cable.js", "app/assets/javascripts/cable.js"
20
+ if behavior == :invoke
21
+ template "javascript/index.js", "app/javascript/channels/index.js"
22
+ template "javascript/consumer.js", "app/javascript/channels/consumer.js"
18
23
  end
19
24
 
20
- js_template "assets/channel", File.join('app/assets/javascripts/channels', class_path, "#{file_name}")
25
+ js_template "javascript/channel", File.join("app/javascript/channels", class_path, "#{file_name}_channel")
21
26
  end
22
27
 
23
28
  generate_application_cable_files
24
29
  end
25
30
 
26
- protected
31
+ private
27
32
  def file_name
28
- @_file_name ||= super.gsub(/_channel/i, '')
33
+ @_file_name ||= super.sub(/_channel\z/i, "")
29
34
  end
30
35
 
31
36
  # FIXME: Change these files to symlinks once RubyGems 2.5.0 is required.
32
37
  def generate_application_cable_files
33
- return if self.behavior != :invoke
38
+ return if behavior != :invoke
34
39
 
35
40
  files = [
36
- 'application_cable/channel.rb',
37
- 'application_cable/connection.rb'
41
+ "application_cable/channel.rb",
42
+ "application_cable/connection.rb"
38
43
  ]
39
44
 
40
45
  files.each do |name|
41
- path = File.join('app/channels/', name)
46
+ path = File.join("app/channels/", name)
42
47
  template(name, path) if !File.exist?(path)
43
48
  end
44
49
  end
@@ -1,13 +1,15 @@
1
- App.<%= class_name.underscore %> = App.cable.subscriptions.create("<%= class_name %>Channel", {
2
- connected: function() {
1
+ import consumer from "./consumer"
2
+
3
+ consumer.subscriptions.create("<%= class_name %>Channel", {
4
+ connected() {
3
5
  // Called when the subscription is ready for use on the server
4
6
  },
5
7
 
6
- disconnected: function() {
8
+ disconnected() {
7
9
  // Called when the subscription has been terminated by the server
8
10
  },
9
11
 
10
- received: function(data) {
12
+ received(data) {
11
13
  // Called when there's incoming data on the websocket for this channel
12
14
  }<%= actions.any? ? ",\n" : '' %>
13
15
  <% actions.each do |action| -%>
@@ -0,0 +1,6 @@
1
+ // Action Cable provides the framework to deal with WebSockets in Rails.
2
+ // You can generate new channels where WebSocket features live using the `bin/rails generate channel` command.
3
+
4
+ import { createConsumer } from "@rails/actioncable"
5
+
6
+ export default createConsumer()
@@ -0,0 +1,5 @@
1
+ // Load all the channels within this directory and all subdirectories.
2
+ // Channel files must be named *_channel.js.
3
+
4
+ const channels = require.context('.', true, /_channel\.js$/)
5
+ channels.keys().forEach(channels)
@@ -0,0 +1,20 @@
1
+ # frozen_string_literal: true
2
+
3
+ module TestUnit
4
+ module Generators
5
+ class ChannelGenerator < ::Rails::Generators::NamedBase
6
+ source_root File.expand_path("templates", __dir__)
7
+
8
+ check_class_collision suffix: "ChannelTest"
9
+
10
+ def create_test_files
11
+ template "channel_test.rb", File.join("test/channels", class_path, "#{file_name}_channel_test.rb")
12
+ end
13
+
14
+ private
15
+ def file_name # :doc:
16
+ @_file_name ||= super.sub(/_channel\z/i, "")
17
+ end
18
+ end
19
+ end
20
+ end
@@ -0,0 +1,8 @@
1
+ require "test_helper"
2
+
3
+ class <%= class_name %>ChannelTest < ActionCable::Channel::TestCase
4
+ # test "subscribes" do
5
+ # subscribe
6
+ # assert subscription.confirmed?
7
+ # end
8
+ end
metadata CHANGED
@@ -1,72 +1,72 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: actioncable
3
3
  version: !ruby/object:Gem::Version
4
- version: 5.0.1
4
+ version: 6.1.3
5
5
  platform: ruby
6
6
  authors:
7
7
  - Pratik Naik
8
8
  - David Heinemeier Hansson
9
- autorequire:
9
+ autorequire:
10
10
  bindir: bin
11
11
  cert_chain: []
12
- date: 2016-12-21 00:00:00.000000000 Z
12
+ date: 2021-02-17 00:00:00.000000000 Z
13
13
  dependencies:
14
14
  - !ruby/object:Gem::Dependency
15
- name: actionpack
15
+ name: activesupport
16
16
  requirement: !ruby/object:Gem::Requirement
17
17
  requirements:
18
18
  - - '='
19
19
  - !ruby/object:Gem::Version
20
- version: 5.0.1
20
+ version: 6.1.3
21
21
  type: :runtime
22
22
  prerelease: false
23
23
  version_requirements: !ruby/object:Gem::Requirement
24
24
  requirements:
25
25
  - - '='
26
26
  - !ruby/object:Gem::Version
27
- version: 5.0.1
27
+ version: 6.1.3
28
28
  - !ruby/object:Gem::Dependency
29
- name: nio4r
29
+ name: actionpack
30
30
  requirement: !ruby/object:Gem::Requirement
31
31
  requirements:
32
- - - "~>"
32
+ - - '='
33
33
  - !ruby/object:Gem::Version
34
- version: '1.2'
34
+ version: 6.1.3
35
35
  type: :runtime
36
36
  prerelease: false
37
37
  version_requirements: !ruby/object:Gem::Requirement
38
38
  requirements:
39
- - - "~>"
39
+ - - '='
40
40
  - !ruby/object:Gem::Version
41
- version: '1.2'
41
+ version: 6.1.3
42
42
  - !ruby/object:Gem::Dependency
43
- name: websocket-driver
43
+ name: nio4r
44
44
  requirement: !ruby/object:Gem::Requirement
45
45
  requirements:
46
46
  - - "~>"
47
47
  - !ruby/object:Gem::Version
48
- version: 0.6.1
48
+ version: '2.0'
49
49
  type: :runtime
50
50
  prerelease: false
51
51
  version_requirements: !ruby/object:Gem::Requirement
52
52
  requirements:
53
53
  - - "~>"
54
54
  - !ruby/object:Gem::Version
55
- version: 0.6.1
55
+ version: '2.0'
56
56
  - !ruby/object:Gem::Dependency
57
- name: blade
57
+ name: websocket-driver
58
58
  requirement: !ruby/object:Gem::Requirement
59
59
  requirements:
60
- - - "~>"
60
+ - - ">="
61
61
  - !ruby/object:Gem::Version
62
- version: 0.5.1
63
- type: :development
62
+ version: 0.6.1
63
+ type: :runtime
64
64
  prerelease: false
65
65
  version_requirements: !ruby/object:Gem::Requirement
66
66
  requirements:
67
- - - "~>"
67
+ - - ">="
68
68
  - !ruby/object:Gem::Version
69
- version: 0.5.1
69
+ version: 0.6.1
70
70
  description: Structure many real-time application concerns into channels over a single
71
71
  WebSocket connection.
72
72
  email:
@@ -79,6 +79,7 @@ files:
79
79
  - CHANGELOG.md
80
80
  - MIT-LICENSE
81
81
  - README.md
82
+ - app/assets/javascripts/action_cable.js
82
83
  - lib/action_cable.rb
83
84
  - lib/action_cable/channel.rb
84
85
  - lib/action_cable/channel/base.rb
@@ -87,12 +88,11 @@ files:
87
88
  - lib/action_cable/channel/naming.rb
88
89
  - lib/action_cable/channel/periodic_timers.rb
89
90
  - lib/action_cable/channel/streams.rb
91
+ - lib/action_cable/channel/test_case.rb
90
92
  - lib/action_cable/connection.rb
91
93
  - lib/action_cable/connection/authorization.rb
92
94
  - lib/action_cable/connection/base.rb
93
95
  - lib/action_cable/connection/client_socket.rb
94
- - lib/action_cable/connection/faye_client_socket.rb
95
- - lib/action_cable/connection/faye_event_loop.rb
96
96
  - lib/action_cable/connection/identification.rb
97
97
  - lib/action_cable/connection/internal_channel.rb
98
98
  - lib/action_cable/connection/message_buffer.rb
@@ -100,6 +100,7 @@ files:
100
100
  - lib/action_cable/connection/stream_event_loop.rb
101
101
  - lib/action_cable/connection/subscriptions.rb
102
102
  - lib/action_cable/connection/tagged_logger_proxy.rb
103
+ - lib/action_cable/connection/test_case.rb
103
104
  - lib/action_cable/connection/web_socket.rb
104
105
  - lib/action_cable/engine.rb
105
106
  - lib/action_cable/gem_version.rb
@@ -115,26 +116,35 @@ files:
115
116
  - lib/action_cable/subscription_adapter.rb
116
117
  - lib/action_cable/subscription_adapter/async.rb
117
118
  - lib/action_cable/subscription_adapter/base.rb
118
- - lib/action_cable/subscription_adapter/evented_redis.rb
119
+ - lib/action_cable/subscription_adapter/channel_prefix.rb
119
120
  - lib/action_cable/subscription_adapter/inline.rb
120
121
  - lib/action_cable/subscription_adapter/postgresql.rb
121
122
  - lib/action_cable/subscription_adapter/redis.rb
122
123
  - lib/action_cable/subscription_adapter/subscriber_map.rb
124
+ - lib/action_cable/subscription_adapter/test.rb
125
+ - lib/action_cable/test_case.rb
126
+ - lib/action_cable/test_helper.rb
123
127
  - lib/action_cable/version.rb
124
- - lib/assets/compiled/action_cable.js
125
128
  - lib/rails/generators/channel/USAGE
126
129
  - lib/rails/generators/channel/channel_generator.rb
127
- - lib/rails/generators/channel/templates/application_cable/channel.rb
128
- - lib/rails/generators/channel/templates/application_cable/connection.rb
129
- - lib/rails/generators/channel/templates/assets/cable.js
130
- - lib/rails/generators/channel/templates/assets/channel.coffee
131
- - lib/rails/generators/channel/templates/assets/channel.js
132
- - lib/rails/generators/channel/templates/channel.rb
133
- homepage: http://rubyonrails.org
130
+ - lib/rails/generators/channel/templates/application_cable/channel.rb.tt
131
+ - lib/rails/generators/channel/templates/application_cable/connection.rb.tt
132
+ - lib/rails/generators/channel/templates/channel.rb.tt
133
+ - lib/rails/generators/channel/templates/javascript/channel.js.tt
134
+ - lib/rails/generators/channel/templates/javascript/consumer.js.tt
135
+ - lib/rails/generators/channel/templates/javascript/index.js.tt
136
+ - lib/rails/generators/test_unit/channel_generator.rb
137
+ - lib/rails/generators/test_unit/templates/channel_test.rb.tt
138
+ homepage: https://rubyonrails.org
134
139
  licenses:
135
140
  - MIT
136
- metadata: {}
137
- post_install_message:
141
+ metadata:
142
+ bug_tracker_uri: https://github.com/rails/rails/issues
143
+ changelog_uri: https://github.com/rails/rails/blob/v6.1.3/actioncable/CHANGELOG.md
144
+ documentation_uri: https://api.rubyonrails.org/v6.1.3/
145
+ mailing_list_uri: https://discuss.rubyonrails.org/c/rubyonrails-talk
146
+ source_code_uri: https://github.com/rails/rails/tree/v6.1.3/actioncable
147
+ post_install_message:
138
148
  rdoc_options: []
139
149
  require_paths:
140
150
  - lib
@@ -142,17 +152,15 @@ required_ruby_version: !ruby/object:Gem::Requirement
142
152
  requirements:
143
153
  - - ">="
144
154
  - !ruby/object:Gem::Version
145
- version: 2.2.2
155
+ version: 2.5.0
146
156
  required_rubygems_version: !ruby/object:Gem::Requirement
147
157
  requirements:
148
158
  - - ">="
149
159
  - !ruby/object:Gem::Version
150
160
  version: '0'
151
161
  requirements: []
152
- rubyforge_project:
153
- rubygems_version: 2.5.2
154
- signing_key:
162
+ rubygems_version: 3.2.3
163
+ signing_key:
155
164
  specification_version: 4
156
165
  summary: WebSocket framework for Rails.
157
166
  test_files: []
158
- has_rdoc: