Embed rails 5.1 temporarily till gitlab supports rails 5.2
This commit is contained in:
parent
88178d6b0c
commit
e243bafc9b
1320 changed files with 138493 additions and 0 deletions
108
debian/gems-compat/actioncable-5.1.6.1/CHANGELOG.md
vendored
Normal file
108
debian/gems-compat/actioncable-5.1.6.1/CHANGELOG.md
vendored
Normal file
|
@ -0,0 +1,108 @@
|
||||||
|
## Rails 5.1.6.1 (November 27, 2018) ##
|
||||||
|
|
||||||
|
* No changes.
|
||||||
|
|
||||||
|
|
||||||
|
## Rails 5.1.6 (March 29, 2018) ##
|
||||||
|
|
||||||
|
* No changes.
|
||||||
|
|
||||||
|
|
||||||
|
## Rails 5.1.5 (February 14, 2018) ##
|
||||||
|
|
||||||
|
* Support redis-rb 4.0.
|
||||||
|
|
||||||
|
*Jeremy Daer*
|
||||||
|
|
||||||
|
|
||||||
|
## Rails 5.1.4 (September 07, 2017) ##
|
||||||
|
|
||||||
|
* No changes.
|
||||||
|
|
||||||
|
|
||||||
|
## Rails 5.1.4.rc1 (August 24, 2017) ##
|
||||||
|
|
||||||
|
* No changes.
|
||||||
|
|
||||||
|
|
||||||
|
## Rails 5.1.3 (August 03, 2017) ##
|
||||||
|
|
||||||
|
* No changes.
|
||||||
|
|
||||||
|
|
||||||
|
## Rails 5.1.3.rc3 (July 31, 2017) ##
|
||||||
|
|
||||||
|
* No changes.
|
||||||
|
|
||||||
|
|
||||||
|
## Rails 5.1.3.rc2 (July 25, 2017) ##
|
||||||
|
|
||||||
|
* No changes.
|
||||||
|
|
||||||
|
|
||||||
|
## Rails 5.1.3.rc1 (July 19, 2017) ##
|
||||||
|
|
||||||
|
* No changes.
|
||||||
|
|
||||||
|
|
||||||
|
## Rails 5.1.2 (June 26, 2017) ##
|
||||||
|
|
||||||
|
* No changes.
|
||||||
|
|
||||||
|
|
||||||
|
## Rails 5.1.1 (May 12, 2017) ##
|
||||||
|
|
||||||
|
* No changes.
|
||||||
|
|
||||||
|
|
||||||
|
## Rails 5.1.0 (April 27, 2017) ##
|
||||||
|
|
||||||
|
* ActionCable socket errors are now logged to the console
|
||||||
|
|
||||||
|
Previously any socket errors were ignored and this made it hard to diagnose socket issues (e.g. as discussed in #28362).
|
||||||
|
|
||||||
|
*Edward Poot*
|
||||||
|
|
||||||
|
* Redis subscription adapters now support `channel_prefix` option in `cable.yml`
|
||||||
|
|
||||||
|
Avoids channel name collisions when multiple apps use the same Redis server.
|
||||||
|
|
||||||
|
*Chad Ingram*
|
||||||
|
|
||||||
|
* Permit same-origin connections by default.
|
||||||
|
|
||||||
|
Added new option `config.action_cable.allow_same_origin_as_host = false`
|
||||||
|
to disable this behaviour.
|
||||||
|
|
||||||
|
*Dávid Halász*, *Matthew Draper*
|
||||||
|
|
||||||
|
* Prevent race where the client could receive and act upon a
|
||||||
|
subscription confirmation before the channel's `subscribed` method
|
||||||
|
completed.
|
||||||
|
|
||||||
|
Fixes #25381.
|
||||||
|
|
||||||
|
*Vladimir Dementyev*
|
||||||
|
|
||||||
|
* Buffer now writes to WebSocket connections, to avoid blocking threads
|
||||||
|
that could be doing more useful things.
|
||||||
|
|
||||||
|
*Matthew Draper*, *Tinco Andringa*
|
||||||
|
|
||||||
|
* Protect against concurrent writes to a WebSocket connection from
|
||||||
|
multiple threads; the underlying OS write is not always threadsafe.
|
||||||
|
|
||||||
|
*Tinco Andringa*
|
||||||
|
|
||||||
|
* Add `ActiveSupport::Notifications` hook to `Broadcaster#broadcast`.
|
||||||
|
|
||||||
|
*Matthew Wear*
|
||||||
|
|
||||||
|
* Close hijacked socket when connection is shut down.
|
||||||
|
|
||||||
|
Fixes #25613.
|
||||||
|
|
||||||
|
*Tinco Andringa*
|
||||||
|
|
||||||
|
|
||||||
|
Please check [5-0-stable](https://github.com/rails/rails/blob/5-0-stable/actioncable/CHANGELOG.md) for previous changes.
|
20
debian/gems-compat/actioncable-5.1.6.1/MIT-LICENSE
vendored
Normal file
20
debian/gems-compat/actioncable-5.1.6.1/MIT-LICENSE
vendored
Normal file
|
@ -0,0 +1,20 @@
|
||||||
|
Copyright (c) 2015-2017 Basecamp, LLC
|
||||||
|
|
||||||
|
Permission is hereby granted, free of charge, to any person obtaining
|
||||||
|
a copy of this software and associated documentation files (the
|
||||||
|
"Software"), to deal in the Software without restriction, including
|
||||||
|
without limitation the rights to use, copy, modify, merge, publish,
|
||||||
|
distribute, sublicense, and/or sell copies of the Software, and to
|
||||||
|
permit persons to whom the Software is furnished to do so, subject to
|
||||||
|
the following conditions:
|
||||||
|
|
||||||
|
The above copyright notice and this permission notice shall be
|
||||||
|
included in all copies or substantial portions of the Software.
|
||||||
|
|
||||||
|
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
|
||||||
|
EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
|
||||||
|
MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
|
||||||
|
NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
|
||||||
|
LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
|
||||||
|
OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
|
||||||
|
WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
567
debian/gems-compat/actioncable-5.1.6.1/README.md
vendored
Normal file
567
debian/gems-compat/actioncable-5.1.6.1/README.md
vendored
Normal file
|
@ -0,0 +1,567 @@
|
||||||
|
# Action Cable – Integrated WebSockets for Rails
|
||||||
|
|
||||||
|
Action Cable seamlessly integrates WebSockets with the rest of your Rails application.
|
||||||
|
It allows for real-time features to be written in Ruby in the same style
|
||||||
|
and form as the rest of your Rails application, while still being performant
|
||||||
|
and scalable. It's a full-stack offering that provides both a client-side
|
||||||
|
JavaScript framework and a server-side Ruby framework. You have access to your full
|
||||||
|
domain model written with Active Record or your ORM of choice.
|
||||||
|
|
||||||
|
## Terminology
|
||||||
|
|
||||||
|
A single Action Cable server can handle multiple connection instances. It has one
|
||||||
|
connection instance per WebSocket connection. A single user may have multiple
|
||||||
|
WebSockets open to your application if they use multiple browser tabs or devices.
|
||||||
|
The client of a WebSocket connection is called the consumer.
|
||||||
|
|
||||||
|
Each consumer can in turn subscribe to multiple cable channels. Each channel encapsulates
|
||||||
|
a logical unit of work, similar to what a controller does in a regular MVC setup. For example,
|
||||||
|
you could have a `ChatChannel` and an `AppearancesChannel`, and a consumer could be subscribed to either
|
||||||
|
or to both of these channels. At the very least, a consumer should be subscribed to one channel.
|
||||||
|
|
||||||
|
When the consumer is subscribed to a channel, they act as a subscriber. The connection between
|
||||||
|
the subscriber and the channel is, surprise-surprise, called a subscription. A consumer
|
||||||
|
can act as a subscriber to a given channel any number of times. For example, a consumer
|
||||||
|
could subscribe to multiple chat rooms at the same time. (And remember that a physical user may
|
||||||
|
have multiple consumers, one per tab/device open to your connection).
|
||||||
|
|
||||||
|
Each channel can then again be streaming zero or more broadcastings. A broadcasting is a
|
||||||
|
pubsub link where anything transmitted by the broadcaster is sent directly to the channel
|
||||||
|
subscribers who are streaming that named broadcasting.
|
||||||
|
|
||||||
|
As you can see, this is a fairly deep architectural stack. There's a lot of new terminology
|
||||||
|
to identify the new pieces, and on top of that, you're dealing with both client and server side
|
||||||
|
reflections of each unit.
|
||||||
|
|
||||||
|
## Examples
|
||||||
|
|
||||||
|
### A full-stack example
|
||||||
|
|
||||||
|
The first thing you must do is define your `ApplicationCable::Connection` class in Ruby. This
|
||||||
|
is the place where you authorize the incoming connection, and proceed to establish it,
|
||||||
|
if all is well. Here's the simplest example starting with the server-side connection class:
|
||||||
|
|
||||||
|
```ruby
|
||||||
|
# app/channels/application_cable/connection.rb
|
||||||
|
module ApplicationCable
|
||||||
|
class Connection < ActionCable::Connection::Base
|
||||||
|
identified_by :current_user
|
||||||
|
|
||||||
|
def connect
|
||||||
|
self.current_user = find_verified_user
|
||||||
|
end
|
||||||
|
|
||||||
|
private
|
||||||
|
def find_verified_user
|
||||||
|
if verified_user = User.find_by(id: cookies.signed[:user_id])
|
||||||
|
verified_user
|
||||||
|
else
|
||||||
|
reject_unauthorized_connection
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
||||||
|
```
|
||||||
|
Here `identified_by` is a connection identifier that can be used to find the specific connection again or later.
|
||||||
|
Note that anything marked as an identifier will automatically create a delegate by the same name on any channel instances created off the connection.
|
||||||
|
|
||||||
|
This relies on the fact that you will already have handled authentication of the user, and
|
||||||
|
that a successful authentication sets a signed cookie with the `user_id`. This cookie is then
|
||||||
|
automatically sent to the connection instance when a new connection is attempted, and you
|
||||||
|
use that to set the `current_user`. By identifying the connection by this same current_user,
|
||||||
|
you're also ensuring that you can later retrieve all open connections by a given user (and
|
||||||
|
potentially disconnect them all if the user is deleted or deauthorized).
|
||||||
|
|
||||||
|
Next, you should define your `ApplicationCable::Channel` class in Ruby. This is the place where you put
|
||||||
|
shared logic between your channels.
|
||||||
|
|
||||||
|
```ruby
|
||||||
|
# app/channels/application_cable/channel.rb
|
||||||
|
module ApplicationCable
|
||||||
|
class Channel < ActionCable::Channel::Base
|
||||||
|
end
|
||||||
|
end
|
||||||
|
```
|
||||||
|
|
||||||
|
The client-side needs to setup a consumer instance of this connection. That's done like so:
|
||||||
|
|
||||||
|
```js
|
||||||
|
// app/assets/javascripts/cable.js
|
||||||
|
//= require action_cable
|
||||||
|
//= require_self
|
||||||
|
//= require_tree ./channels
|
||||||
|
|
||||||
|
(function() {
|
||||||
|
this.App || (this.App = {});
|
||||||
|
|
||||||
|
App.cable = ActionCable.createConsumer("ws://cable.example.com");
|
||||||
|
}).call(this);
|
||||||
|
```
|
||||||
|
|
||||||
|
The `ws://cable.example.com` address must point to your Action Cable server(s), and it
|
||||||
|
must share a cookie namespace with the rest of the application (which may live under http://example.com).
|
||||||
|
This ensures that the signed cookie will be correctly sent.
|
||||||
|
|
||||||
|
That's all you need to establish the connection! But of course, this isn't very useful in
|
||||||
|
itself. This just gives you the plumbing. To make stuff happen, you need content. That content
|
||||||
|
is defined by declaring channels on the server and allowing the consumer to subscribe to them.
|
||||||
|
|
||||||
|
|
||||||
|
### Channel example 1: User appearances
|
||||||
|
|
||||||
|
Here's a simple example of a channel that tracks whether a user is online or not, and also what page they are currently on.
|
||||||
|
(This is useful for creating presence features like showing a green dot next to a user's name if they're online).
|
||||||
|
|
||||||
|
First you declare the server-side channel:
|
||||||
|
|
||||||
|
```ruby
|
||||||
|
# app/channels/appearance_channel.rb
|
||||||
|
class AppearanceChannel < ApplicationCable::Channel
|
||||||
|
def subscribed
|
||||||
|
current_user.appear
|
||||||
|
end
|
||||||
|
|
||||||
|
def unsubscribed
|
||||||
|
current_user.disappear
|
||||||
|
end
|
||||||
|
|
||||||
|
def appear(data)
|
||||||
|
current_user.appear on: data['appearing_on']
|
||||||
|
end
|
||||||
|
|
||||||
|
def away
|
||||||
|
current_user.away
|
||||||
|
end
|
||||||
|
end
|
||||||
|
```
|
||||||
|
|
||||||
|
The `#subscribed` callback is invoked when, as we'll show below, a client-side subscription is initiated. In this case,
|
||||||
|
we take that opportunity to say "the current user has indeed appeared". That appear/disappear API could be backed by
|
||||||
|
Redis or a database or whatever else. Here's what the client-side of that looks like:
|
||||||
|
|
||||||
|
```coffeescript
|
||||||
|
# app/assets/javascripts/cable/subscriptions/appearance.coffee
|
||||||
|
App.cable.subscriptions.create "AppearanceChannel",
|
||||||
|
# Called when the subscription is ready for use on the server
|
||||||
|
connected: ->
|
||||||
|
@install()
|
||||||
|
@appear()
|
||||||
|
|
||||||
|
# Called when the WebSocket connection is closed
|
||||||
|
disconnected: ->
|
||||||
|
@uninstall()
|
||||||
|
|
||||||
|
# Called when the subscription is rejected by the server
|
||||||
|
rejected: ->
|
||||||
|
@uninstall()
|
||||||
|
|
||||||
|
appear: ->
|
||||||
|
# Calls `AppearanceChannel#appear(data)` on the server
|
||||||
|
@perform("appear", appearing_on: $("main").data("appearing-on"))
|
||||||
|
|
||||||
|
away: ->
|
||||||
|
# Calls `AppearanceChannel#away` on the server
|
||||||
|
@perform("away")
|
||||||
|
|
||||||
|
|
||||||
|
buttonSelector = "[data-behavior~=appear_away]"
|
||||||
|
|
||||||
|
install: ->
|
||||||
|
$(document).on "turbolinks:load.appearance", =>
|
||||||
|
@appear()
|
||||||
|
|
||||||
|
$(document).on "click.appearance", buttonSelector, =>
|
||||||
|
@away()
|
||||||
|
false
|
||||||
|
|
||||||
|
$(buttonSelector).show()
|
||||||
|
|
||||||
|
uninstall: ->
|
||||||
|
$(document).off(".appearance")
|
||||||
|
$(buttonSelector).hide()
|
||||||
|
```
|
||||||
|
|
||||||
|
Simply calling `App.cable.subscriptions.create` will setup the subscription, which will call `AppearanceChannel#subscribed`,
|
||||||
|
which in turn is linked to the original `App.cable` -> `ApplicationCable::Connection` instances.
|
||||||
|
|
||||||
|
Next, we link the client-side `appear` method to `AppearanceChannel#appear(data)`. This is possible because the server-side
|
||||||
|
channel instance will automatically expose the public methods declared on the class (minus the callbacks), so that these
|
||||||
|
can be reached as remote procedure calls via a subscription's `perform` method.
|
||||||
|
|
||||||
|
### Channel example 2: Receiving new web notifications
|
||||||
|
|
||||||
|
The appearance example was all about exposing server functionality to client-side invocation over the WebSocket connection.
|
||||||
|
But the great thing about WebSockets is that it's a two-way street. So now let's show an example where the server invokes
|
||||||
|
an action on the client.
|
||||||
|
|
||||||
|
This is a web notification channel that allows you to trigger client-side web notifications when you broadcast to the right
|
||||||
|
streams:
|
||||||
|
|
||||||
|
```ruby
|
||||||
|
# app/channels/web_notifications_channel.rb
|
||||||
|
class WebNotificationsChannel < ApplicationCable::Channel
|
||||||
|
def subscribed
|
||||||
|
stream_from "web_notifications_#{current_user.id}"
|
||||||
|
end
|
||||||
|
end
|
||||||
|
```
|
||||||
|
|
||||||
|
```coffeescript
|
||||||
|
# Client-side, which assumes you've already requested the right to send web notifications
|
||||||
|
App.cable.subscriptions.create "WebNotificationsChannel",
|
||||||
|
received: (data) ->
|
||||||
|
new Notification data["title"], body: data["body"]
|
||||||
|
```
|
||||||
|
|
||||||
|
```ruby
|
||||||
|
# Somewhere in your app this is called, perhaps from a NewCommentJob
|
||||||
|
ActionCable.server.broadcast \
|
||||||
|
"web_notifications_#{current_user.id}", { title: 'New things!', body: 'All the news that is fit to print' }
|
||||||
|
```
|
||||||
|
|
||||||
|
The `ActionCable.server.broadcast` call places a message in the Action Cable pubsub queue under a separate broadcasting name for each user. For a user with an ID of 1, the broadcasting name would be `web_notifications_1`.
|
||||||
|
The channel has been instructed to stream everything that arrives at `web_notifications_1` directly to the client by invoking the
|
||||||
|
`#received(data)` callback. The data is the hash sent as the second parameter to the server-side broadcast call, JSON encoded for the trip
|
||||||
|
across the wire, and unpacked for the data argument arriving to `#received`.
|
||||||
|
|
||||||
|
|
||||||
|
### Passing Parameters to Channel
|
||||||
|
|
||||||
|
You can pass parameters from the client side to the server side when creating a subscription. For example:
|
||||||
|
|
||||||
|
```ruby
|
||||||
|
# app/channels/chat_channel.rb
|
||||||
|
class ChatChannel < ApplicationCable::Channel
|
||||||
|
def subscribed
|
||||||
|
stream_from "chat_#{params[:room]}"
|
||||||
|
end
|
||||||
|
end
|
||||||
|
```
|
||||||
|
|
||||||
|
If you pass an object as the first argument to `subscriptions.create`, that object will become the params hash in your cable channel. The keyword `channel` is required.
|
||||||
|
|
||||||
|
```coffeescript
|
||||||
|
# Client-side, which assumes you've already requested the right to send web notifications
|
||||||
|
App.cable.subscriptions.create { channel: "ChatChannel", room: "Best Room" },
|
||||||
|
received: (data) ->
|
||||||
|
@appendLine(data)
|
||||||
|
|
||||||
|
appendLine: (data) ->
|
||||||
|
html = @createLine(data)
|
||||||
|
$("[data-chat-room='Best Room']").append(html)
|
||||||
|
|
||||||
|
createLine: (data) ->
|
||||||
|
"""
|
||||||
|
<article class="chat-line">
|
||||||
|
<span class="speaker">#{data["sent_by"]}</span>
|
||||||
|
<span class="body">#{data["body"]}</span>
|
||||||
|
</article>
|
||||||
|
"""
|
||||||
|
```
|
||||||
|
|
||||||
|
```ruby
|
||||||
|
# Somewhere in your app this is called, perhaps from a NewCommentJob
|
||||||
|
ActionCable.server.broadcast \
|
||||||
|
"chat_#{room}", { sent_by: 'Paul', body: 'This is a cool chat app.' }
|
||||||
|
```
|
||||||
|
|
||||||
|
|
||||||
|
### Rebroadcasting message
|
||||||
|
|
||||||
|
A common use case is to rebroadcast a message sent by one client to any other connected clients.
|
||||||
|
|
||||||
|
```ruby
|
||||||
|
# app/channels/chat_channel.rb
|
||||||
|
class ChatChannel < ApplicationCable::Channel
|
||||||
|
def subscribed
|
||||||
|
stream_from "chat_#{params[:room]}"
|
||||||
|
end
|
||||||
|
|
||||||
|
def receive(data)
|
||||||
|
ActionCable.server.broadcast "chat_#{params[:room]}", data
|
||||||
|
end
|
||||||
|
end
|
||||||
|
```
|
||||||
|
|
||||||
|
```coffeescript
|
||||||
|
# Client-side, which assumes you've already requested the right to send web notifications
|
||||||
|
App.chatChannel = App.cable.subscriptions.create { channel: "ChatChannel", room: "Best Room" },
|
||||||
|
received: (data) ->
|
||||||
|
# data => { sent_by: "Paul", body: "This is a cool chat app." }
|
||||||
|
|
||||||
|
App.chatChannel.send({ sent_by: "Paul", body: "This is a cool chat app." })
|
||||||
|
```
|
||||||
|
|
||||||
|
The rebroadcast will be received by all connected clients, _including_ the client that sent the message. Note that params are the same as they were when you subscribed to the channel.
|
||||||
|
|
||||||
|
|
||||||
|
### More complete examples
|
||||||
|
|
||||||
|
See the [rails/actioncable-examples](https://github.com/rails/actioncable-examples) repository for a full example of how to setup Action Cable in a Rails app, and how to add channels.
|
||||||
|
|
||||||
|
## Configuration
|
||||||
|
|
||||||
|
Action Cable has three required configurations: a subscription adapter, allowed request origins, and the cable server URL (which can optionally be set on the client side).
|
||||||
|
|
||||||
|
### Redis
|
||||||
|
|
||||||
|
By default, `ActionCable::Server::Base` will look for a configuration file in `Rails.root.join('config/cable.yml')`.
|
||||||
|
This file must specify an adapter and a URL for each Rails environment. It may use the following format:
|
||||||
|
|
||||||
|
```yaml
|
||||||
|
production: &production
|
||||||
|
adapter: redis
|
||||||
|
url: redis://10.10.3.153:6381
|
||||||
|
development: &development
|
||||||
|
adapter: redis
|
||||||
|
url: redis://localhost:6379
|
||||||
|
test: *development
|
||||||
|
```
|
||||||
|
|
||||||
|
You can also change the location of the Action Cable config file in a Rails initializer with something like:
|
||||||
|
|
||||||
|
```ruby
|
||||||
|
Rails.application.paths.add "config/cable", with: "somewhere/else/cable.yml"
|
||||||
|
```
|
||||||
|
|
||||||
|
### Allowed Request Origins
|
||||||
|
|
||||||
|
Action Cable will only accept requests from specific origins.
|
||||||
|
|
||||||
|
By default, only an origin matching the cable server itself will be permitted.
|
||||||
|
Additional origins can be specified using strings or regular expressions, provided in an array.
|
||||||
|
|
||||||
|
```ruby
|
||||||
|
Rails.application.config.action_cable.allowed_request_origins = ['http://rubyonrails.com', /http:\/\/ruby.*/]
|
||||||
|
```
|
||||||
|
|
||||||
|
When running in the development environment, this defaults to "http://localhost:3000".
|
||||||
|
|
||||||
|
To disable protection and allow requests from any origin:
|
||||||
|
|
||||||
|
```ruby
|
||||||
|
Rails.application.config.action_cable.disable_request_forgery_protection = true
|
||||||
|
```
|
||||||
|
|
||||||
|
To disable automatic access for same-origin requests, and strictly allow
|
||||||
|
only the configured origins:
|
||||||
|
|
||||||
|
```ruby
|
||||||
|
Rails.application.config.action_cable.allow_same_origin_as_host = false
|
||||||
|
```
|
||||||
|
|
||||||
|
### Consumer Configuration
|
||||||
|
|
||||||
|
Once you have decided how to run your cable server (see below), you must provide the server URL (or path) to your client-side setup.
|
||||||
|
There are two ways you can do this.
|
||||||
|
|
||||||
|
The first is to simply pass it in when creating your consumer. For a standalone server,
|
||||||
|
this would be something like: `App.cable = ActionCable.createConsumer("ws://example.com:28080")`, and for an in-app server,
|
||||||
|
something like: `App.cable = ActionCable.createConsumer("/cable")`.
|
||||||
|
|
||||||
|
The second option is to pass the server URL through the `action_cable_meta_tag` in your layout.
|
||||||
|
This uses a URL or path typically set via `config.action_cable.url` in the environment configuration files, or defaults to "/cable".
|
||||||
|
|
||||||
|
This method is especially useful if your WebSocket URL might change between environments. If you host your production server via https, you will need to use the wss scheme
|
||||||
|
for your Action Cable server, but development might remain http and use the ws scheme. You might use localhost in development and your
|
||||||
|
domain in production.
|
||||||
|
|
||||||
|
In any case, to vary the WebSocket URL between environments, add the following configuration to each environment:
|
||||||
|
|
||||||
|
```ruby
|
||||||
|
config.action_cable.url = "ws://example.com:28080"
|
||||||
|
```
|
||||||
|
|
||||||
|
Then add the following line to your layout before your JavaScript tag:
|
||||||
|
|
||||||
|
```erb
|
||||||
|
<%= action_cable_meta_tag %>
|
||||||
|
```
|
||||||
|
|
||||||
|
And finally, create your consumer like so:
|
||||||
|
|
||||||
|
```coffeescript
|
||||||
|
App.cable = ActionCable.createConsumer()
|
||||||
|
```
|
||||||
|
|
||||||
|
### Other Configurations
|
||||||
|
|
||||||
|
The other common option to configure is the log tags applied to the per-connection logger. Here's an example that uses the user account id if available, else "no-account" while tagging:
|
||||||
|
|
||||||
|
```ruby
|
||||||
|
config.action_cable.log_tags = [
|
||||||
|
-> request { request.env['user_account_id'] || "no-account" },
|
||||||
|
:action_cable,
|
||||||
|
-> request { request.uuid }
|
||||||
|
]
|
||||||
|
```
|
||||||
|
|
||||||
|
For a full list of all configuration options, see the `ActionCable::Server::Configuration` class.
|
||||||
|
|
||||||
|
Also note that your server must provide at least the same number of database connections as you have workers. The default worker pool is set to 4, so that means you have to make at least that available. You can change that in `config/database.yml` through the `pool` attribute.
|
||||||
|
|
||||||
|
|
||||||
|
## Running the cable server
|
||||||
|
|
||||||
|
### Standalone
|
||||||
|
The cable server(s) is separated from your normal application server. It's still a Rack application, but it is its own Rack
|
||||||
|
application. The recommended basic setup is as follows:
|
||||||
|
|
||||||
|
```ruby
|
||||||
|
# cable/config.ru
|
||||||
|
require ::File.expand_path('../../config/environment', __FILE__)
|
||||||
|
Rails.application.eager_load!
|
||||||
|
|
||||||
|
run ActionCable.server
|
||||||
|
```
|
||||||
|
|
||||||
|
Then you start the server using a binstub in bin/cable ala:
|
||||||
|
```sh
|
||||||
|
#!/bin/bash
|
||||||
|
bundle exec puma -p 28080 cable/config.ru
|
||||||
|
```
|
||||||
|
|
||||||
|
The above will start a cable server on port 28080.
|
||||||
|
|
||||||
|
### In app
|
||||||
|
|
||||||
|
If you are using a server that supports the [Rack socket hijacking API](http://www.rubydoc.info/github/rack/rack/file/SPEC#Hijacking), Action Cable can run alongside your Rails application. For example, to listen for WebSocket requests on `/websocket`, specify that path to `config.action_cable.mount_path`:
|
||||||
|
|
||||||
|
```ruby
|
||||||
|
# config/application.rb
|
||||||
|
class Application < Rails::Application
|
||||||
|
config.action_cable.mount_path = '/websocket'
|
||||||
|
end
|
||||||
|
```
|
||||||
|
|
||||||
|
For every instance of your server you create and for every worker your server spawns, you will also have a new instance of Action Cable, but the use of Redis keeps messages synced across connections.
|
||||||
|
|
||||||
|
### Notes
|
||||||
|
|
||||||
|
Beware that currently, the cable server will _not_ auto-reload any changes in the framework. As we've discussed, long-running cable connections mean long-running objects. We don't yet have a way of reloading the classes of those objects in a safe manner. So when you change your channels, or the model your channels use, you must restart the cable server.
|
||||||
|
|
||||||
|
We'll get all this abstracted properly when the framework is integrated into Rails.
|
||||||
|
|
||||||
|
The WebSocket server doesn't have access to the session, but it has access to the cookies. This can be used when you need to handle authentication. You can see one way of doing that with Devise in this [article](http://www.rubytutorial.io/actioncable-devise-authentication).
|
||||||
|
|
||||||
|
## Dependencies
|
||||||
|
|
||||||
|
Action Cable provides a subscription adapter interface to process its pubsub internals. By default, asynchronous, inline, PostgreSQL, evented Redis, and non-evented Redis adapters are included. The default adapter in new Rails applications is the asynchronous (`async`) adapter. To create your own adapter, you can look at `ActionCable::SubscriptionAdapter::Base` for all methods that must be implemented, and any of the adapters included within Action Cable as example implementations.
|
||||||
|
|
||||||
|
The Ruby side of things is built on top of [websocket-driver](https://github.com/faye/websocket-driver-ruby), [nio4r](https://github.com/celluloid/nio4r), and [concurrent-ruby](https://github.com/ruby-concurrency/concurrent-ruby).
|
||||||
|
|
||||||
|
|
||||||
|
## Deployment
|
||||||
|
|
||||||
|
Action Cable is powered by a combination of WebSockets and threads. All of the
|
||||||
|
connection management is handled internally by utilizing Ruby’s native thread
|
||||||
|
support, which means you can use all your regular Rails models with no problems
|
||||||
|
as long as you haven’t committed any thread-safety sins.
|
||||||
|
|
||||||
|
The Action Cable server does _not_ need to be a multi-threaded application server.
|
||||||
|
This is because Action Cable uses the [Rack socket hijacking API](http://www.rubydoc.info/github/rack/rack/file/SPEC#Hijacking)
|
||||||
|
to take over control of connections from the application server. Action Cable
|
||||||
|
then manages connections internally, in a multithreaded manner, regardless of
|
||||||
|
whether the application server is multi-threaded or not. So Action Cable works
|
||||||
|
with all the popular application servers -- Unicorn, Puma and Passenger.
|
||||||
|
|
||||||
|
Action Cable does not work with WEBrick, because WEBrick does not support the
|
||||||
|
Rack socket hijacking API.
|
||||||
|
|
||||||
|
## Frontend assets
|
||||||
|
|
||||||
|
Action Cable's frontend assets are distributed through two channels: the
|
||||||
|
official gem and npm package, both titled `actioncable`.
|
||||||
|
|
||||||
|
### Gem usage
|
||||||
|
|
||||||
|
Through the `actioncable` gem, Action Cable's frontend assets are
|
||||||
|
available through the Rails Asset Pipeline. Create a `cable.js` or
|
||||||
|
`cable.coffee` file (this is automatically done for you with Rails
|
||||||
|
generators), and then simply require the assets:
|
||||||
|
|
||||||
|
In JavaScript...
|
||||||
|
|
||||||
|
```javascript
|
||||||
|
//= require action_cable
|
||||||
|
```
|
||||||
|
|
||||||
|
... and in CoffeeScript:
|
||||||
|
|
||||||
|
```coffeescript
|
||||||
|
#= require action_cable
|
||||||
|
```
|
||||||
|
|
||||||
|
### npm usage
|
||||||
|
|
||||||
|
In addition to being available through the `actioncable` gem, Action Cable's
|
||||||
|
frontend JS assets are also bundled in an officially supported npm module,
|
||||||
|
intended for usage in standalone frontend applications that communicate with a
|
||||||
|
Rails application. A common use case for this could be if you have a decoupled
|
||||||
|
frontend application written in React, Ember.js, etc. and want to add real-time
|
||||||
|
WebSocket functionality.
|
||||||
|
|
||||||
|
### Installation
|
||||||
|
|
||||||
|
```
|
||||||
|
npm install actioncable --save
|
||||||
|
```
|
||||||
|
|
||||||
|
### Usage
|
||||||
|
|
||||||
|
The `ActionCable` constant is available as a `require`-able module, so
|
||||||
|
you only have to require the package to gain access to the API that is
|
||||||
|
provided.
|
||||||
|
|
||||||
|
In JavaScript...
|
||||||
|
|
||||||
|
```javascript
|
||||||
|
ActionCable = require('actioncable')
|
||||||
|
|
||||||
|
var cable = ActionCable.createConsumer('wss://RAILS-API-PATH.com/cable')
|
||||||
|
|
||||||
|
cable.subscriptions.create('AppearanceChannel', {
|
||||||
|
// normal channel code goes here...
|
||||||
|
});
|
||||||
|
```
|
||||||
|
|
||||||
|
and in CoffeeScript...
|
||||||
|
|
||||||
|
```coffeescript
|
||||||
|
ActionCable = require('actioncable')
|
||||||
|
|
||||||
|
cable = ActionCable.createConsumer('wss://RAILS-API-PATH.com/cable')
|
||||||
|
|
||||||
|
cable.subscriptions.create 'AppearanceChannel',
|
||||||
|
# normal channel code goes here...
|
||||||
|
```
|
||||||
|
|
||||||
|
## Download and Installation
|
||||||
|
|
||||||
|
The latest version of Action Cable can be installed with [RubyGems](#gem-usage),
|
||||||
|
or with [npm](#npm-usage).
|
||||||
|
|
||||||
|
Source code can be downloaded as part of the Rails project on GitHub
|
||||||
|
|
||||||
|
* https://github.com/rails/rails/tree/master/actioncable
|
||||||
|
|
||||||
|
## License
|
||||||
|
|
||||||
|
Action Cable is released under the MIT license:
|
||||||
|
|
||||||
|
* http://www.opensource.org/licenses/MIT
|
||||||
|
|
||||||
|
|
||||||
|
## Support
|
||||||
|
|
||||||
|
API documentation is at:
|
||||||
|
|
||||||
|
* http://api.rubyonrails.org
|
||||||
|
|
||||||
|
Bug reports can be filed for the Ruby on Rails project here:
|
||||||
|
|
||||||
|
* https://github.com/rails/rails/issues
|
||||||
|
|
||||||
|
Feature requests should be discussed on the rails-core mailing list here:
|
||||||
|
|
||||||
|
* https://groups.google.com/forum/?fromgroups#!forum/rubyonrails-core
|
42
debian/gems-compat/actioncable-5.1.6.1/actioncable.gemspec
vendored
Normal file
42
debian/gems-compat/actioncable-5.1.6.1/actioncable.gemspec
vendored
Normal file
|
@ -0,0 +1,42 @@
|
||||||
|
#########################################################
|
||||||
|
# This file has been automatically generated by gem2tgz #
|
||||||
|
#########################################################
|
||||||
|
# -*- encoding: utf-8 -*-
|
||||||
|
# stub: actioncable 5.1.6.1 ruby lib
|
||||||
|
|
||||||
|
Gem::Specification.new do |s|
|
||||||
|
s.name = "actioncable".freeze
|
||||||
|
s.version = "5.1.6.1"
|
||||||
|
|
||||||
|
s.required_rubygems_version = Gem::Requirement.new(">= 0".freeze) if s.respond_to? :required_rubygems_version=
|
||||||
|
s.metadata = { "changelog_uri" => "https://github.com/rails/rails/blob/v5.1.6.1/actioncable/CHANGELOG.md", "source_code_uri" => "https://github.com/rails/rails/tree/v5.1.6.1/actioncable" } if s.respond_to? :metadata=
|
||||||
|
s.require_paths = ["lib".freeze]
|
||||||
|
s.authors = ["Pratik Naik".freeze, "David Heinemeier Hansson".freeze]
|
||||||
|
s.date = "2018-11-27"
|
||||||
|
s.description = "Structure many real-time application concerns into channels over a single WebSocket connection.".freeze
|
||||||
|
s.email = ["pratiknaik@gmail.com".freeze, "david@loudthinking.com".freeze]
|
||||||
|
s.files = ["CHANGELOG.md".freeze, "MIT-LICENSE".freeze, "README.md".freeze, "lib/action_cable.rb".freeze, "lib/action_cable/channel.rb".freeze, "lib/action_cable/channel/base.rb".freeze, "lib/action_cable/channel/broadcasting.rb".freeze, "lib/action_cable/channel/callbacks.rb".freeze, "lib/action_cable/channel/naming.rb".freeze, "lib/action_cable/channel/periodic_timers.rb".freeze, "lib/action_cable/channel/streams.rb".freeze, "lib/action_cable/connection.rb".freeze, "lib/action_cable/connection/authorization.rb".freeze, "lib/action_cable/connection/base.rb".freeze, "lib/action_cable/connection/client_socket.rb".freeze, "lib/action_cable/connection/identification.rb".freeze, "lib/action_cable/connection/internal_channel.rb".freeze, "lib/action_cable/connection/message_buffer.rb".freeze, "lib/action_cable/connection/stream.rb".freeze, "lib/action_cable/connection/stream_event_loop.rb".freeze, "lib/action_cable/connection/subscriptions.rb".freeze, "lib/action_cable/connection/tagged_logger_proxy.rb".freeze, "lib/action_cable/connection/web_socket.rb".freeze, "lib/action_cable/engine.rb".freeze, "lib/action_cable/gem_version.rb".freeze, "lib/action_cable/helpers/action_cable_helper.rb".freeze, "lib/action_cable/remote_connections.rb".freeze, "lib/action_cable/server.rb".freeze, "lib/action_cable/server/base.rb".freeze, "lib/action_cable/server/broadcasting.rb".freeze, "lib/action_cable/server/configuration.rb".freeze, "lib/action_cable/server/connections.rb".freeze, "lib/action_cable/server/worker.rb".freeze, "lib/action_cable/server/worker/active_record_connection_management.rb".freeze, "lib/action_cable/subscription_adapter.rb".freeze, "lib/action_cable/subscription_adapter/async.rb".freeze, "lib/action_cable/subscription_adapter/base.rb".freeze, "lib/action_cable/subscription_adapter/channel_prefix.rb".freeze, "lib/action_cable/subscription_adapter/evented_redis.rb".freeze, "lib/action_cable/subscription_adapter/inline.rb".freeze, "lib/action_cable/subscription_adapter/postgresql.rb".freeze, "lib/action_cable/subscription_adapter/redis.rb".freeze, "lib/action_cable/subscription_adapter/subscriber_map.rb".freeze, "lib/action_cable/version.rb".freeze, "lib/assets/compiled/action_cable.js".freeze, "lib/rails/generators/channel/USAGE".freeze, "lib/rails/generators/channel/channel_generator.rb".freeze, "lib/rails/generators/channel/templates/application_cable/channel.rb".freeze, "lib/rails/generators/channel/templates/application_cable/connection.rb".freeze, "lib/rails/generators/channel/templates/assets/cable.js".freeze, "lib/rails/generators/channel/templates/assets/channel.coffee".freeze, "lib/rails/generators/channel/templates/assets/channel.js".freeze, "lib/rails/generators/channel/templates/channel.rb".freeze]
|
||||||
|
s.homepage = "http://rubyonrails.org".freeze
|
||||||
|
s.licenses = ["MIT".freeze]
|
||||||
|
s.required_ruby_version = Gem::Requirement.new(">= 2.2.2".freeze)
|
||||||
|
s.rubygems_version = "2.7.6".freeze
|
||||||
|
s.summary = "WebSocket framework for Rails.".freeze
|
||||||
|
|
||||||
|
if s.respond_to? :specification_version then
|
||||||
|
s.specification_version = 4
|
||||||
|
|
||||||
|
if Gem::Version.new(Gem::VERSION) >= Gem::Version.new('1.2.0') then
|
||||||
|
s.add_runtime_dependency(%q<actionpack>.freeze, ["= 5.1.6.1"])
|
||||||
|
s.add_runtime_dependency(%q<nio4r>.freeze, ["~> 2.0"])
|
||||||
|
s.add_runtime_dependency(%q<websocket-driver>.freeze, ["~> 0.6.1"])
|
||||||
|
else
|
||||||
|
s.add_dependency(%q<actionpack>.freeze, ["= 5.1.6.1"])
|
||||||
|
s.add_dependency(%q<nio4r>.freeze, ["~> 2.0"])
|
||||||
|
s.add_dependency(%q<websocket-driver>.freeze, ["~> 0.6.1"])
|
||||||
|
end
|
||||||
|
else
|
||||||
|
s.add_dependency(%q<actionpack>.freeze, ["= 5.1.6.1"])
|
||||||
|
s.add_dependency(%q<nio4r>.freeze, ["~> 2.0"])
|
||||||
|
s.add_dependency(%q<websocket-driver>.freeze, ["~> 0.6.1"])
|
||||||
|
end
|
||||||
|
end
|
52
debian/gems-compat/actioncable-5.1.6.1/lib/action_cable.rb
vendored
Normal file
52
debian/gems-compat/actioncable-5.1.6.1/lib/action_cable.rb
vendored
Normal file
|
@ -0,0 +1,52 @@
|
||||||
|
#--
|
||||||
|
# Copyright (c) 2015-2017 Basecamp, LLC
|
||||||
|
#
|
||||||
|
# Permission is hereby granted, free of charge, to any person obtaining
|
||||||
|
# a copy of this software and associated documentation files (the
|
||||||
|
# "Software"), to deal in the Software without restriction, including
|
||||||
|
# without limitation the rights to use, copy, modify, merge, publish,
|
||||||
|
# distribute, sublicense, and/or sell copies of the Software, and to
|
||||||
|
# permit persons to whom the Software is furnished to do so, subject to
|
||||||
|
# the following conditions:
|
||||||
|
#
|
||||||
|
# The above copyright notice and this permission notice shall be
|
||||||
|
# included in all copies or substantial portions of the Software.
|
||||||
|
#
|
||||||
|
# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
|
||||||
|
# EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
|
||||||
|
# MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
|
||||||
|
# NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
|
||||||
|
# LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
|
||||||
|
# OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
|
||||||
|
# WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
||||||
|
#++
|
||||||
|
|
||||||
|
require "active_support"
|
||||||
|
require "active_support/rails"
|
||||||
|
require "action_cable/version"
|
||||||
|
|
||||||
|
module ActionCable
|
||||||
|
extend ActiveSupport::Autoload
|
||||||
|
|
||||||
|
INTERNAL = {
|
||||||
|
message_types: {
|
||||||
|
welcome: "welcome".freeze,
|
||||||
|
ping: "ping".freeze,
|
||||||
|
confirmation: "confirm_subscription".freeze,
|
||||||
|
rejection: "reject_subscription".freeze
|
||||||
|
},
|
||||||
|
default_mount_path: "/cable".freeze,
|
||||||
|
protocols: ["actioncable-v1-json".freeze, "actioncable-unsupported".freeze].freeze
|
||||||
|
}
|
||||||
|
|
||||||
|
# Singleton instance of the server
|
||||||
|
module_function def server
|
||||||
|
@server ||= ActionCable::Server::Base.new
|
||||||
|
end
|
||||||
|
|
||||||
|
autoload :Server
|
||||||
|
autoload :Connection
|
||||||
|
autoload :Channel
|
||||||
|
autoload :RemoteConnections
|
||||||
|
autoload :SubscriptionAdapter
|
||||||
|
end
|
14
debian/gems-compat/actioncable-5.1.6.1/lib/action_cable/channel.rb
vendored
Normal file
14
debian/gems-compat/actioncable-5.1.6.1/lib/action_cable/channel.rb
vendored
Normal file
|
@ -0,0 +1,14 @@
|
||||||
|
module ActionCable
|
||||||
|
module Channel
|
||||||
|
extend ActiveSupport::Autoload
|
||||||
|
|
||||||
|
eager_autoload do
|
||||||
|
autoload :Base
|
||||||
|
autoload :Broadcasting
|
||||||
|
autoload :Callbacks
|
||||||
|
autoload :Naming
|
||||||
|
autoload :PeriodicTimers
|
||||||
|
autoload :Streams
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
301
debian/gems-compat/actioncable-5.1.6.1/lib/action_cable/channel/base.rb
vendored
Normal file
301
debian/gems-compat/actioncable-5.1.6.1/lib/action_cable/channel/base.rb
vendored
Normal file
|
@ -0,0 +1,301 @@
|
||||||
|
require "set"
|
||||||
|
|
||||||
|
module ActionCable
|
||||||
|
module Channel
|
||||||
|
# The channel provides the basic structure of grouping behavior into logical units when communicating over the WebSocket connection.
|
||||||
|
# You can think of a channel like a form of controller, but one that's capable of pushing content to the subscriber in addition to simply
|
||||||
|
# responding to the subscriber's direct requests.
|
||||||
|
#
|
||||||
|
# Channel instances are long-lived. A channel object will be instantiated when the cable consumer becomes a subscriber, and then
|
||||||
|
# lives until the consumer disconnects. This may be seconds, minutes, hours, or even days. That means you have to take special care
|
||||||
|
# not to do anything silly in a channel that would balloon its memory footprint or whatever. The references are forever, so they won't be released
|
||||||
|
# as is normally the case with a controller instance that gets thrown away after every request.
|
||||||
|
#
|
||||||
|
# Long-lived channels (and connections) also mean you're responsible for ensuring that the data is fresh. If you hold a reference to a user
|
||||||
|
# record, but the name is changed while that reference is held, you may be sending stale data if you don't take precautions to avoid it.
|
||||||
|
#
|
||||||
|
# The upside of long-lived channel instances is that you can use instance variables to keep reference to objects that future subscriber requests
|
||||||
|
# can interact with. Here's a quick example:
|
||||||
|
#
|
||||||
|
# class ChatChannel < ApplicationCable::Channel
|
||||||
|
# def subscribed
|
||||||
|
# @room = Chat::Room[params[:room_number]]
|
||||||
|
# end
|
||||||
|
#
|
||||||
|
# def speak(data)
|
||||||
|
# @room.speak data, user: current_user
|
||||||
|
# end
|
||||||
|
# end
|
||||||
|
#
|
||||||
|
# The #speak action simply uses the Chat::Room object that was created when the channel was first subscribed to by the consumer when that
|
||||||
|
# subscriber wants to say something in the room.
|
||||||
|
#
|
||||||
|
# == Action processing
|
||||||
|
#
|
||||||
|
# Unlike subclasses of ActionController::Base, channels do not follow a RESTful
|
||||||
|
# constraint form for their actions. Instead, Action Cable operates through a
|
||||||
|
# remote-procedure call model. You can declare any public method on the
|
||||||
|
# channel (optionally taking a <tt>data</tt> argument), and this method is
|
||||||
|
# automatically exposed as callable to the client.
|
||||||
|
#
|
||||||
|
# Example:
|
||||||
|
#
|
||||||
|
# class AppearanceChannel < ApplicationCable::Channel
|
||||||
|
# def subscribed
|
||||||
|
# @connection_token = generate_connection_token
|
||||||
|
# end
|
||||||
|
#
|
||||||
|
# def unsubscribed
|
||||||
|
# current_user.disappear @connection_token
|
||||||
|
# end
|
||||||
|
#
|
||||||
|
# def appear(data)
|
||||||
|
# current_user.appear @connection_token, on: data['appearing_on']
|
||||||
|
# end
|
||||||
|
#
|
||||||
|
# def away
|
||||||
|
# current_user.away @connection_token
|
||||||
|
# end
|
||||||
|
#
|
||||||
|
# private
|
||||||
|
# def generate_connection_token
|
||||||
|
# SecureRandom.hex(36)
|
||||||
|
# end
|
||||||
|
# end
|
||||||
|
#
|
||||||
|
# In this example, the subscribed and unsubscribed methods are not callable methods, as they
|
||||||
|
# were already declared in ActionCable::Channel::Base, but <tt>#appear</tt>
|
||||||
|
# and <tt>#away</tt> are. <tt>#generate_connection_token</tt> is also not
|
||||||
|
# callable, since it's a private method. You'll see that appear accepts a data
|
||||||
|
# parameter, which it then uses as part of its model call. <tt>#away</tt>
|
||||||
|
# does not, since it's simply a trigger action.
|
||||||
|
#
|
||||||
|
# Also note that in this example, <tt>current_user</tt> is available because
|
||||||
|
# it was marked as an identifying attribute on the connection. All such
|
||||||
|
# identifiers will automatically create a delegation method of the same name
|
||||||
|
# on the channel instance.
|
||||||
|
#
|
||||||
|
# == Rejecting subscription requests
|
||||||
|
#
|
||||||
|
# A channel can reject a subscription request in the #subscribed callback by
|
||||||
|
# invoking the #reject method:
|
||||||
|
#
|
||||||
|
# class ChatChannel < ApplicationCable::Channel
|
||||||
|
# def subscribed
|
||||||
|
# @room = Chat::Room[params[:room_number]]
|
||||||
|
# reject unless current_user.can_access?(@room)
|
||||||
|
# end
|
||||||
|
# end
|
||||||
|
#
|
||||||
|
# In this example, the subscription will be rejected if the
|
||||||
|
# <tt>current_user</tt> does not have access to the chat room. On the
|
||||||
|
# client-side, the <tt>Channel#rejected</tt> callback will get invoked when
|
||||||
|
# the server rejects the subscription request.
|
||||||
|
class Base
|
||||||
|
include Callbacks
|
||||||
|
include PeriodicTimers
|
||||||
|
include Streams
|
||||||
|
include Naming
|
||||||
|
include Broadcasting
|
||||||
|
|
||||||
|
attr_reader :params, :connection, :identifier
|
||||||
|
delegate :logger, to: :connection
|
||||||
|
|
||||||
|
class << self
|
||||||
|
# A list of method names that should be considered actions. This
|
||||||
|
# includes all public instance methods on a channel, less
|
||||||
|
# any internal methods (defined on Base), adding back in
|
||||||
|
# any methods that are internal, but still exist on the class
|
||||||
|
# itself.
|
||||||
|
#
|
||||||
|
# ==== Returns
|
||||||
|
# * <tt>Set</tt> - A set of all methods that should be considered actions.
|
||||||
|
def action_methods
|
||||||
|
@action_methods ||= begin
|
||||||
|
# All public instance methods of this class, including ancestors
|
||||||
|
methods = (public_instance_methods(true) -
|
||||||
|
# Except for public instance methods of Base and its ancestors
|
||||||
|
ActionCable::Channel::Base.public_instance_methods(true) +
|
||||||
|
# Be sure to include shadowed public instance methods of this class
|
||||||
|
public_instance_methods(false)).uniq.map(&:to_s)
|
||||||
|
methods.to_set
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
private
|
||||||
|
# action_methods are cached and there is sometimes need to refresh
|
||||||
|
# them. ::clear_action_methods! allows you to do that, so next time
|
||||||
|
# you run action_methods, they will be recalculated.
|
||||||
|
def clear_action_methods! # :doc:
|
||||||
|
@action_methods = nil
|
||||||
|
end
|
||||||
|
|
||||||
|
# Refresh the cached action_methods when a new action_method is added.
|
||||||
|
def method_added(name) # :doc:
|
||||||
|
super
|
||||||
|
clear_action_methods!
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
def initialize(connection, identifier, params = {})
|
||||||
|
@connection = connection
|
||||||
|
@identifier = identifier
|
||||||
|
@params = params
|
||||||
|
|
||||||
|
# When a channel is streaming via pubsub, we want to delay the confirmation
|
||||||
|
# transmission until pubsub subscription is confirmed.
|
||||||
|
#
|
||||||
|
# The counter starts at 1 because it's awaiting a call to #subscribe_to_channel
|
||||||
|
@defer_subscription_confirmation_counter = Concurrent::AtomicFixnum.new(1)
|
||||||
|
|
||||||
|
@reject_subscription = nil
|
||||||
|
@subscription_confirmation_sent = nil
|
||||||
|
|
||||||
|
delegate_connection_identifiers
|
||||||
|
end
|
||||||
|
|
||||||
|
# Extract the action name from the passed data and process it via the channel. The process will ensure
|
||||||
|
# that the action requested is a public method on the channel declared by the user (so not one of the callbacks
|
||||||
|
# like #subscribed).
|
||||||
|
def perform_action(data)
|
||||||
|
action = extract_action(data)
|
||||||
|
|
||||||
|
if processable_action?(action)
|
||||||
|
payload = { channel_class: self.class.name, action: action, data: data }
|
||||||
|
ActiveSupport::Notifications.instrument("perform_action.action_cable", payload) do
|
||||||
|
dispatch_action(action, data)
|
||||||
|
end
|
||||||
|
else
|
||||||
|
logger.error "Unable to process #{action_signature(action, data)}"
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
# This method is called after subscription has been added to the connection
|
||||||
|
# and confirms or rejects the subscription.
|
||||||
|
def subscribe_to_channel
|
||||||
|
run_callbacks :subscribe do
|
||||||
|
subscribed
|
||||||
|
end
|
||||||
|
|
||||||
|
reject_subscription if subscription_rejected?
|
||||||
|
ensure_confirmation_sent
|
||||||
|
end
|
||||||
|
|
||||||
|
# Called by the cable connection when it's cut, so the channel has a chance to cleanup with callbacks.
|
||||||
|
# This method is not intended to be called directly by the user. Instead, overwrite the #unsubscribed callback.
|
||||||
|
def unsubscribe_from_channel # :nodoc:
|
||||||
|
run_callbacks :unsubscribe do
|
||||||
|
unsubscribed
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
private
|
||||||
|
# Called once a consumer has become a subscriber of the channel. Usually the place to setup any streams
|
||||||
|
# you want this channel to be sending to the subscriber.
|
||||||
|
def subscribed # :doc:
|
||||||
|
# Override in subclasses
|
||||||
|
end
|
||||||
|
|
||||||
|
# Called once a consumer has cut its cable connection. Can be used for cleaning up connections or marking
|
||||||
|
# users as offline or the like.
|
||||||
|
def unsubscribed # :doc:
|
||||||
|
# Override in subclasses
|
||||||
|
end
|
||||||
|
|
||||||
|
# Transmit a hash of data to the subscriber. The hash will automatically be wrapped in a JSON envelope with
|
||||||
|
# the proper channel identifier marked as the recipient.
|
||||||
|
def transmit(data, via: nil) # :doc:
|
||||||
|
logger.debug "#{self.class.name} transmitting #{data.inspect.truncate(300)}".tap { |m| m << " (via #{via})" if via }
|
||||||
|
|
||||||
|
payload = { channel_class: self.class.name, data: data, via: via }
|
||||||
|
ActiveSupport::Notifications.instrument("transmit.action_cable", payload) do
|
||||||
|
connection.transmit identifier: @identifier, message: data
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
def ensure_confirmation_sent # :doc:
|
||||||
|
return if subscription_rejected?
|
||||||
|
@defer_subscription_confirmation_counter.decrement
|
||||||
|
transmit_subscription_confirmation unless defer_subscription_confirmation?
|
||||||
|
end
|
||||||
|
|
||||||
|
def defer_subscription_confirmation! # :doc:
|
||||||
|
@defer_subscription_confirmation_counter.increment
|
||||||
|
end
|
||||||
|
|
||||||
|
def defer_subscription_confirmation? # :doc:
|
||||||
|
@defer_subscription_confirmation_counter.value > 0
|
||||||
|
end
|
||||||
|
|
||||||
|
def subscription_confirmation_sent? # :doc:
|
||||||
|
@subscription_confirmation_sent
|
||||||
|
end
|
||||||
|
|
||||||
|
def reject # :doc:
|
||||||
|
@reject_subscription = true
|
||||||
|
end
|
||||||
|
|
||||||
|
def subscription_rejected? # :doc:
|
||||||
|
@reject_subscription
|
||||||
|
end
|
||||||
|
|
||||||
|
def delegate_connection_identifiers
|
||||||
|
connection.identifiers.each do |identifier|
|
||||||
|
define_singleton_method(identifier) do
|
||||||
|
connection.send(identifier)
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
def extract_action(data)
|
||||||
|
(data["action"].presence || :receive).to_sym
|
||||||
|
end
|
||||||
|
|
||||||
|
def processable_action?(action)
|
||||||
|
self.class.action_methods.include?(action.to_s) unless subscription_rejected?
|
||||||
|
end
|
||||||
|
|
||||||
|
def dispatch_action(action, data)
|
||||||
|
logger.info action_signature(action, data)
|
||||||
|
|
||||||
|
if method(action).arity == 1
|
||||||
|
public_send action, data
|
||||||
|
else
|
||||||
|
public_send action
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
def action_signature(action, data)
|
||||||
|
"#{self.class.name}##{action}".tap do |signature|
|
||||||
|
if (arguments = data.except("action")).any?
|
||||||
|
signature << "(#{arguments.inspect})"
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
def transmit_subscription_confirmation
|
||||||
|
unless subscription_confirmation_sent?
|
||||||
|
logger.info "#{self.class.name} is transmitting the subscription confirmation"
|
||||||
|
|
||||||
|
ActiveSupport::Notifications.instrument("transmit_subscription_confirmation.action_cable", channel_class: self.class.name) do
|
||||||
|
connection.transmit identifier: @identifier, type: ActionCable::INTERNAL[:message_types][:confirmation]
|
||||||
|
@subscription_confirmation_sent = true
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
def reject_subscription
|
||||||
|
connection.subscriptions.remove_subscription self
|
||||||
|
transmit_subscription_rejection
|
||||||
|
end
|
||||||
|
|
||||||
|
def transmit_subscription_rejection
|
||||||
|
logger.info "#{self.class.name} is transmitting the subscription rejection"
|
||||||
|
|
||||||
|
ActiveSupport::Notifications.instrument("transmit_subscription_rejection.action_cable", channel_class: self.class.name) do
|
||||||
|
connection.transmit identifier: @identifier, type: ActionCable::INTERNAL[:message_types][:rejection]
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
29
debian/gems-compat/actioncable-5.1.6.1/lib/action_cable/channel/broadcasting.rb
vendored
Normal file
29
debian/gems-compat/actioncable-5.1.6.1/lib/action_cable/channel/broadcasting.rb
vendored
Normal file
|
@ -0,0 +1,29 @@
|
||||||
|
require "active_support/core_ext/object/to_param"
|
||||||
|
|
||||||
|
module ActionCable
|
||||||
|
module Channel
|
||||||
|
module Broadcasting
|
||||||
|
extend ActiveSupport::Concern
|
||||||
|
|
||||||
|
delegate :broadcasting_for, to: :class
|
||||||
|
|
||||||
|
class_methods do
|
||||||
|
# Broadcast a hash to a unique broadcasting for this <tt>model</tt> in this channel.
|
||||||
|
def broadcast_to(model, message)
|
||||||
|
ActionCable.server.broadcast(broadcasting_for([ channel_name, model ]), message)
|
||||||
|
end
|
||||||
|
|
||||||
|
def broadcasting_for(model) #:nodoc:
|
||||||
|
case
|
||||||
|
when model.is_a?(Array)
|
||||||
|
model.map { |m| broadcasting_for(m) }.join(":")
|
||||||
|
when model.respond_to?(:to_gid_param)
|
||||||
|
model.to_gid_param
|
||||||
|
else
|
||||||
|
model.to_param
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
35
debian/gems-compat/actioncable-5.1.6.1/lib/action_cable/channel/callbacks.rb
vendored
Normal file
35
debian/gems-compat/actioncable-5.1.6.1/lib/action_cable/channel/callbacks.rb
vendored
Normal file
|
@ -0,0 +1,35 @@
|
||||||
|
require "active_support/callbacks"
|
||||||
|
|
||||||
|
module ActionCable
|
||||||
|
module Channel
|
||||||
|
module Callbacks
|
||||||
|
extend ActiveSupport::Concern
|
||||||
|
include ActiveSupport::Callbacks
|
||||||
|
|
||||||
|
included do
|
||||||
|
define_callbacks :subscribe
|
||||||
|
define_callbacks :unsubscribe
|
||||||
|
end
|
||||||
|
|
||||||
|
class_methods do
|
||||||
|
def before_subscribe(*methods, &block)
|
||||||
|
set_callback(:subscribe, :before, *methods, &block)
|
||||||
|
end
|
||||||
|
|
||||||
|
def after_subscribe(*methods, &block)
|
||||||
|
set_callback(:subscribe, :after, *methods, &block)
|
||||||
|
end
|
||||||
|
alias_method :on_subscribe, :after_subscribe
|
||||||
|
|
||||||
|
def before_unsubscribe(*methods, &block)
|
||||||
|
set_callback(:unsubscribe, :before, *methods, &block)
|
||||||
|
end
|
||||||
|
|
||||||
|
def after_unsubscribe(*methods, &block)
|
||||||
|
set_callback(:unsubscribe, :after, *methods, &block)
|
||||||
|
end
|
||||||
|
alias_method :on_unsubscribe, :after_unsubscribe
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
23
debian/gems-compat/actioncable-5.1.6.1/lib/action_cable/channel/naming.rb
vendored
Normal file
23
debian/gems-compat/actioncable-5.1.6.1/lib/action_cable/channel/naming.rb
vendored
Normal file
|
@ -0,0 +1,23 @@
|
||||||
|
module ActionCable
|
||||||
|
module Channel
|
||||||
|
module Naming
|
||||||
|
extend ActiveSupport::Concern
|
||||||
|
|
||||||
|
class_methods do
|
||||||
|
# Returns the name of the channel, underscored, without the <tt>Channel</tt> ending.
|
||||||
|
# If the channel is in a namespace, then the namespaces are represented by single
|
||||||
|
# colon separators in the channel name.
|
||||||
|
#
|
||||||
|
# ChatChannel.channel_name # => 'chat'
|
||||||
|
# Chats::AppearancesChannel.channel_name # => 'chats:appearances'
|
||||||
|
# FooChats::BarAppearancesChannel.channel_name # => 'foo_chats:bar_appearances'
|
||||||
|
def channel_name
|
||||||
|
@channel_name ||= name.sub(/Channel$/, "").gsub("::", ":").underscore
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
# Delegates to the class' <tt>channel_name</tt>
|
||||||
|
delegate :channel_name, to: :class
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
77
debian/gems-compat/actioncable-5.1.6.1/lib/action_cable/channel/periodic_timers.rb
vendored
Normal file
77
debian/gems-compat/actioncable-5.1.6.1/lib/action_cable/channel/periodic_timers.rb
vendored
Normal file
|
@ -0,0 +1,77 @@
|
||||||
|
module ActionCable
|
||||||
|
module Channel
|
||||||
|
module PeriodicTimers
|
||||||
|
extend ActiveSupport::Concern
|
||||||
|
|
||||||
|
included do
|
||||||
|
class_attribute :periodic_timers, instance_reader: false
|
||||||
|
self.periodic_timers = []
|
||||||
|
|
||||||
|
after_subscribe :start_periodic_timers
|
||||||
|
after_unsubscribe :stop_periodic_timers
|
||||||
|
end
|
||||||
|
|
||||||
|
module ClassMethods
|
||||||
|
# Periodically performs a task on the channel, like updating an online
|
||||||
|
# user counter, polling a backend for new status messages, sending
|
||||||
|
# regular "heartbeat" messages, or doing some internal work and giving
|
||||||
|
# progress updates.
|
||||||
|
#
|
||||||
|
# Pass a method name or lambda argument or provide a block to call.
|
||||||
|
# Specify the calling period in seconds using the <tt>every:</tt>
|
||||||
|
# keyword argument.
|
||||||
|
#
|
||||||
|
# periodically :transmit_progress, every: 5.seconds
|
||||||
|
#
|
||||||
|
# periodically every: 3.minutes do
|
||||||
|
# transmit action: :update_count, count: current_count
|
||||||
|
# end
|
||||||
|
#
|
||||||
|
def periodically(callback_or_method_name = nil, every:, &block)
|
||||||
|
callback =
|
||||||
|
if block_given?
|
||||||
|
raise ArgumentError, "Pass a block or provide a callback arg, not both" if callback_or_method_name
|
||||||
|
block
|
||||||
|
else
|
||||||
|
case callback_or_method_name
|
||||||
|
when Proc
|
||||||
|
callback_or_method_name
|
||||||
|
when Symbol
|
||||||
|
-> { __send__ callback_or_method_name }
|
||||||
|
else
|
||||||
|
raise ArgumentError, "Expected a Symbol method name or a Proc, got #{callback_or_method_name.inspect}"
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
unless every.kind_of?(Numeric) && every > 0
|
||||||
|
raise ArgumentError, "Expected every: to be a positive number of seconds, got #{every.inspect}"
|
||||||
|
end
|
||||||
|
|
||||||
|
self.periodic_timers += [[ callback, every: every ]]
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
private
|
||||||
|
def active_periodic_timers
|
||||||
|
@active_periodic_timers ||= []
|
||||||
|
end
|
||||||
|
|
||||||
|
def start_periodic_timers
|
||||||
|
self.class.periodic_timers.each do |callback, options|
|
||||||
|
active_periodic_timers << start_periodic_timer(callback, every: options.fetch(:every))
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
def start_periodic_timer(callback, every:)
|
||||||
|
connection.server.event_loop.timer every do
|
||||||
|
connection.worker_pool.async_exec self, connection: connection, &callback
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
def stop_periodic_timers
|
||||||
|
active_periodic_timers.each { |timer| timer.shutdown }
|
||||||
|
active_periodic_timers.clear
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
174
debian/gems-compat/actioncable-5.1.6.1/lib/action_cable/channel/streams.rb
vendored
Normal file
174
debian/gems-compat/actioncable-5.1.6.1/lib/action_cable/channel/streams.rb
vendored
Normal file
|
@ -0,0 +1,174 @@
|
||||||
|
module ActionCable
|
||||||
|
module Channel
|
||||||
|
# Streams allow channels to route broadcastings to the subscriber. A broadcasting is, as discussed elsewhere, a pubsub queue where any data
|
||||||
|
# placed into it is automatically sent to the clients that are connected at that time. It's purely an online queue, though. If you're not
|
||||||
|
# streaming a broadcasting at the very moment it sends out an update, you will not get that update, even if you connect after it has been sent.
|
||||||
|
#
|
||||||
|
# Most commonly, the streamed broadcast is sent straight to the subscriber on the client-side. The channel just acts as a connector between
|
||||||
|
# the two parties (the broadcaster and the channel subscriber). Here's an example of a channel that allows subscribers to get all new
|
||||||
|
# comments on a given page:
|
||||||
|
#
|
||||||
|
# class CommentsChannel < ApplicationCable::Channel
|
||||||
|
# def follow(data)
|
||||||
|
# stream_from "comments_for_#{data['recording_id']}"
|
||||||
|
# end
|
||||||
|
#
|
||||||
|
# def unfollow
|
||||||
|
# stop_all_streams
|
||||||
|
# end
|
||||||
|
# end
|
||||||
|
#
|
||||||
|
# Based on the above example, the subscribers of this channel will get whatever data is put into the,
|
||||||
|
# let's say, <tt>comments_for_45</tt> broadcasting as soon as it's put there.
|
||||||
|
#
|
||||||
|
# An example broadcasting for this channel looks like so:
|
||||||
|
#
|
||||||
|
# ActionCable.server.broadcast "comments_for_45", author: 'DHH', content: 'Rails is just swell'
|
||||||
|
#
|
||||||
|
# If you have a stream that is related to a model, then the broadcasting used can be generated from the model and channel.
|
||||||
|
# The following example would subscribe to a broadcasting like <tt>comments:Z2lkOi8vVGVzdEFwcC9Qb3N0LzE</tt>.
|
||||||
|
#
|
||||||
|
# class CommentsChannel < ApplicationCable::Channel
|
||||||
|
# def subscribed
|
||||||
|
# post = Post.find(params[:id])
|
||||||
|
# stream_for post
|
||||||
|
# end
|
||||||
|
# end
|
||||||
|
#
|
||||||
|
# You can then broadcast to this channel using:
|
||||||
|
#
|
||||||
|
# CommentsChannel.broadcast_to(@post, @comment)
|
||||||
|
#
|
||||||
|
# If you don't just want to parlay the broadcast unfiltered to the subscriber, you can also supply a callback that lets you alter what is sent out.
|
||||||
|
# The below example shows how you can use this to provide performance introspection in the process:
|
||||||
|
#
|
||||||
|
# class ChatChannel < ApplicationCable::Channel
|
||||||
|
# def subscribed
|
||||||
|
# @room = Chat::Room[params[:room_number]]
|
||||||
|
#
|
||||||
|
# stream_for @room, coder: ActiveSupport::JSON do |message|
|
||||||
|
# if message['originated_at'].present?
|
||||||
|
# elapsed_time = (Time.now.to_f - message['originated_at']).round(2)
|
||||||
|
#
|
||||||
|
# ActiveSupport::Notifications.instrument :performance, measurement: 'Chat.message_delay', value: elapsed_time, action: :timing
|
||||||
|
# logger.info "Message took #{elapsed_time}s to arrive"
|
||||||
|
# end
|
||||||
|
#
|
||||||
|
# transmit message
|
||||||
|
# end
|
||||||
|
# end
|
||||||
|
# end
|
||||||
|
#
|
||||||
|
# You can stop streaming from all broadcasts by calling #stop_all_streams.
|
||||||
|
module Streams
|
||||||
|
extend ActiveSupport::Concern
|
||||||
|
|
||||||
|
included do
|
||||||
|
on_unsubscribe :stop_all_streams
|
||||||
|
end
|
||||||
|
|
||||||
|
# Start streaming from the named <tt>broadcasting</tt> pubsub queue. Optionally, you can pass a <tt>callback</tt> that'll be used
|
||||||
|
# instead of the default of just transmitting the updates straight to the subscriber.
|
||||||
|
# Pass <tt>coder: ActiveSupport::JSON</tt> to decode messages as JSON before passing to the callback.
|
||||||
|
# Defaults to <tt>coder: nil</tt> which does no decoding, passes raw messages.
|
||||||
|
def stream_from(broadcasting, callback = nil, coder: nil, &block)
|
||||||
|
broadcasting = String(broadcasting)
|
||||||
|
|
||||||
|
# Don't send the confirmation until pubsub#subscribe is successful
|
||||||
|
defer_subscription_confirmation!
|
||||||
|
|
||||||
|
# Build a stream handler by wrapping the user-provided callback with
|
||||||
|
# a decoder or defaulting to a JSON-decoding retransmitter.
|
||||||
|
handler = worker_pool_stream_handler(broadcasting, callback || block, coder: coder)
|
||||||
|
streams << [ broadcasting, handler ]
|
||||||
|
|
||||||
|
connection.server.event_loop.post do
|
||||||
|
pubsub.subscribe(broadcasting, handler, lambda do
|
||||||
|
ensure_confirmation_sent
|
||||||
|
logger.info "#{self.class.name} is streaming from #{broadcasting}"
|
||||||
|
end)
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
# Start streaming the pubsub queue for the <tt>model</tt> in this channel. Optionally, you can pass a
|
||||||
|
# <tt>callback</tt> that'll be used instead of the default of just transmitting the updates straight
|
||||||
|
# to the subscriber.
|
||||||
|
#
|
||||||
|
# Pass <tt>coder: ActiveSupport::JSON</tt> to decode messages as JSON before passing to the callback.
|
||||||
|
# Defaults to <tt>coder: nil</tt> which does no decoding, passes raw messages.
|
||||||
|
def stream_for(model, callback = nil, coder: nil, &block)
|
||||||
|
stream_from(broadcasting_for([ channel_name, model ]), callback || block, coder: coder)
|
||||||
|
end
|
||||||
|
|
||||||
|
# Unsubscribes all streams associated with this channel from the pubsub queue.
|
||||||
|
def stop_all_streams
|
||||||
|
streams.each do |broadcasting, callback|
|
||||||
|
pubsub.unsubscribe broadcasting, callback
|
||||||
|
logger.info "#{self.class.name} stopped streaming from #{broadcasting}"
|
||||||
|
end.clear
|
||||||
|
end
|
||||||
|
|
||||||
|
private
|
||||||
|
delegate :pubsub, to: :connection
|
||||||
|
|
||||||
|
def streams
|
||||||
|
@_streams ||= []
|
||||||
|
end
|
||||||
|
|
||||||
|
# Always wrap the outermost handler to invoke the user handler on the
|
||||||
|
# worker pool rather than blocking the event loop.
|
||||||
|
def worker_pool_stream_handler(broadcasting, user_handler, coder: nil)
|
||||||
|
handler = stream_handler(broadcasting, user_handler, coder: coder)
|
||||||
|
|
||||||
|
-> message do
|
||||||
|
connection.worker_pool.async_invoke handler, :call, message, connection: connection
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
# May be overridden to add instrumentation, logging, specialized error
|
||||||
|
# handling, or other forms of handler decoration.
|
||||||
|
#
|
||||||
|
# TODO: Tests demonstrating this.
|
||||||
|
def stream_handler(broadcasting, user_handler, coder: nil)
|
||||||
|
if user_handler
|
||||||
|
stream_decoder user_handler, coder: coder
|
||||||
|
else
|
||||||
|
default_stream_handler broadcasting, coder: coder
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
# May be overridden to change the default stream handling behavior
|
||||||
|
# which decodes JSON and transmits to the client.
|
||||||
|
#
|
||||||
|
# TODO: Tests demonstrating this.
|
||||||
|
#
|
||||||
|
# TODO: Room for optimization. Update transmit API to be coder-aware
|
||||||
|
# so we can no-op when pubsub and connection are both JSON-encoded.
|
||||||
|
# Then we can skip decode+encode if we're just proxying messages.
|
||||||
|
def default_stream_handler(broadcasting, coder:)
|
||||||
|
coder ||= ActiveSupport::JSON
|
||||||
|
stream_transmitter stream_decoder(coder: coder), broadcasting: broadcasting
|
||||||
|
end
|
||||||
|
|
||||||
|
def stream_decoder(handler = identity_handler, coder:)
|
||||||
|
if coder
|
||||||
|
-> message { handler.(coder.decode(message)) }
|
||||||
|
else
|
||||||
|
handler
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
def stream_transmitter(handler = identity_handler, broadcasting:)
|
||||||
|
via = "streamed from #{broadcasting}"
|
||||||
|
|
||||||
|
-> (message) do
|
||||||
|
transmit handler.(message), via: via
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
def identity_handler
|
||||||
|
-> message { message }
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
19
debian/gems-compat/actioncable-5.1.6.1/lib/action_cable/connection.rb
vendored
Normal file
19
debian/gems-compat/actioncable-5.1.6.1/lib/action_cable/connection.rb
vendored
Normal file
|
@ -0,0 +1,19 @@
|
||||||
|
module ActionCable
|
||||||
|
module Connection
|
||||||
|
extend ActiveSupport::Autoload
|
||||||
|
|
||||||
|
eager_autoload do
|
||||||
|
autoload :Authorization
|
||||||
|
autoload :Base
|
||||||
|
autoload :ClientSocket
|
||||||
|
autoload :Identification
|
||||||
|
autoload :InternalChannel
|
||||||
|
autoload :MessageBuffer
|
||||||
|
autoload :Stream
|
||||||
|
autoload :StreamEventLoop
|
||||||
|
autoload :Subscriptions
|
||||||
|
autoload :TaggedLoggerProxy
|
||||||
|
autoload :WebSocket
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
13
debian/gems-compat/actioncable-5.1.6.1/lib/action_cable/connection/authorization.rb
vendored
Normal file
13
debian/gems-compat/actioncable-5.1.6.1/lib/action_cable/connection/authorization.rb
vendored
Normal file
|
@ -0,0 +1,13 @@
|
||||||
|
module ActionCable
|
||||||
|
module Connection
|
||||||
|
module Authorization
|
||||||
|
class UnauthorizedError < StandardError; end
|
||||||
|
|
||||||
|
# Closes the \WebSocket connection if it is open and returns a 404 "File not Found" response.
|
||||||
|
def reject_unauthorized_connection
|
||||||
|
logger.error "An unauthorized connection attempt was rejected"
|
||||||
|
raise UnauthorizedError
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
258
debian/gems-compat/actioncable-5.1.6.1/lib/action_cable/connection/base.rb
vendored
Normal file
258
debian/gems-compat/actioncable-5.1.6.1/lib/action_cable/connection/base.rb
vendored
Normal file
|
@ -0,0 +1,258 @@
|
||||||
|
require "action_dispatch"
|
||||||
|
|
||||||
|
module ActionCable
|
||||||
|
module Connection
|
||||||
|
# For every WebSocket connection the Action Cable server accepts, a Connection object will be instantiated. This instance becomes the parent
|
||||||
|
# of all of the channel subscriptions that are created from there on. Incoming messages are then routed to these channel subscriptions
|
||||||
|
# based on an identifier sent by the Action Cable consumer. The Connection itself does not deal with any specific application logic beyond
|
||||||
|
# authentication and authorization.
|
||||||
|
#
|
||||||
|
# Here's a basic example:
|
||||||
|
#
|
||||||
|
# module ApplicationCable
|
||||||
|
# class Connection < ActionCable::Connection::Base
|
||||||
|
# identified_by :current_user
|
||||||
|
#
|
||||||
|
# def connect
|
||||||
|
# self.current_user = find_verified_user
|
||||||
|
# logger.add_tags current_user.name
|
||||||
|
# end
|
||||||
|
#
|
||||||
|
# def disconnect
|
||||||
|
# # Any cleanup work needed when the cable connection is cut.
|
||||||
|
# end
|
||||||
|
#
|
||||||
|
# private
|
||||||
|
# def find_verified_user
|
||||||
|
# User.find_by_identity(cookies.signed[:identity_id]) ||
|
||||||
|
# reject_unauthorized_connection
|
||||||
|
# end
|
||||||
|
# end
|
||||||
|
# end
|
||||||
|
#
|
||||||
|
# First, we declare that this connection can be identified by its current_user. This allows us to later be able to find all connections
|
||||||
|
# established for that current_user (and potentially disconnect them). You can declare as many
|
||||||
|
# identification indexes as you like. Declaring an identification means that an attr_accessor is automatically set for that key.
|
||||||
|
#
|
||||||
|
# Second, we rely on the fact that the WebSocket connection is established with the cookies from the domain being sent along. This makes
|
||||||
|
# it easy to use signed cookies that were set when logging in via a web interface to authorize the WebSocket connection.
|
||||||
|
#
|
||||||
|
# Finally, we add a tag to the connection-specific logger with the name of the current user to easily distinguish their messages in the log.
|
||||||
|
#
|
||||||
|
# Pretty simple, eh?
|
||||||
|
class Base
|
||||||
|
include Identification
|
||||||
|
include InternalChannel
|
||||||
|
include Authorization
|
||||||
|
|
||||||
|
attr_reader :server, :env, :subscriptions, :logger, :worker_pool, :protocol
|
||||||
|
delegate :event_loop, :pubsub, to: :server
|
||||||
|
|
||||||
|
def initialize(server, env, coder: ActiveSupport::JSON)
|
||||||
|
@server, @env, @coder = server, env, coder
|
||||||
|
|
||||||
|
@worker_pool = server.worker_pool
|
||||||
|
@logger = new_tagged_logger
|
||||||
|
|
||||||
|
@websocket = ActionCable::Connection::WebSocket.new(env, self, event_loop)
|
||||||
|
@subscriptions = ActionCable::Connection::Subscriptions.new(self)
|
||||||
|
@message_buffer = ActionCable::Connection::MessageBuffer.new(self)
|
||||||
|
|
||||||
|
@_internal_subscriptions = nil
|
||||||
|
@started_at = Time.now
|
||||||
|
end
|
||||||
|
|
||||||
|
# Called by the server when a new WebSocket connection is established. This configures the callbacks intended for overwriting by the user.
|
||||||
|
# This method should not be called directly -- instead rely upon on the #connect (and #disconnect) callbacks.
|
||||||
|
def process #:nodoc:
|
||||||
|
logger.info started_request_message
|
||||||
|
|
||||||
|
if websocket.possible? && allow_request_origin?
|
||||||
|
respond_to_successful_request
|
||||||
|
else
|
||||||
|
respond_to_invalid_request
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
# Decodes WebSocket messages and dispatches them to subscribed channels.
|
||||||
|
# WebSocket message transfer encoding is always JSON.
|
||||||
|
def receive(websocket_message) #:nodoc:
|
||||||
|
send_async :dispatch_websocket_message, websocket_message
|
||||||
|
end
|
||||||
|
|
||||||
|
def dispatch_websocket_message(websocket_message) #:nodoc:
|
||||||
|
if websocket.alive?
|
||||||
|
subscriptions.execute_command decode(websocket_message)
|
||||||
|
else
|
||||||
|
logger.error "Ignoring message processed after the WebSocket was closed: #{websocket_message.inspect})"
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
def transmit(cable_message) # :nodoc:
|
||||||
|
websocket.transmit encode(cable_message)
|
||||||
|
end
|
||||||
|
|
||||||
|
# Close the WebSocket connection.
|
||||||
|
def close
|
||||||
|
websocket.close
|
||||||
|
end
|
||||||
|
|
||||||
|
# Invoke a method on the connection asynchronously through the pool of thread workers.
|
||||||
|
def send_async(method, *arguments)
|
||||||
|
worker_pool.async_invoke(self, method, *arguments)
|
||||||
|
end
|
||||||
|
|
||||||
|
# Return a basic hash of statistics for the connection keyed with <tt>identifier</tt>, <tt>started_at</tt>, <tt>subscriptions</tt>, and <tt>request_id</tt>.
|
||||||
|
# This can be returned by a health check against the connection.
|
||||||
|
def statistics
|
||||||
|
{
|
||||||
|
identifier: connection_identifier,
|
||||||
|
started_at: @started_at,
|
||||||
|
subscriptions: subscriptions.identifiers,
|
||||||
|
request_id: @env["action_dispatch.request_id"]
|
||||||
|
}
|
||||||
|
end
|
||||||
|
|
||||||
|
def beat
|
||||||
|
transmit type: ActionCable::INTERNAL[:message_types][:ping], message: Time.now.to_i
|
||||||
|
end
|
||||||
|
|
||||||
|
def on_open # :nodoc:
|
||||||
|
send_async :handle_open
|
||||||
|
end
|
||||||
|
|
||||||
|
def on_message(message) # :nodoc:
|
||||||
|
message_buffer.append message
|
||||||
|
end
|
||||||
|
|
||||||
|
def on_error(message) # :nodoc:
|
||||||
|
# log errors to make diagnosing socket errors easier
|
||||||
|
logger.error "WebSocket error occurred: #{message}"
|
||||||
|
end
|
||||||
|
|
||||||
|
def on_close(reason, code) # :nodoc:
|
||||||
|
send_async :handle_close
|
||||||
|
end
|
||||||
|
|
||||||
|
# TODO Change this to private once we've dropped Ruby 2.2 support.
|
||||||
|
# Workaround for Ruby 2.2 "private attribute?" warning.
|
||||||
|
protected
|
||||||
|
attr_reader :websocket
|
||||||
|
attr_reader :message_buffer
|
||||||
|
|
||||||
|
private
|
||||||
|
# The request that initiated the WebSocket connection is available here. This gives access to the environment, cookies, etc.
|
||||||
|
def request # :doc:
|
||||||
|
@request ||= begin
|
||||||
|
environment = Rails.application.env_config.merge(env) if defined?(Rails.application) && Rails.application
|
||||||
|
ActionDispatch::Request.new(environment || env)
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
# The cookies of the request that initiated the WebSocket connection. Useful for performing authorization checks.
|
||||||
|
def cookies # :doc:
|
||||||
|
request.cookie_jar
|
||||||
|
end
|
||||||
|
|
||||||
|
def encode(cable_message)
|
||||||
|
@coder.encode cable_message
|
||||||
|
end
|
||||||
|
|
||||||
|
def decode(websocket_message)
|
||||||
|
@coder.decode websocket_message
|
||||||
|
end
|
||||||
|
|
||||||
|
def handle_open
|
||||||
|
@protocol = websocket.protocol
|
||||||
|
connect if respond_to?(:connect)
|
||||||
|
subscribe_to_internal_channel
|
||||||
|
send_welcome_message
|
||||||
|
|
||||||
|
message_buffer.process!
|
||||||
|
server.add_connection(self)
|
||||||
|
rescue ActionCable::Connection::Authorization::UnauthorizedError
|
||||||
|
respond_to_invalid_request
|
||||||
|
end
|
||||||
|
|
||||||
|
def handle_close
|
||||||
|
logger.info finished_request_message
|
||||||
|
|
||||||
|
server.remove_connection(self)
|
||||||
|
|
||||||
|
subscriptions.unsubscribe_from_all
|
||||||
|
unsubscribe_from_internal_channel
|
||||||
|
|
||||||
|
disconnect if respond_to?(:disconnect)
|
||||||
|
end
|
||||||
|
|
||||||
|
def send_welcome_message
|
||||||
|
# Send welcome message to the internal connection monitor channel.
|
||||||
|
# This ensures the connection monitor state is reset after a successful
|
||||||
|
# websocket connection.
|
||||||
|
transmit type: ActionCable::INTERNAL[:message_types][:welcome]
|
||||||
|
end
|
||||||
|
|
||||||
|
def allow_request_origin?
|
||||||
|
return true if server.config.disable_request_forgery_protection
|
||||||
|
|
||||||
|
proto = Rack::Request.new(env).ssl? ? "https" : "http"
|
||||||
|
if server.config.allow_same_origin_as_host && env["HTTP_ORIGIN"] == "#{proto}://#{env['HTTP_HOST']}"
|
||||||
|
true
|
||||||
|
elsif Array(server.config.allowed_request_origins).any? { |allowed_origin| allowed_origin === env["HTTP_ORIGIN"] }
|
||||||
|
true
|
||||||
|
else
|
||||||
|
logger.error("Request origin not allowed: #{env['HTTP_ORIGIN']}")
|
||||||
|
false
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
def respond_to_successful_request
|
||||||
|
logger.info successful_request_message
|
||||||
|
websocket.rack_response
|
||||||
|
end
|
||||||
|
|
||||||
|
def respond_to_invalid_request
|
||||||
|
close if websocket.alive?
|
||||||
|
|
||||||
|
logger.error invalid_request_message
|
||||||
|
logger.info finished_request_message
|
||||||
|
[ 404, { "Content-Type" => "text/plain" }, [ "Page not found" ] ]
|
||||||
|
end
|
||||||
|
|
||||||
|
# Tags are declared in the server but computed in the connection. This allows us per-connection tailored tags.
|
||||||
|
def new_tagged_logger
|
||||||
|
TaggedLoggerProxy.new server.logger,
|
||||||
|
tags: server.config.log_tags.map { |tag| tag.respond_to?(:call) ? tag.call(request) : tag.to_s.camelize }
|
||||||
|
end
|
||||||
|
|
||||||
|
def started_request_message
|
||||||
|
'Started %s "%s"%s for %s at %s' % [
|
||||||
|
request.request_method,
|
||||||
|
request.filtered_path,
|
||||||
|
websocket.possible? ? " [WebSocket]" : "[non-WebSocket]",
|
||||||
|
request.ip,
|
||||||
|
Time.now.to_s ]
|
||||||
|
end
|
||||||
|
|
||||||
|
def finished_request_message
|
||||||
|
'Finished "%s"%s for %s at %s' % [
|
||||||
|
request.filtered_path,
|
||||||
|
websocket.possible? ? " [WebSocket]" : "[non-WebSocket]",
|
||||||
|
request.ip,
|
||||||
|
Time.now.to_s ]
|
||||||
|
end
|
||||||
|
|
||||||
|
def invalid_request_message
|
||||||
|
"Failed to upgrade to WebSocket (REQUEST_METHOD: %s, HTTP_CONNECTION: %s, HTTP_UPGRADE: %s)" % [
|
||||||
|
env["REQUEST_METHOD"], env["HTTP_CONNECTION"], env["HTTP_UPGRADE"]
|
||||||
|
]
|
||||||
|
end
|
||||||
|
|
||||||
|
def successful_request_message
|
||||||
|
"Successfully upgraded to WebSocket (REQUEST_METHOD: %s, HTTP_CONNECTION: %s, HTTP_UPGRADE: %s)" % [
|
||||||
|
env["REQUEST_METHOD"], env["HTTP_CONNECTION"], env["HTTP_UPGRADE"]
|
||||||
|
]
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
155
debian/gems-compat/actioncable-5.1.6.1/lib/action_cable/connection/client_socket.rb
vendored
Normal file
155
debian/gems-compat/actioncable-5.1.6.1/lib/action_cable/connection/client_socket.rb
vendored
Normal file
|
@ -0,0 +1,155 @@
|
||||||
|
require "websocket/driver"
|
||||||
|
|
||||||
|
module ActionCable
|
||||||
|
module Connection
|
||||||
|
#--
|
||||||
|
# This class is heavily based on faye-websocket-ruby
|
||||||
|
#
|
||||||
|
# Copyright (c) 2010-2015 James Coglan
|
||||||
|
class ClientSocket # :nodoc:
|
||||||
|
def self.determine_url(env)
|
||||||
|
scheme = secure_request?(env) ? "wss:" : "ws:"
|
||||||
|
"#{ scheme }//#{ env['HTTP_HOST'] }#{ env['REQUEST_URI'] }"
|
||||||
|
end
|
||||||
|
|
||||||
|
def self.secure_request?(env)
|
||||||
|
return true if env["HTTPS"] == "on"
|
||||||
|
return true if env["HTTP_X_FORWARDED_SSL"] == "on"
|
||||||
|
return true if env["HTTP_X_FORWARDED_SCHEME"] == "https"
|
||||||
|
return true if env["HTTP_X_FORWARDED_PROTO"] == "https"
|
||||||
|
return true if env["rack.url_scheme"] == "https"
|
||||||
|
|
||||||
|
return false
|
||||||
|
end
|
||||||
|
|
||||||
|
CONNECTING = 0
|
||||||
|
OPEN = 1
|
||||||
|
CLOSING = 2
|
||||||
|
CLOSED = 3
|
||||||
|
|
||||||
|
attr_reader :env, :url
|
||||||
|
|
||||||
|
def initialize(env, event_target, event_loop, protocols)
|
||||||
|
@env = env
|
||||||
|
@event_target = event_target
|
||||||
|
@event_loop = event_loop
|
||||||
|
|
||||||
|
@url = ClientSocket.determine_url(@env)
|
||||||
|
|
||||||
|
@driver = @driver_started = nil
|
||||||
|
@close_params = ["", 1006]
|
||||||
|
|
||||||
|
@ready_state = CONNECTING
|
||||||
|
|
||||||
|
# The driver calls +env+, +url+, and +write+
|
||||||
|
@driver = ::WebSocket::Driver.rack(self, protocols: protocols)
|
||||||
|
|
||||||
|
@driver.on(:open) { |e| open }
|
||||||
|
@driver.on(:message) { |e| receive_message(e.data) }
|
||||||
|
@driver.on(:close) { |e| begin_close(e.reason, e.code) }
|
||||||
|
@driver.on(:error) { |e| emit_error(e.message) }
|
||||||
|
|
||||||
|
@stream = ActionCable::Connection::Stream.new(@event_loop, self)
|
||||||
|
end
|
||||||
|
|
||||||
|
def start_driver
|
||||||
|
return if @driver.nil? || @driver_started
|
||||||
|
@stream.hijack_rack_socket
|
||||||
|
|
||||||
|
if callback = @env["async.callback"]
|
||||||
|
callback.call([101, {}, @stream])
|
||||||
|
end
|
||||||
|
|
||||||
|
@driver_started = true
|
||||||
|
@driver.start
|
||||||
|
end
|
||||||
|
|
||||||
|
def rack_response
|
||||||
|
start_driver
|
||||||
|
[ -1, {}, [] ]
|
||||||
|
end
|
||||||
|
|
||||||
|
def write(data)
|
||||||
|
@stream.write(data)
|
||||||
|
rescue => e
|
||||||
|
emit_error e.message
|
||||||
|
end
|
||||||
|
|
||||||
|
def transmit(message)
|
||||||
|
return false if @ready_state > OPEN
|
||||||
|
case message
|
||||||
|
when Numeric then @driver.text(message.to_s)
|
||||||
|
when String then @driver.text(message)
|
||||||
|
when Array then @driver.binary(message)
|
||||||
|
else false
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
def close(code = nil, reason = nil)
|
||||||
|
code ||= 1000
|
||||||
|
reason ||= ""
|
||||||
|
|
||||||
|
unless code == 1000 || (code >= 3000 && code <= 4999)
|
||||||
|
raise ArgumentError, "Failed to execute 'close' on WebSocket: " \
|
||||||
|
"The code must be either 1000, or between 3000 and 4999. " \
|
||||||
|
"#{code} is neither."
|
||||||
|
end
|
||||||
|
|
||||||
|
@ready_state = CLOSING unless @ready_state == CLOSED
|
||||||
|
@driver.close(reason, code)
|
||||||
|
end
|
||||||
|
|
||||||
|
def parse(data)
|
||||||
|
@driver.parse(data)
|
||||||
|
end
|
||||||
|
|
||||||
|
def client_gone
|
||||||
|
finalize_close
|
||||||
|
end
|
||||||
|
|
||||||
|
def alive?
|
||||||
|
@ready_state == OPEN
|
||||||
|
end
|
||||||
|
|
||||||
|
def protocol
|
||||||
|
@driver.protocol
|
||||||
|
end
|
||||||
|
|
||||||
|
private
|
||||||
|
def open
|
||||||
|
return unless @ready_state == CONNECTING
|
||||||
|
@ready_state = OPEN
|
||||||
|
|
||||||
|
@event_target.on_open
|
||||||
|
end
|
||||||
|
|
||||||
|
def receive_message(data)
|
||||||
|
return unless @ready_state == OPEN
|
||||||
|
|
||||||
|
@event_target.on_message(data)
|
||||||
|
end
|
||||||
|
|
||||||
|
def emit_error(message)
|
||||||
|
return if @ready_state >= CLOSING
|
||||||
|
|
||||||
|
@event_target.on_error(message)
|
||||||
|
end
|
||||||
|
|
||||||
|
def begin_close(reason, code)
|
||||||
|
return if @ready_state == CLOSED
|
||||||
|
@ready_state = CLOSING
|
||||||
|
@close_params = [reason, code]
|
||||||
|
|
||||||
|
@stream.shutdown if @stream
|
||||||
|
finalize_close
|
||||||
|
end
|
||||||
|
|
||||||
|
def finalize_close
|
||||||
|
return if @ready_state == CLOSED
|
||||||
|
@ready_state = CLOSED
|
||||||
|
|
||||||
|
@event_target.on_close(*@close_params)
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
46
debian/gems-compat/actioncable-5.1.6.1/lib/action_cable/connection/identification.rb
vendored
Normal file
46
debian/gems-compat/actioncable-5.1.6.1/lib/action_cable/connection/identification.rb
vendored
Normal file
|
@ -0,0 +1,46 @@
|
||||||
|
require "set"
|
||||||
|
|
||||||
|
module ActionCable
|
||||||
|
module Connection
|
||||||
|
module Identification
|
||||||
|
extend ActiveSupport::Concern
|
||||||
|
|
||||||
|
included do
|
||||||
|
class_attribute :identifiers
|
||||||
|
self.identifiers = Set.new
|
||||||
|
end
|
||||||
|
|
||||||
|
class_methods do
|
||||||
|
# Mark a key as being a connection identifier index that can then be used to find the specific connection again later.
|
||||||
|
# Common identifiers are current_user and current_account, but could be anything, really.
|
||||||
|
#
|
||||||
|
# Note that anything marked as an identifier will automatically create a delegate by the same name on any
|
||||||
|
# channel instances created off the connection.
|
||||||
|
def identified_by(*identifiers)
|
||||||
|
Array(identifiers).each { |identifier| attr_accessor identifier }
|
||||||
|
self.identifiers += identifiers
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
# Return a single connection identifier that combines the value of all the registered identifiers into a single gid.
|
||||||
|
def connection_identifier
|
||||||
|
unless defined? @connection_identifier
|
||||||
|
@connection_identifier = connection_gid identifiers.map { |id| instance_variable_get("@#{id}") }.compact
|
||||||
|
end
|
||||||
|
|
||||||
|
@connection_identifier
|
||||||
|
end
|
||||||
|
|
||||||
|
private
|
||||||
|
def connection_gid(ids)
|
||||||
|
ids.map do |o|
|
||||||
|
if o.respond_to? :to_gid_param
|
||||||
|
o.to_gid_param
|
||||||
|
else
|
||||||
|
o.to_s
|
||||||
|
end
|
||||||
|
end.sort.join(":")
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
43
debian/gems-compat/actioncable-5.1.6.1/lib/action_cable/connection/internal_channel.rb
vendored
Normal file
43
debian/gems-compat/actioncable-5.1.6.1/lib/action_cable/connection/internal_channel.rb
vendored
Normal file
|
@ -0,0 +1,43 @@
|
||||||
|
module ActionCable
|
||||||
|
module Connection
|
||||||
|
# Makes it possible for the RemoteConnection to disconnect a specific connection.
|
||||||
|
module InternalChannel
|
||||||
|
extend ActiveSupport::Concern
|
||||||
|
|
||||||
|
private
|
||||||
|
def internal_channel
|
||||||
|
"action_cable/#{connection_identifier}"
|
||||||
|
end
|
||||||
|
|
||||||
|
def subscribe_to_internal_channel
|
||||||
|
if connection_identifier.present?
|
||||||
|
callback = -> (message) { process_internal_message decode(message) }
|
||||||
|
@_internal_subscriptions ||= []
|
||||||
|
@_internal_subscriptions << [ internal_channel, callback ]
|
||||||
|
|
||||||
|
server.event_loop.post { pubsub.subscribe(internal_channel, callback) }
|
||||||
|
logger.info "Registered connection (#{connection_identifier})"
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
def unsubscribe_from_internal_channel
|
||||||
|
if @_internal_subscriptions.present?
|
||||||
|
@_internal_subscriptions.each { |channel, callback| server.event_loop.post { pubsub.unsubscribe(channel, callback) } }
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
def process_internal_message(message)
|
||||||
|
case message["type"]
|
||||||
|
when "disconnect"
|
||||||
|
logger.info "Removing connection (#{connection_identifier})"
|
||||||
|
websocket.close
|
||||||
|
end
|
||||||
|
rescue Exception => e
|
||||||
|
logger.error "There was an exception - #{e.class}(#{e.message})"
|
||||||
|
logger.error e.backtrace.join("\n")
|
||||||
|
|
||||||
|
close
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
55
debian/gems-compat/actioncable-5.1.6.1/lib/action_cable/connection/message_buffer.rb
vendored
Normal file
55
debian/gems-compat/actioncable-5.1.6.1/lib/action_cable/connection/message_buffer.rb
vendored
Normal file
|
@ -0,0 +1,55 @@
|
||||||
|
module ActionCable
|
||||||
|
module Connection
|
||||||
|
# Allows us to buffer messages received from the WebSocket before the Connection has been fully initialized, and is ready to receive them.
|
||||||
|
class MessageBuffer # :nodoc:
|
||||||
|
def initialize(connection)
|
||||||
|
@connection = connection
|
||||||
|
@buffered_messages = []
|
||||||
|
end
|
||||||
|
|
||||||
|
def append(message)
|
||||||
|
if valid? message
|
||||||
|
if processing?
|
||||||
|
receive message
|
||||||
|
else
|
||||||
|
buffer message
|
||||||
|
end
|
||||||
|
else
|
||||||
|
connection.logger.error "Couldn't handle non-string message: #{message.class}"
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
def processing?
|
||||||
|
@processing
|
||||||
|
end
|
||||||
|
|
||||||
|
def process!
|
||||||
|
@processing = true
|
||||||
|
receive_buffered_messages
|
||||||
|
end
|
||||||
|
|
||||||
|
# TODO Change this to private once we've dropped Ruby 2.2 support.
|
||||||
|
# Workaround for Ruby 2.2 "private attribute?" warning.
|
||||||
|
protected
|
||||||
|
attr_reader :connection
|
||||||
|
attr_reader :buffered_messages
|
||||||
|
|
||||||
|
private
|
||||||
|
def valid?(message)
|
||||||
|
message.is_a?(String)
|
||||||
|
end
|
||||||
|
|
||||||
|
def receive(message)
|
||||||
|
connection.receive message
|
||||||
|
end
|
||||||
|
|
||||||
|
def buffer(message)
|
||||||
|
buffered_messages << message
|
||||||
|
end
|
||||||
|
|
||||||
|
def receive_buffered_messages
|
||||||
|
receive buffered_messages.shift until buffered_messages.empty?
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
113
debian/gems-compat/actioncable-5.1.6.1/lib/action_cable/connection/stream.rb
vendored
Normal file
113
debian/gems-compat/actioncable-5.1.6.1/lib/action_cable/connection/stream.rb
vendored
Normal file
|
@ -0,0 +1,113 @@
|
||||||
|
require "thread"
|
||||||
|
|
||||||
|
module ActionCable
|
||||||
|
module Connection
|
||||||
|
#--
|
||||||
|
# This class is heavily based on faye-websocket-ruby
|
||||||
|
#
|
||||||
|
# Copyright (c) 2010-2015 James Coglan
|
||||||
|
class Stream # :nodoc:
|
||||||
|
def initialize(event_loop, socket)
|
||||||
|
@event_loop = event_loop
|
||||||
|
@socket_object = socket
|
||||||
|
@stream_send = socket.env["stream.send"]
|
||||||
|
|
||||||
|
@rack_hijack_io = nil
|
||||||
|
@write_lock = Mutex.new
|
||||||
|
|
||||||
|
@write_head = nil
|
||||||
|
@write_buffer = Queue.new
|
||||||
|
end
|
||||||
|
|
||||||
|
def each(&callback)
|
||||||
|
@stream_send ||= callback
|
||||||
|
end
|
||||||
|
|
||||||
|
def close
|
||||||
|
shutdown
|
||||||
|
@socket_object.client_gone
|
||||||
|
end
|
||||||
|
|
||||||
|
def shutdown
|
||||||
|
clean_rack_hijack
|
||||||
|
end
|
||||||
|
|
||||||
|
def write(data)
|
||||||
|
if @stream_send
|
||||||
|
return @stream_send.call(data)
|
||||||
|
end
|
||||||
|
|
||||||
|
if @write_lock.try_lock
|
||||||
|
begin
|
||||||
|
if @write_head.nil? && @write_buffer.empty?
|
||||||
|
written = @rack_hijack_io.write_nonblock(data, exception: false)
|
||||||
|
|
||||||
|
case written
|
||||||
|
when :wait_writable
|
||||||
|
# proceed below
|
||||||
|
when data.bytesize
|
||||||
|
return data.bytesize
|
||||||
|
else
|
||||||
|
@write_head = data.byteslice(written, data.bytesize)
|
||||||
|
@event_loop.writes_pending @rack_hijack_io
|
||||||
|
|
||||||
|
return data.bytesize
|
||||||
|
end
|
||||||
|
end
|
||||||
|
ensure
|
||||||
|
@write_lock.unlock
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
@write_buffer << data
|
||||||
|
@event_loop.writes_pending @rack_hijack_io
|
||||||
|
|
||||||
|
data.bytesize
|
||||||
|
rescue EOFError, Errno::ECONNRESET
|
||||||
|
@socket_object.client_gone
|
||||||
|
end
|
||||||
|
|
||||||
|
def flush_write_buffer
|
||||||
|
@write_lock.synchronize do
|
||||||
|
loop do
|
||||||
|
if @write_head.nil?
|
||||||
|
return true if @write_buffer.empty?
|
||||||
|
@write_head = @write_buffer.pop
|
||||||
|
end
|
||||||
|
|
||||||
|
written = @rack_hijack_io.write_nonblock(@write_head, exception: false)
|
||||||
|
case written
|
||||||
|
when :wait_writable
|
||||||
|
return false
|
||||||
|
when @write_head.bytesize
|
||||||
|
@write_head = nil
|
||||||
|
else
|
||||||
|
@write_head = @write_head.byteslice(written, @write_head.bytesize)
|
||||||
|
return false
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
def receive(data)
|
||||||
|
@socket_object.parse(data)
|
||||||
|
end
|
||||||
|
|
||||||
|
def hijack_rack_socket
|
||||||
|
return unless @socket_object.env["rack.hijack"]
|
||||||
|
|
||||||
|
@socket_object.env["rack.hijack"].call
|
||||||
|
@rack_hijack_io = @socket_object.env["rack.hijack_io"]
|
||||||
|
|
||||||
|
@event_loop.attach(@rack_hijack_io, self)
|
||||||
|
end
|
||||||
|
|
||||||
|
private
|
||||||
|
def clean_rack_hijack
|
||||||
|
return unless @rack_hijack_io
|
||||||
|
@event_loop.detach(@rack_hijack_io, self)
|
||||||
|
@rack_hijack_io = nil
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
134
debian/gems-compat/actioncable-5.1.6.1/lib/action_cable/connection/stream_event_loop.rb
vendored
Normal file
134
debian/gems-compat/actioncable-5.1.6.1/lib/action_cable/connection/stream_event_loop.rb
vendored
Normal file
|
@ -0,0 +1,134 @@
|
||||||
|
require "nio"
|
||||||
|
require "thread"
|
||||||
|
|
||||||
|
module ActionCable
|
||||||
|
module Connection
|
||||||
|
class StreamEventLoop
|
||||||
|
def initialize
|
||||||
|
@nio = @executor = @thread = nil
|
||||||
|
@map = {}
|
||||||
|
@stopping = false
|
||||||
|
@todo = Queue.new
|
||||||
|
|
||||||
|
@spawn_mutex = Mutex.new
|
||||||
|
end
|
||||||
|
|
||||||
|
def timer(interval, &block)
|
||||||
|
Concurrent::TimerTask.new(execution_interval: interval, &block).tap(&:execute)
|
||||||
|
end
|
||||||
|
|
||||||
|
def post(task = nil, &block)
|
||||||
|
task ||= block
|
||||||
|
|
||||||
|
spawn
|
||||||
|
@executor << task
|
||||||
|
end
|
||||||
|
|
||||||
|
def attach(io, stream)
|
||||||
|
@todo << lambda do
|
||||||
|
@map[io] = @nio.register(io, :r)
|
||||||
|
@map[io].value = stream
|
||||||
|
end
|
||||||
|
wakeup
|
||||||
|
end
|
||||||
|
|
||||||
|
def detach(io, stream)
|
||||||
|
@todo << lambda do
|
||||||
|
@nio.deregister io
|
||||||
|
@map.delete io
|
||||||
|
io.close
|
||||||
|
end
|
||||||
|
wakeup
|
||||||
|
end
|
||||||
|
|
||||||
|
def writes_pending(io)
|
||||||
|
@todo << lambda do
|
||||||
|
if monitor = @map[io]
|
||||||
|
monitor.interests = :rw
|
||||||
|
end
|
||||||
|
end
|
||||||
|
wakeup
|
||||||
|
end
|
||||||
|
|
||||||
|
def stop
|
||||||
|
@stopping = true
|
||||||
|
wakeup if @nio
|
||||||
|
end
|
||||||
|
|
||||||
|
private
|
||||||
|
def spawn
|
||||||
|
return if @thread && @thread.status
|
||||||
|
|
||||||
|
@spawn_mutex.synchronize do
|
||||||
|
return if @thread && @thread.status
|
||||||
|
|
||||||
|
@nio ||= NIO::Selector.new
|
||||||
|
|
||||||
|
@executor ||= Concurrent::ThreadPoolExecutor.new(
|
||||||
|
min_threads: 1,
|
||||||
|
max_threads: 10,
|
||||||
|
max_queue: 0,
|
||||||
|
)
|
||||||
|
|
||||||
|
@thread = Thread.new { run }
|
||||||
|
|
||||||
|
return true
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
def wakeup
|
||||||
|
spawn || @nio.wakeup
|
||||||
|
end
|
||||||
|
|
||||||
|
def run
|
||||||
|
loop do
|
||||||
|
if @stopping
|
||||||
|
@nio.close
|
||||||
|
break
|
||||||
|
end
|
||||||
|
|
||||||
|
until @todo.empty?
|
||||||
|
@todo.pop(true).call
|
||||||
|
end
|
||||||
|
|
||||||
|
next unless monitors = @nio.select
|
||||||
|
|
||||||
|
monitors.each do |monitor|
|
||||||
|
io = monitor.io
|
||||||
|
stream = monitor.value
|
||||||
|
|
||||||
|
begin
|
||||||
|
if monitor.writable?
|
||||||
|
if stream.flush_write_buffer
|
||||||
|
monitor.interests = :r
|
||||||
|
end
|
||||||
|
next unless monitor.readable?
|
||||||
|
end
|
||||||
|
|
||||||
|
incoming = io.read_nonblock(4096, exception: false)
|
||||||
|
case incoming
|
||||||
|
when :wait_readable
|
||||||
|
next
|
||||||
|
when nil
|
||||||
|
stream.close
|
||||||
|
else
|
||||||
|
stream.receive incoming
|
||||||
|
end
|
||||||
|
rescue
|
||||||
|
# We expect one of EOFError or Errno::ECONNRESET in
|
||||||
|
# normal operation (when the client goes away). But if
|
||||||
|
# anything else goes wrong, this is still the best way
|
||||||
|
# to handle it.
|
||||||
|
begin
|
||||||
|
stream.close
|
||||||
|
rescue
|
||||||
|
@nio.deregister io
|
||||||
|
@map.delete io
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
81
debian/gems-compat/actioncable-5.1.6.1/lib/action_cable/connection/subscriptions.rb
vendored
Normal file
81
debian/gems-compat/actioncable-5.1.6.1/lib/action_cable/connection/subscriptions.rb
vendored
Normal file
|
@ -0,0 +1,81 @@
|
||||||
|
require "active_support/core_ext/hash/indifferent_access"
|
||||||
|
|
||||||
|
module ActionCable
|
||||||
|
module Connection
|
||||||
|
# Collection class for all the channel subscriptions established on a given connection. Responsible for routing incoming commands that arrive on
|
||||||
|
# the connection to the proper channel.
|
||||||
|
class Subscriptions # :nodoc:
|
||||||
|
def initialize(connection)
|
||||||
|
@connection = connection
|
||||||
|
@subscriptions = {}
|
||||||
|
end
|
||||||
|
|
||||||
|
def execute_command(data)
|
||||||
|
case data["command"]
|
||||||
|
when "subscribe" then add data
|
||||||
|
when "unsubscribe" then remove data
|
||||||
|
when "message" then perform_action data
|
||||||
|
else
|
||||||
|
logger.error "Received unrecognized command in #{data.inspect}"
|
||||||
|
end
|
||||||
|
rescue Exception => e
|
||||||
|
logger.error "Could not execute command from (#{data.inspect}) [#{e.class} - #{e.message}]: #{e.backtrace.first(5).join(" | ")}"
|
||||||
|
end
|
||||||
|
|
||||||
|
def add(data)
|
||||||
|
id_key = data["identifier"]
|
||||||
|
id_options = ActiveSupport::JSON.decode(id_key).with_indifferent_access
|
||||||
|
|
||||||
|
return if subscriptions.key?(id_key)
|
||||||
|
|
||||||
|
subscription_klass = id_options[:channel].safe_constantize
|
||||||
|
|
||||||
|
if subscription_klass && ActionCable::Channel::Base >= subscription_klass
|
||||||
|
subscription = subscription_klass.new(connection, id_key, id_options)
|
||||||
|
subscriptions[id_key] = subscription
|
||||||
|
subscription.subscribe_to_channel
|
||||||
|
else
|
||||||
|
logger.error "Subscription class not found: #{id_options[:channel].inspect}"
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
def remove(data)
|
||||||
|
logger.info "Unsubscribing from channel: #{data['identifier']}"
|
||||||
|
remove_subscription subscriptions[data["identifier"]]
|
||||||
|
end
|
||||||
|
|
||||||
|
def remove_subscription(subscription)
|
||||||
|
subscription.unsubscribe_from_channel
|
||||||
|
subscriptions.delete(subscription.identifier)
|
||||||
|
end
|
||||||
|
|
||||||
|
def perform_action(data)
|
||||||
|
find(data).perform_action ActiveSupport::JSON.decode(data["data"])
|
||||||
|
end
|
||||||
|
|
||||||
|
def identifiers
|
||||||
|
subscriptions.keys
|
||||||
|
end
|
||||||
|
|
||||||
|
def unsubscribe_from_all
|
||||||
|
subscriptions.each { |id, channel| remove_subscription(channel) }
|
||||||
|
end
|
||||||
|
|
||||||
|
# TODO Change this to private once we've dropped Ruby 2.2 support.
|
||||||
|
# Workaround for Ruby 2.2 "private attribute?" warning.
|
||||||
|
protected
|
||||||
|
attr_reader :connection, :subscriptions
|
||||||
|
|
||||||
|
private
|
||||||
|
delegate :logger, to: :connection
|
||||||
|
|
||||||
|
def find(data)
|
||||||
|
if subscription = subscriptions[data["identifier"]]
|
||||||
|
subscription
|
||||||
|
else
|
||||||
|
raise "Unable to find subscription with identifier: #{data['identifier']}"
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
40
debian/gems-compat/actioncable-5.1.6.1/lib/action_cable/connection/tagged_logger_proxy.rb
vendored
Normal file
40
debian/gems-compat/actioncable-5.1.6.1/lib/action_cable/connection/tagged_logger_proxy.rb
vendored
Normal file
|
@ -0,0 +1,40 @@
|
||||||
|
module ActionCable
|
||||||
|
module Connection
|
||||||
|
# Allows the use of per-connection tags against the server logger. This wouldn't work using the traditional
|
||||||
|
# <tt>ActiveSupport::TaggedLogging</tt> enhanced Rails.logger, as that logger will reset the tags between requests.
|
||||||
|
# The connection is long-lived, so it needs its own set of tags for its independent duration.
|
||||||
|
class TaggedLoggerProxy
|
||||||
|
attr_reader :tags
|
||||||
|
|
||||||
|
def initialize(logger, tags:)
|
||||||
|
@logger = logger
|
||||||
|
@tags = tags.flatten
|
||||||
|
end
|
||||||
|
|
||||||
|
def add_tags(*tags)
|
||||||
|
@tags += tags.flatten
|
||||||
|
@tags = @tags.uniq
|
||||||
|
end
|
||||||
|
|
||||||
|
def tag(logger)
|
||||||
|
if logger.respond_to?(:tagged)
|
||||||
|
current_tags = tags - logger.formatter.current_tags
|
||||||
|
logger.tagged(*current_tags) { yield }
|
||||||
|
else
|
||||||
|
yield
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
%i( debug info warn error fatal unknown ).each do |severity|
|
||||||
|
define_method(severity) do |message|
|
||||||
|
log severity, message
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
private
|
||||||
|
def log(type, message) # :doc:
|
||||||
|
tag(@logger) { @logger.send type, message }
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
41
debian/gems-compat/actioncable-5.1.6.1/lib/action_cable/connection/web_socket.rb
vendored
Normal file
41
debian/gems-compat/actioncable-5.1.6.1/lib/action_cable/connection/web_socket.rb
vendored
Normal file
|
@ -0,0 +1,41 @@
|
||||||
|
require "websocket/driver"
|
||||||
|
|
||||||
|
module ActionCable
|
||||||
|
module Connection
|
||||||
|
# Wrap the real socket to minimize the externally-presented API
|
||||||
|
class WebSocket
|
||||||
|
def initialize(env, event_target, event_loop, protocols: ActionCable::INTERNAL[:protocols])
|
||||||
|
@websocket = ::WebSocket::Driver.websocket?(env) ? ClientSocket.new(env, event_target, event_loop, protocols) : nil
|
||||||
|
end
|
||||||
|
|
||||||
|
def possible?
|
||||||
|
websocket
|
||||||
|
end
|
||||||
|
|
||||||
|
def alive?
|
||||||
|
websocket && websocket.alive?
|
||||||
|
end
|
||||||
|
|
||||||
|
def transmit(data)
|
||||||
|
websocket.transmit data
|
||||||
|
end
|
||||||
|
|
||||||
|
def close
|
||||||
|
websocket.close
|
||||||
|
end
|
||||||
|
|
||||||
|
def protocol
|
||||||
|
websocket.protocol
|
||||||
|
end
|
||||||
|
|
||||||
|
def rack_response
|
||||||
|
websocket.rack_response
|
||||||
|
end
|
||||||
|
|
||||||
|
# TODO Change this to private once we've dropped Ruby 2.2 support.
|
||||||
|
# Workaround for Ruby 2.2 "private attribute?" warning.
|
||||||
|
protected
|
||||||
|
attr_reader :websocket
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
77
debian/gems-compat/actioncable-5.1.6.1/lib/action_cable/engine.rb
vendored
Normal file
77
debian/gems-compat/actioncable-5.1.6.1/lib/action_cable/engine.rb
vendored
Normal file
|
@ -0,0 +1,77 @@
|
||||||
|
require "rails"
|
||||||
|
require "action_cable"
|
||||||
|
require "action_cable/helpers/action_cable_helper"
|
||||||
|
require "active_support/core_ext/hash/indifferent_access"
|
||||||
|
|
||||||
|
module ActionCable
|
||||||
|
class Engine < Rails::Engine # :nodoc:
|
||||||
|
config.action_cable = ActiveSupport::OrderedOptions.new
|
||||||
|
config.action_cable.mount_path = ActionCable::INTERNAL[:default_mount_path]
|
||||||
|
|
||||||
|
config.eager_load_namespaces << ActionCable
|
||||||
|
|
||||||
|
initializer "action_cable.helpers" do
|
||||||
|
ActiveSupport.on_load(:action_view) do
|
||||||
|
include ActionCable::Helpers::ActionCableHelper
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
initializer "action_cable.logger" do
|
||||||
|
ActiveSupport.on_load(:action_cable) { self.logger ||= ::Rails.logger }
|
||||||
|
end
|
||||||
|
|
||||||
|
initializer "action_cable.set_configs" do |app|
|
||||||
|
options = app.config.action_cable
|
||||||
|
options.allowed_request_origins ||= /https?:\/\/localhost:\d+/ if ::Rails.env.development?
|
||||||
|
|
||||||
|
app.paths.add "config/cable", with: "config/cable.yml"
|
||||||
|
|
||||||
|
ActiveSupport.on_load(:action_cable) do
|
||||||
|
if (config_path = Pathname.new(app.config.paths["config/cable"].first)).exist?
|
||||||
|
self.cable = Rails.application.config_for(config_path).with_indifferent_access
|
||||||
|
end
|
||||||
|
|
||||||
|
previous_connection_class = connection_class
|
||||||
|
self.connection_class = -> { "ApplicationCable::Connection".safe_constantize || previous_connection_class.call }
|
||||||
|
|
||||||
|
options.each { |k, v| send("#{k}=", v) }
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
initializer "action_cable.routes" do
|
||||||
|
config.after_initialize do |app|
|
||||||
|
config = app.config
|
||||||
|
unless config.action_cable.mount_path.nil?
|
||||||
|
app.routes.prepend do
|
||||||
|
mount ActionCable.server => config.action_cable.mount_path, internal: true
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
initializer "action_cable.set_work_hooks" do |app|
|
||||||
|
ActiveSupport.on_load(:action_cable) do
|
||||||
|
ActionCable::Server::Worker.set_callback :work, :around, prepend: true do |_, inner|
|
||||||
|
app.executor.wrap do
|
||||||
|
# If we took a while to get the lock, we may have been halted
|
||||||
|
# in the meantime. As we haven't started doing any real work
|
||||||
|
# yet, we should pretend that we never made it off the queue.
|
||||||
|
unless stopping?
|
||||||
|
inner.call
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
wrap = lambda do |_, inner|
|
||||||
|
app.executor.wrap(&inner)
|
||||||
|
end
|
||||||
|
ActionCable::Channel::Base.set_callback :subscribe, :around, prepend: true, &wrap
|
||||||
|
ActionCable::Channel::Base.set_callback :unsubscribe, :around, prepend: true, &wrap
|
||||||
|
|
||||||
|
app.reloader.before_class_unload do
|
||||||
|
ActionCable.server.restart
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
15
debian/gems-compat/actioncable-5.1.6.1/lib/action_cable/gem_version.rb
vendored
Normal file
15
debian/gems-compat/actioncable-5.1.6.1/lib/action_cable/gem_version.rb
vendored
Normal file
|
@ -0,0 +1,15 @@
|
||||||
|
module ActionCable
|
||||||
|
# Returns the version of the currently loaded Action Cable as a <tt>Gem::Version</tt>.
|
||||||
|
def self.gem_version
|
||||||
|
Gem::Version.new VERSION::STRING
|
||||||
|
end
|
||||||
|
|
||||||
|
module VERSION
|
||||||
|
MAJOR = 5
|
||||||
|
MINOR = 1
|
||||||
|
TINY = 6
|
||||||
|
PRE = "1"
|
||||||
|
|
||||||
|
STRING = [MAJOR, MINOR, TINY, PRE].compact.join(".")
|
||||||
|
end
|
||||||
|
end
|
40
debian/gems-compat/actioncable-5.1.6.1/lib/action_cable/helpers/action_cable_helper.rb
vendored
Normal file
40
debian/gems-compat/actioncable-5.1.6.1/lib/action_cable/helpers/action_cable_helper.rb
vendored
Normal file
|
@ -0,0 +1,40 @@
|
||||||
|
module ActionCable
|
||||||
|
module Helpers
|
||||||
|
module ActionCableHelper
|
||||||
|
# Returns an "action-cable-url" meta tag with the value of the URL specified in your
|
||||||
|
# configuration. Ensure this is above your JavaScript tag:
|
||||||
|
#
|
||||||
|
# <head>
|
||||||
|
# <%= action_cable_meta_tag %>
|
||||||
|
# <%= javascript_include_tag 'application', 'data-turbolinks-track' => 'reload' %>
|
||||||
|
# </head>
|
||||||
|
#
|
||||||
|
# This is then used by Action Cable to determine the URL of your WebSocket server.
|
||||||
|
# Your CoffeeScript can then connect to the server without needing to specify the
|
||||||
|
# URL directly:
|
||||||
|
#
|
||||||
|
# #= require cable
|
||||||
|
# @App = {}
|
||||||
|
# App.cable = Cable.createConsumer()
|
||||||
|
#
|
||||||
|
# Make sure to specify the correct server location in each of your environment
|
||||||
|
# config files:
|
||||||
|
#
|
||||||
|
# config.action_cable.mount_path = "/cable123"
|
||||||
|
# <%= action_cable_meta_tag %> would render:
|
||||||
|
# => <meta name="action-cable-url" content="/cable123" />
|
||||||
|
#
|
||||||
|
# config.action_cable.url = "ws://actioncable.com"
|
||||||
|
# <%= action_cable_meta_tag %> would render:
|
||||||
|
# => <meta name="action-cable-url" content="ws://actioncable.com" />
|
||||||
|
#
|
||||||
|
def action_cable_meta_tag
|
||||||
|
tag "meta", name: "action-cable-url", content: (
|
||||||
|
ActionCable.server.config.url ||
|
||||||
|
ActionCable.server.config.mount_path ||
|
||||||
|
raise("No Action Cable URL configured -- please configure this at config.action_cable.url")
|
||||||
|
)
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
66
debian/gems-compat/actioncable-5.1.6.1/lib/action_cable/remote_connections.rb
vendored
Normal file
66
debian/gems-compat/actioncable-5.1.6.1/lib/action_cable/remote_connections.rb
vendored
Normal file
|
@ -0,0 +1,66 @@
|
||||||
|
module ActionCable
|
||||||
|
# If you need to disconnect a given connection, you can go through the
|
||||||
|
# RemoteConnections. You can find the connections you're looking for by
|
||||||
|
# searching for the identifier declared on the connection. For example:
|
||||||
|
#
|
||||||
|
# module ApplicationCable
|
||||||
|
# class Connection < ActionCable::Connection::Base
|
||||||
|
# identified_by :current_user
|
||||||
|
# ....
|
||||||
|
# end
|
||||||
|
# end
|
||||||
|
#
|
||||||
|
# ActionCable.server.remote_connections.where(current_user: User.find(1)).disconnect
|
||||||
|
#
|
||||||
|
# This will disconnect all the connections established for
|
||||||
|
# <tt>User.find(1)</tt>, across all servers running on all machines, because
|
||||||
|
# it uses the internal channel that all of these servers are subscribed to.
|
||||||
|
class RemoteConnections
|
||||||
|
attr_reader :server
|
||||||
|
|
||||||
|
def initialize(server)
|
||||||
|
@server = server
|
||||||
|
end
|
||||||
|
|
||||||
|
def where(identifier)
|
||||||
|
RemoteConnection.new(server, identifier)
|
||||||
|
end
|
||||||
|
|
||||||
|
private
|
||||||
|
# Represents a single remote connection found via <tt>ActionCable.server.remote_connections.where(*)</tt>.
|
||||||
|
# Exists solely for the purpose of calling #disconnect on that connection.
|
||||||
|
class RemoteConnection
|
||||||
|
class InvalidIdentifiersError < StandardError; end
|
||||||
|
|
||||||
|
include Connection::Identification, Connection::InternalChannel
|
||||||
|
|
||||||
|
def initialize(server, ids)
|
||||||
|
@server = server
|
||||||
|
set_identifier_instance_vars(ids)
|
||||||
|
end
|
||||||
|
|
||||||
|
# Uses the internal channel to disconnect the connection.
|
||||||
|
def disconnect
|
||||||
|
server.broadcast internal_channel, type: "disconnect"
|
||||||
|
end
|
||||||
|
|
||||||
|
# Returns all the identifiers that were applied to this connection.
|
||||||
|
def identifiers
|
||||||
|
server.connection_identifiers
|
||||||
|
end
|
||||||
|
|
||||||
|
private
|
||||||
|
attr_reader :server
|
||||||
|
|
||||||
|
def set_identifier_instance_vars(ids)
|
||||||
|
raise InvalidIdentifiersError unless valid_identifiers?(ids)
|
||||||
|
ids.each { |k, v| instance_variable_set("@#{k}", v) }
|
||||||
|
end
|
||||||
|
|
||||||
|
def valid_identifiers?(ids)
|
||||||
|
keys = ids.keys
|
||||||
|
identifiers.all? { |id| keys.include?(id) }
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
15
debian/gems-compat/actioncable-5.1.6.1/lib/action_cable/server.rb
vendored
Normal file
15
debian/gems-compat/actioncable-5.1.6.1/lib/action_cable/server.rb
vendored
Normal file
|
@ -0,0 +1,15 @@
|
||||||
|
module ActionCable
|
||||||
|
module Server
|
||||||
|
extend ActiveSupport::Autoload
|
||||||
|
|
||||||
|
eager_autoload do
|
||||||
|
autoload :Base
|
||||||
|
autoload :Broadcasting
|
||||||
|
autoload :Connections
|
||||||
|
autoload :Configuration
|
||||||
|
|
||||||
|
autoload :Worker
|
||||||
|
autoload :ActiveRecordConnectionManagement, "action_cable/server/worker/active_record_connection_management"
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
87
debian/gems-compat/actioncable-5.1.6.1/lib/action_cable/server/base.rb
vendored
Normal file
87
debian/gems-compat/actioncable-5.1.6.1/lib/action_cable/server/base.rb
vendored
Normal file
|
@ -0,0 +1,87 @@
|
||||||
|
require "monitor"
|
||||||
|
|
||||||
|
module ActionCable
|
||||||
|
module Server
|
||||||
|
# A singleton ActionCable::Server instance is available via ActionCable.server. It's used by the Rack process that starts the Action Cable server, but
|
||||||
|
# is also used by the user to reach the RemoteConnections object, which is used for finding and disconnecting connections across all servers.
|
||||||
|
#
|
||||||
|
# Also, this is the server instance used for broadcasting. See Broadcasting for more information.
|
||||||
|
class Base
|
||||||
|
include ActionCable::Server::Broadcasting
|
||||||
|
include ActionCable::Server::Connections
|
||||||
|
|
||||||
|
cattr_accessor(:config, instance_accessor: true) { ActionCable::Server::Configuration.new }
|
||||||
|
|
||||||
|
def self.logger; config.logger; end
|
||||||
|
delegate :logger, to: :config
|
||||||
|
|
||||||
|
attr_reader :mutex
|
||||||
|
|
||||||
|
def initialize
|
||||||
|
@mutex = Monitor.new
|
||||||
|
@remote_connections = @event_loop = @worker_pool = @pubsub = nil
|
||||||
|
end
|
||||||
|
|
||||||
|
# Called by Rack to setup the server.
|
||||||
|
def call(env)
|
||||||
|
setup_heartbeat_timer
|
||||||
|
config.connection_class.call.new(self, env).process
|
||||||
|
end
|
||||||
|
|
||||||
|
# Disconnect all the connections identified by `identifiers` on this server or any others via RemoteConnections.
|
||||||
|
def disconnect(identifiers)
|
||||||
|
remote_connections.where(identifiers).disconnect
|
||||||
|
end
|
||||||
|
|
||||||
|
def restart
|
||||||
|
connections.each(&:close)
|
||||||
|
|
||||||
|
@mutex.synchronize do
|
||||||
|
# Shutdown the worker pool
|
||||||
|
@worker_pool.halt if @worker_pool
|
||||||
|
@worker_pool = nil
|
||||||
|
|
||||||
|
# Shutdown the pub/sub adapter
|
||||||
|
@pubsub.shutdown if @pubsub
|
||||||
|
@pubsub = nil
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
# Gateway to RemoteConnections. See that class for details.
|
||||||
|
def remote_connections
|
||||||
|
@remote_connections || @mutex.synchronize { @remote_connections ||= RemoteConnections.new(self) }
|
||||||
|
end
|
||||||
|
|
||||||
|
def event_loop
|
||||||
|
@event_loop || @mutex.synchronize { @event_loop ||= ActionCable::Connection::StreamEventLoop.new }
|
||||||
|
end
|
||||||
|
|
||||||
|
# The worker pool is where we run connection callbacks and channel actions. We do as little as possible on the server's main thread.
|
||||||
|
# The worker pool is an executor service that's backed by a pool of threads working from a task queue. The thread pool size maxes out
|
||||||
|
# at 4 worker threads by default. Tune the size yourself with <tt>config.action_cable.worker_pool_size</tt>.
|
||||||
|
#
|
||||||
|
# Using Active Record, Redis, etc within your channel actions means you'll get a separate connection from each thread in the worker pool.
|
||||||
|
# Plan your deployment accordingly: 5 servers each running 5 Puma workers each running an 8-thread worker pool means at least 200 database
|
||||||
|
# connections.
|
||||||
|
#
|
||||||
|
# Also, ensure that your database connection pool size is as least as large as your worker pool size. Otherwise, workers may oversubscribe
|
||||||
|
# the database connection pool and block while they wait for other workers to release their connections. Use a smaller worker pool or a larger
|
||||||
|
# database connection pool instead.
|
||||||
|
def worker_pool
|
||||||
|
@worker_pool || @mutex.synchronize { @worker_pool ||= ActionCable::Server::Worker.new(max_size: config.worker_pool_size) }
|
||||||
|
end
|
||||||
|
|
||||||
|
# Adapter used for all streams/broadcasting.
|
||||||
|
def pubsub
|
||||||
|
@pubsub || @mutex.synchronize { @pubsub ||= config.pubsub_adapter.new(self) }
|
||||||
|
end
|
||||||
|
|
||||||
|
# All of the identifiers applied to the connection class associated with this server.
|
||||||
|
def connection_identifiers
|
||||||
|
config.connection_class.call.identifiers
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
ActiveSupport.run_load_hooks(:action_cable, Base.config)
|
||||||
|
end
|
||||||
|
end
|
52
debian/gems-compat/actioncable-5.1.6.1/lib/action_cable/server/broadcasting.rb
vendored
Normal file
52
debian/gems-compat/actioncable-5.1.6.1/lib/action_cable/server/broadcasting.rb
vendored
Normal file
|
@ -0,0 +1,52 @@
|
||||||
|
module ActionCable
|
||||||
|
module Server
|
||||||
|
# Broadcasting is how other parts of your application can send messages to a channel's subscribers. As explained in Channel, most of the time, these
|
||||||
|
# broadcastings are streamed directly to the clients subscribed to the named broadcasting. Let's explain with a full-stack example:
|
||||||
|
#
|
||||||
|
# class WebNotificationsChannel < ApplicationCable::Channel
|
||||||
|
# def subscribed
|
||||||
|
# stream_from "web_notifications_#{current_user.id}"
|
||||||
|
# end
|
||||||
|
# end
|
||||||
|
#
|
||||||
|
# # Somewhere in your app this is called, perhaps from a NewCommentJob:
|
||||||
|
# ActionCable.server.broadcast \
|
||||||
|
# "web_notifications_1", { title: "New things!", body: "All that's fit for print" }
|
||||||
|
#
|
||||||
|
# # Client-side CoffeeScript, which assumes you've already requested the right to send web notifications:
|
||||||
|
# App.cable.subscriptions.create "WebNotificationsChannel",
|
||||||
|
# received: (data) ->
|
||||||
|
# new Notification data['title'], body: data['body']
|
||||||
|
module Broadcasting
|
||||||
|
# Broadcast a hash directly to a named <tt>broadcasting</tt>. This will later be JSON encoded.
|
||||||
|
def broadcast(broadcasting, message, coder: ActiveSupport::JSON)
|
||||||
|
broadcaster_for(broadcasting, coder: coder).broadcast(message)
|
||||||
|
end
|
||||||
|
|
||||||
|
# Returns a broadcaster for a named <tt>broadcasting</tt> that can be reused. Useful when you have an object that
|
||||||
|
# may need multiple spots to transmit to a specific broadcasting over and over.
|
||||||
|
def broadcaster_for(broadcasting, coder: ActiveSupport::JSON)
|
||||||
|
Broadcaster.new(self, String(broadcasting), coder: coder)
|
||||||
|
end
|
||||||
|
|
||||||
|
private
|
||||||
|
class Broadcaster
|
||||||
|
attr_reader :server, :broadcasting, :coder
|
||||||
|
|
||||||
|
def initialize(server, broadcasting, coder:)
|
||||||
|
@server, @broadcasting, @coder = server, broadcasting, coder
|
||||||
|
end
|
||||||
|
|
||||||
|
def broadcast(message)
|
||||||
|
server.logger.debug "[ActionCable] Broadcasting to #{broadcasting}: #{message.inspect}"
|
||||||
|
|
||||||
|
payload = { broadcasting: broadcasting, message: message, coder: coder }
|
||||||
|
ActiveSupport::Notifications.instrument("broadcast.action_cable", payload) do
|
||||||
|
encoded = coder ? coder.encode(message) : message
|
||||||
|
server.pubsub.broadcast broadcasting, encoded
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
41
debian/gems-compat/actioncable-5.1.6.1/lib/action_cable/server/configuration.rb
vendored
Normal file
41
debian/gems-compat/actioncable-5.1.6.1/lib/action_cable/server/configuration.rb
vendored
Normal file
|
@ -0,0 +1,41 @@
|
||||||
|
module ActionCable
|
||||||
|
module Server
|
||||||
|
# An instance of this configuration object is available via ActionCable.server.config, which allows you to tweak Action Cable configuration
|
||||||
|
# in a Rails config initializer.
|
||||||
|
class Configuration
|
||||||
|
attr_accessor :logger, :log_tags
|
||||||
|
attr_accessor :connection_class, :worker_pool_size
|
||||||
|
attr_accessor :disable_request_forgery_protection, :allowed_request_origins, :allow_same_origin_as_host
|
||||||
|
attr_accessor :cable, :url, :mount_path
|
||||||
|
|
||||||
|
def initialize
|
||||||
|
@log_tags = []
|
||||||
|
|
||||||
|
@connection_class = -> { ActionCable::Connection::Base }
|
||||||
|
@worker_pool_size = 4
|
||||||
|
|
||||||
|
@disable_request_forgery_protection = false
|
||||||
|
@allow_same_origin_as_host = true
|
||||||
|
end
|
||||||
|
|
||||||
|
# Returns constant of subscription adapter specified in config/cable.yml.
|
||||||
|
# If the adapter cannot be found, this will default to the Redis adapter.
|
||||||
|
# Also makes sure proper dependencies are required.
|
||||||
|
def pubsub_adapter
|
||||||
|
adapter = (cable.fetch("adapter") { "redis" })
|
||||||
|
path_to_adapter = "action_cable/subscription_adapter/#{adapter}"
|
||||||
|
begin
|
||||||
|
require path_to_adapter
|
||||||
|
rescue Gem::LoadError => e
|
||||||
|
raise Gem::LoadError, "Specified '#{adapter}' for Action Cable pubsub adapter, but the gem is not loaded. Add `gem '#{e.name}'` to your Gemfile (and ensure its version is at the minimum required by Action Cable)."
|
||||||
|
rescue LoadError => e
|
||||||
|
raise LoadError, "Could not load '#{path_to_adapter}'. Make sure that the adapter in config/cable.yml is valid. If you use an adapter other than 'postgresql' or 'redis' add the necessary adapter gem to the Gemfile.", e.backtrace
|
||||||
|
end
|
||||||
|
|
||||||
|
adapter = adapter.camelize
|
||||||
|
adapter = "PostgreSQL" if adapter == "Postgresql"
|
||||||
|
"ActionCable::SubscriptionAdapter::#{adapter}".constantize
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
34
debian/gems-compat/actioncable-5.1.6.1/lib/action_cable/server/connections.rb
vendored
Normal file
34
debian/gems-compat/actioncable-5.1.6.1/lib/action_cable/server/connections.rb
vendored
Normal file
|
@ -0,0 +1,34 @@
|
||||||
|
module ActionCable
|
||||||
|
module Server
|
||||||
|
# Collection class for all the connections that have been established on this specific server. Remember, usually you'll run many Action Cable servers, so
|
||||||
|
# you can't use this collection as a full list of all of the connections established against your application. Instead, use RemoteConnections for that.
|
||||||
|
module Connections # :nodoc:
|
||||||
|
BEAT_INTERVAL = 3
|
||||||
|
|
||||||
|
def connections
|
||||||
|
@connections ||= []
|
||||||
|
end
|
||||||
|
|
||||||
|
def add_connection(connection)
|
||||||
|
connections << connection
|
||||||
|
end
|
||||||
|
|
||||||
|
def remove_connection(connection)
|
||||||
|
connections.delete connection
|
||||||
|
end
|
||||||
|
|
||||||
|
# WebSocket connection implementations differ on when they'll mark a connection as stale. We basically never want a connection to go stale, as you
|
||||||
|
# then can't rely on being able to communicate with the connection. To solve this, a 3 second heartbeat runs on all connections. If the beat fails, we automatically
|
||||||
|
# disconnect.
|
||||||
|
def setup_heartbeat_timer
|
||||||
|
@heartbeat_timer ||= event_loop.timer(BEAT_INTERVAL) do
|
||||||
|
event_loop.post { connections.map(&:beat) }
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
def open_connections_statistics
|
||||||
|
connections.map(&:statistics)
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
75
debian/gems-compat/actioncable-5.1.6.1/lib/action_cable/server/worker.rb
vendored
Normal file
75
debian/gems-compat/actioncable-5.1.6.1/lib/action_cable/server/worker.rb
vendored
Normal file
|
@ -0,0 +1,75 @@
|
||||||
|
require "active_support/callbacks"
|
||||||
|
require "active_support/core_ext/module/attribute_accessors_per_thread"
|
||||||
|
require "concurrent"
|
||||||
|
|
||||||
|
module ActionCable
|
||||||
|
module Server
|
||||||
|
# Worker used by Server.send_async to do connection work in threads.
|
||||||
|
class Worker # :nodoc:
|
||||||
|
include ActiveSupport::Callbacks
|
||||||
|
|
||||||
|
thread_mattr_accessor :connection
|
||||||
|
define_callbacks :work
|
||||||
|
include ActiveRecordConnectionManagement
|
||||||
|
|
||||||
|
attr_reader :executor
|
||||||
|
|
||||||
|
def initialize(max_size: 5)
|
||||||
|
@executor = Concurrent::ThreadPoolExecutor.new(
|
||||||
|
min_threads: 1,
|
||||||
|
max_threads: max_size,
|
||||||
|
max_queue: 0,
|
||||||
|
)
|
||||||
|
end
|
||||||
|
|
||||||
|
# Stop processing work: any work that has not already started
|
||||||
|
# running will be discarded from the queue
|
||||||
|
def halt
|
||||||
|
@executor.shutdown
|
||||||
|
end
|
||||||
|
|
||||||
|
def stopping?
|
||||||
|
@executor.shuttingdown?
|
||||||
|
end
|
||||||
|
|
||||||
|
def work(connection)
|
||||||
|
self.connection = connection
|
||||||
|
|
||||||
|
run_callbacks :work do
|
||||||
|
yield
|
||||||
|
end
|
||||||
|
ensure
|
||||||
|
self.connection = nil
|
||||||
|
end
|
||||||
|
|
||||||
|
def async_exec(receiver, *args, connection:, &block)
|
||||||
|
async_invoke receiver, :instance_exec, *args, connection: connection, &block
|
||||||
|
end
|
||||||
|
|
||||||
|
def async_invoke(receiver, method, *args, connection: receiver, &block)
|
||||||
|
@executor.post do
|
||||||
|
invoke(receiver, method, *args, connection: connection, &block)
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
def invoke(receiver, method, *args, connection:, &block)
|
||||||
|
work(connection) do
|
||||||
|
begin
|
||||||
|
receiver.send method, *args, &block
|
||||||
|
rescue Exception => e
|
||||||
|
logger.error "There was an exception - #{e.class}(#{e.message})"
|
||||||
|
logger.error e.backtrace.join("\n")
|
||||||
|
|
||||||
|
receiver.handle_exception if receiver.respond_to?(:handle_exception)
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
private
|
||||||
|
|
||||||
|
def logger
|
||||||
|
ActionCable.server.logger
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
|
@ -0,0 +1,19 @@
|
||||||
|
module ActionCable
|
||||||
|
module Server
|
||||||
|
class Worker
|
||||||
|
module ActiveRecordConnectionManagement
|
||||||
|
extend ActiveSupport::Concern
|
||||||
|
|
||||||
|
included do
|
||||||
|
if defined?(ActiveRecord::Base)
|
||||||
|
set_callback :work, :around, :with_database_connections
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
def with_database_connections
|
||||||
|
connection.logger.tag(ActiveRecord::Base.logger) { yield }
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
9
debian/gems-compat/actioncable-5.1.6.1/lib/action_cable/subscription_adapter.rb
vendored
Normal file
9
debian/gems-compat/actioncable-5.1.6.1/lib/action_cable/subscription_adapter.rb
vendored
Normal file
|
@ -0,0 +1,9 @@
|
||||||
|
module ActionCable
|
||||||
|
module SubscriptionAdapter
|
||||||
|
extend ActiveSupport::Autoload
|
||||||
|
|
||||||
|
autoload :Base
|
||||||
|
autoload :SubscriberMap
|
||||||
|
autoload :ChannelPrefix
|
||||||
|
end
|
||||||
|
end
|
27
debian/gems-compat/actioncable-5.1.6.1/lib/action_cable/subscription_adapter/async.rb
vendored
Normal file
27
debian/gems-compat/actioncable-5.1.6.1/lib/action_cable/subscription_adapter/async.rb
vendored
Normal file
|
@ -0,0 +1,27 @@
|
||||||
|
require "action_cable/subscription_adapter/inline"
|
||||||
|
|
||||||
|
module ActionCable
|
||||||
|
module SubscriptionAdapter
|
||||||
|
class Async < Inline # :nodoc:
|
||||||
|
private
|
||||||
|
def new_subscriber_map
|
||||||
|
AsyncSubscriberMap.new(server.event_loop)
|
||||||
|
end
|
||||||
|
|
||||||
|
class AsyncSubscriberMap < SubscriberMap
|
||||||
|
def initialize(event_loop)
|
||||||
|
@event_loop = event_loop
|
||||||
|
super()
|
||||||
|
end
|
||||||
|
|
||||||
|
def add_subscriber(*)
|
||||||
|
@event_loop.post { super }
|
||||||
|
end
|
||||||
|
|
||||||
|
def invoke_callback(*)
|
||||||
|
@event_loop.post { super }
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
28
debian/gems-compat/actioncable-5.1.6.1/lib/action_cable/subscription_adapter/base.rb
vendored
Normal file
28
debian/gems-compat/actioncable-5.1.6.1/lib/action_cable/subscription_adapter/base.rb
vendored
Normal file
|
@ -0,0 +1,28 @@
|
||||||
|
module ActionCable
|
||||||
|
module SubscriptionAdapter
|
||||||
|
class Base
|
||||||
|
attr_reader :logger, :server
|
||||||
|
|
||||||
|
def initialize(server)
|
||||||
|
@server = server
|
||||||
|
@logger = @server.logger
|
||||||
|
end
|
||||||
|
|
||||||
|
def broadcast(channel, payload)
|
||||||
|
raise NotImplementedError
|
||||||
|
end
|
||||||
|
|
||||||
|
def subscribe(channel, message_callback, success_callback = nil)
|
||||||
|
raise NotImplementedError
|
||||||
|
end
|
||||||
|
|
||||||
|
def unsubscribe(channel, message_callback)
|
||||||
|
raise NotImplementedError
|
||||||
|
end
|
||||||
|
|
||||||
|
def shutdown
|
||||||
|
raise NotImplementedError
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
26
debian/gems-compat/actioncable-5.1.6.1/lib/action_cable/subscription_adapter/channel_prefix.rb
vendored
Normal file
26
debian/gems-compat/actioncable-5.1.6.1/lib/action_cable/subscription_adapter/channel_prefix.rb
vendored
Normal file
|
@ -0,0 +1,26 @@
|
||||||
|
module ActionCable
|
||||||
|
module SubscriptionAdapter
|
||||||
|
module ChannelPrefix # :nodoc:
|
||||||
|
def broadcast(channel, payload)
|
||||||
|
channel = channel_with_prefix(channel)
|
||||||
|
super
|
||||||
|
end
|
||||||
|
|
||||||
|
def subscribe(channel, callback, success_callback = nil)
|
||||||
|
channel = channel_with_prefix(channel)
|
||||||
|
super
|
||||||
|
end
|
||||||
|
|
||||||
|
def unsubscribe(channel, callback)
|
||||||
|
channel = channel_with_prefix(channel)
|
||||||
|
super
|
||||||
|
end
|
||||||
|
|
||||||
|
private
|
||||||
|
# Returns the channel name, including channel_prefix specified in cable.yml
|
||||||
|
def channel_with_prefix(channel)
|
||||||
|
[@server.config.cable[:channel_prefix], channel].compact.join(":")
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
87
debian/gems-compat/actioncable-5.1.6.1/lib/action_cable/subscription_adapter/evented_redis.rb
vendored
Normal file
87
debian/gems-compat/actioncable-5.1.6.1/lib/action_cable/subscription_adapter/evented_redis.rb
vendored
Normal file
|
@ -0,0 +1,87 @@
|
||||||
|
require "thread"
|
||||||
|
|
||||||
|
gem "em-hiredis", "~> 0.3.0"
|
||||||
|
gem "redis", ">= 3", "< 5"
|
||||||
|
require "em-hiredis"
|
||||||
|
require "redis"
|
||||||
|
|
||||||
|
EventMachine.epoll if EventMachine.epoll?
|
||||||
|
EventMachine.kqueue if EventMachine.kqueue?
|
||||||
|
|
||||||
|
module ActionCable
|
||||||
|
module SubscriptionAdapter
|
||||||
|
class EventedRedis < Base # :nodoc:
|
||||||
|
prepend ChannelPrefix
|
||||||
|
|
||||||
|
@@mutex = Mutex.new
|
||||||
|
|
||||||
|
# Overwrite this factory method for EventMachine Redis connections if you want to use a different Redis connection library than EM::Hiredis.
|
||||||
|
# This is needed, for example, when using Makara proxies for distributed Redis.
|
||||||
|
cattr_accessor(:em_redis_connector) { ->(config) { EM::Hiredis.connect(config[:url]) } }
|
||||||
|
|
||||||
|
# Overwrite this factory method for Redis connections if you want to use a different Redis connection library than Redis.
|
||||||
|
# This is needed, for example, when using Makara proxies for distributed Redis.
|
||||||
|
cattr_accessor(:redis_connector) { ->(config) { ::Redis.new(url: config[:url]) } }
|
||||||
|
|
||||||
|
def initialize(*)
|
||||||
|
ActiveSupport::Deprecation.warn(<<-MSG.squish)
|
||||||
|
The "evented_redis" subscription adapter is deprecated and
|
||||||
|
will be removed in Rails 5.2. Please use the "redis" adapter
|
||||||
|
instead.
|
||||||
|
MSG
|
||||||
|
|
||||||
|
super
|
||||||
|
@redis_connection_for_broadcasts = @redis_connection_for_subscriptions = nil
|
||||||
|
end
|
||||||
|
|
||||||
|
def broadcast(channel, payload)
|
||||||
|
redis_connection_for_broadcasts.publish(channel, payload)
|
||||||
|
end
|
||||||
|
|
||||||
|
def subscribe(channel, message_callback, success_callback = nil)
|
||||||
|
redis_connection_for_subscriptions.pubsub.subscribe(channel, &message_callback).tap do |result|
|
||||||
|
result.callback { |reply| success_callback.call } if success_callback
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
def unsubscribe(channel, message_callback)
|
||||||
|
redis_connection_for_subscriptions.pubsub.unsubscribe_proc(channel, message_callback)
|
||||||
|
end
|
||||||
|
|
||||||
|
def shutdown
|
||||||
|
redis_connection_for_subscriptions.pubsub.close_connection
|
||||||
|
@redis_connection_for_subscriptions = nil
|
||||||
|
end
|
||||||
|
|
||||||
|
private
|
||||||
|
def redis_connection_for_subscriptions
|
||||||
|
ensure_reactor_running
|
||||||
|
@redis_connection_for_subscriptions || @server.mutex.synchronize do
|
||||||
|
@redis_connection_for_subscriptions ||= self.class.em_redis_connector.call(@server.config.cable).tap do |redis|
|
||||||
|
redis.on(:reconnect_failed) do
|
||||||
|
@logger.error "[ActionCable] Redis reconnect failed."
|
||||||
|
end
|
||||||
|
|
||||||
|
redis.on(:failed) do
|
||||||
|
@logger.error "[ActionCable] Redis connection has failed."
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
def redis_connection_for_broadcasts
|
||||||
|
@redis_connection_for_broadcasts || @server.mutex.synchronize do
|
||||||
|
@redis_connection_for_broadcasts ||= self.class.redis_connector.call(@server.config.cable)
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
def ensure_reactor_running
|
||||||
|
return if EventMachine.reactor_running? && EventMachine.reactor_thread
|
||||||
|
@@mutex.synchronize do
|
||||||
|
Thread.new { EventMachine.run } unless EventMachine.reactor_running?
|
||||||
|
Thread.pass until EventMachine.reactor_running? && EventMachine.reactor_thread
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
35
debian/gems-compat/actioncable-5.1.6.1/lib/action_cable/subscription_adapter/inline.rb
vendored
Normal file
35
debian/gems-compat/actioncable-5.1.6.1/lib/action_cable/subscription_adapter/inline.rb
vendored
Normal file
|
@ -0,0 +1,35 @@
|
||||||
|
module ActionCable
|
||||||
|
module SubscriptionAdapter
|
||||||
|
class Inline < Base # :nodoc:
|
||||||
|
def initialize(*)
|
||||||
|
super
|
||||||
|
@subscriber_map = nil
|
||||||
|
end
|
||||||
|
|
||||||
|
def broadcast(channel, payload)
|
||||||
|
subscriber_map.broadcast(channel, payload)
|
||||||
|
end
|
||||||
|
|
||||||
|
def subscribe(channel, callback, success_callback = nil)
|
||||||
|
subscriber_map.add_subscriber(channel, callback, success_callback)
|
||||||
|
end
|
||||||
|
|
||||||
|
def unsubscribe(channel, callback)
|
||||||
|
subscriber_map.remove_subscriber(channel, callback)
|
||||||
|
end
|
||||||
|
|
||||||
|
def shutdown
|
||||||
|
# nothing to do
|
||||||
|
end
|
||||||
|
|
||||||
|
private
|
||||||
|
def subscriber_map
|
||||||
|
@subscriber_map || @server.mutex.synchronize { @subscriber_map ||= new_subscriber_map }
|
||||||
|
end
|
||||||
|
|
||||||
|
def new_subscriber_map
|
||||||
|
SubscriberMap.new
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
107
debian/gems-compat/actioncable-5.1.6.1/lib/action_cable/subscription_adapter/postgresql.rb
vendored
Normal file
107
debian/gems-compat/actioncable-5.1.6.1/lib/action_cable/subscription_adapter/postgresql.rb
vendored
Normal file
|
@ -0,0 +1,107 @@
|
||||||
|
gem "pg", ">= 0.18", "< 2.0"
|
||||||
|
require "pg"
|
||||||
|
require "thread"
|
||||||
|
|
||||||
|
module ActionCable
|
||||||
|
module SubscriptionAdapter
|
||||||
|
class PostgreSQL < Base # :nodoc:
|
||||||
|
def initialize(*)
|
||||||
|
super
|
||||||
|
@listener = nil
|
||||||
|
end
|
||||||
|
|
||||||
|
def broadcast(channel, payload)
|
||||||
|
with_connection do |pg_conn|
|
||||||
|
pg_conn.exec("NOTIFY #{pg_conn.escape_identifier(channel)}, '#{pg_conn.escape_string(payload)}'")
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
def subscribe(channel, callback, success_callback = nil)
|
||||||
|
listener.add_subscriber(channel, callback, success_callback)
|
||||||
|
end
|
||||||
|
|
||||||
|
def unsubscribe(channel, callback)
|
||||||
|
listener.remove_subscriber(channel, callback)
|
||||||
|
end
|
||||||
|
|
||||||
|
def shutdown
|
||||||
|
listener.shutdown
|
||||||
|
end
|
||||||
|
|
||||||
|
def with_connection(&block) # :nodoc:
|
||||||
|
ActiveRecord::Base.connection_pool.with_connection do |ar_conn|
|
||||||
|
pg_conn = ar_conn.raw_connection
|
||||||
|
|
||||||
|
unless pg_conn.is_a?(PG::Connection)
|
||||||
|
raise "The Active Record database must be PostgreSQL in order to use the PostgreSQL Action Cable storage adapter"
|
||||||
|
end
|
||||||
|
|
||||||
|
yield pg_conn
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
private
|
||||||
|
def listener
|
||||||
|
@listener || @server.mutex.synchronize { @listener ||= Listener.new(self, @server.event_loop) }
|
||||||
|
end
|
||||||
|
|
||||||
|
class Listener < SubscriberMap
|
||||||
|
def initialize(adapter, event_loop)
|
||||||
|
super()
|
||||||
|
|
||||||
|
@adapter = adapter
|
||||||
|
@event_loop = event_loop
|
||||||
|
@queue = Queue.new
|
||||||
|
|
||||||
|
@thread = Thread.new do
|
||||||
|
Thread.current.abort_on_exception = true
|
||||||
|
listen
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
def listen
|
||||||
|
@adapter.with_connection do |pg_conn|
|
||||||
|
catch :shutdown do
|
||||||
|
loop do
|
||||||
|
until @queue.empty?
|
||||||
|
action, channel, callback = @queue.pop(true)
|
||||||
|
|
||||||
|
case action
|
||||||
|
when :listen
|
||||||
|
pg_conn.exec("LISTEN #{pg_conn.escape_identifier channel}")
|
||||||
|
@event_loop.post(&callback) if callback
|
||||||
|
when :unlisten
|
||||||
|
pg_conn.exec("UNLISTEN #{pg_conn.escape_identifier channel}")
|
||||||
|
when :shutdown
|
||||||
|
throw :shutdown
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
pg_conn.wait_for_notify(1) do |chan, pid, message|
|
||||||
|
broadcast(chan, message)
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
def shutdown
|
||||||
|
@queue.push([:shutdown])
|
||||||
|
Thread.pass while @thread.alive?
|
||||||
|
end
|
||||||
|
|
||||||
|
def add_channel(channel, on_success)
|
||||||
|
@queue.push([:listen, channel, on_success])
|
||||||
|
end
|
||||||
|
|
||||||
|
def remove_channel(channel)
|
||||||
|
@queue.push([:unlisten, channel])
|
||||||
|
end
|
||||||
|
|
||||||
|
def invoke_callback(*)
|
||||||
|
@event_loop.post { super }
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
174
debian/gems-compat/actioncable-5.1.6.1/lib/action_cable/subscription_adapter/redis.rb
vendored
Normal file
174
debian/gems-compat/actioncable-5.1.6.1/lib/action_cable/subscription_adapter/redis.rb
vendored
Normal file
|
@ -0,0 +1,174 @@
|
||||||
|
require "thread"
|
||||||
|
|
||||||
|
gem "redis", ">= 3", "< 5"
|
||||||
|
require "redis"
|
||||||
|
|
||||||
|
module ActionCable
|
||||||
|
module SubscriptionAdapter
|
||||||
|
class Redis < Base # :nodoc:
|
||||||
|
prepend ChannelPrefix
|
||||||
|
|
||||||
|
# Overwrite this factory method for redis connections if you want to use a different Redis library than Redis.
|
||||||
|
# This is needed, for example, when using Makara proxies for distributed Redis.
|
||||||
|
cattr_accessor(:redis_connector) { ->(config) { ::Redis.new(url: config[:url]) } }
|
||||||
|
|
||||||
|
def initialize(*)
|
||||||
|
super
|
||||||
|
@listener = nil
|
||||||
|
@redis_connection_for_broadcasts = nil
|
||||||
|
end
|
||||||
|
|
||||||
|
def broadcast(channel, payload)
|
||||||
|
redis_connection_for_broadcasts.publish(channel, payload)
|
||||||
|
end
|
||||||
|
|
||||||
|
def subscribe(channel, callback, success_callback = nil)
|
||||||
|
listener.add_subscriber(channel, callback, success_callback)
|
||||||
|
end
|
||||||
|
|
||||||
|
def unsubscribe(channel, callback)
|
||||||
|
listener.remove_subscriber(channel, callback)
|
||||||
|
end
|
||||||
|
|
||||||
|
def shutdown
|
||||||
|
@listener.shutdown if @listener
|
||||||
|
end
|
||||||
|
|
||||||
|
def redis_connection_for_subscriptions
|
||||||
|
redis_connection
|
||||||
|
end
|
||||||
|
|
||||||
|
private
|
||||||
|
def listener
|
||||||
|
@listener || @server.mutex.synchronize { @listener ||= Listener.new(self, @server.event_loop) }
|
||||||
|
end
|
||||||
|
|
||||||
|
def redis_connection_for_broadcasts
|
||||||
|
@redis_connection_for_broadcasts || @server.mutex.synchronize do
|
||||||
|
@redis_connection_for_broadcasts ||= redis_connection
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
def redis_connection
|
||||||
|
self.class.redis_connector.call(@server.config.cable)
|
||||||
|
end
|
||||||
|
|
||||||
|
class Listener < SubscriberMap
|
||||||
|
def initialize(adapter, event_loop)
|
||||||
|
super()
|
||||||
|
|
||||||
|
@adapter = adapter
|
||||||
|
@event_loop = event_loop
|
||||||
|
|
||||||
|
@subscribe_callbacks = Hash.new { |h, k| h[k] = [] }
|
||||||
|
@subscription_lock = Mutex.new
|
||||||
|
|
||||||
|
@raw_client = nil
|
||||||
|
|
||||||
|
@when_connected = []
|
||||||
|
|
||||||
|
@thread = nil
|
||||||
|
end
|
||||||
|
|
||||||
|
def listen(conn)
|
||||||
|
conn.without_reconnect do
|
||||||
|
original_client = conn.respond_to?(:_client) ? conn._client : conn.client
|
||||||
|
|
||||||
|
conn.subscribe("_action_cable_internal") do |on|
|
||||||
|
on.subscribe do |chan, count|
|
||||||
|
@subscription_lock.synchronize do
|
||||||
|
if count == 1
|
||||||
|
@raw_client = original_client
|
||||||
|
|
||||||
|
until @when_connected.empty?
|
||||||
|
@when_connected.shift.call
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
if callbacks = @subscribe_callbacks[chan]
|
||||||
|
next_callback = callbacks.shift
|
||||||
|
@event_loop.post(&next_callback) if next_callback
|
||||||
|
@subscribe_callbacks.delete(chan) if callbacks.empty?
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
on.message do |chan, message|
|
||||||
|
broadcast(chan, message)
|
||||||
|
end
|
||||||
|
|
||||||
|
on.unsubscribe do |chan, count|
|
||||||
|
if count == 0
|
||||||
|
@subscription_lock.synchronize do
|
||||||
|
@raw_client = nil
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
def shutdown
|
||||||
|
@subscription_lock.synchronize do
|
||||||
|
return if @thread.nil?
|
||||||
|
|
||||||
|
when_connected do
|
||||||
|
send_command("unsubscribe")
|
||||||
|
@raw_client = nil
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
Thread.pass while @thread.alive?
|
||||||
|
end
|
||||||
|
|
||||||
|
def add_channel(channel, on_success)
|
||||||
|
@subscription_lock.synchronize do
|
||||||
|
ensure_listener_running
|
||||||
|
@subscribe_callbacks[channel] << on_success
|
||||||
|
when_connected { send_command("subscribe", channel) }
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
def remove_channel(channel)
|
||||||
|
@subscription_lock.synchronize do
|
||||||
|
when_connected { send_command("unsubscribe", channel) }
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
def invoke_callback(*)
|
||||||
|
@event_loop.post { super }
|
||||||
|
end
|
||||||
|
|
||||||
|
private
|
||||||
|
def ensure_listener_running
|
||||||
|
@thread ||= Thread.new do
|
||||||
|
Thread.current.abort_on_exception = true
|
||||||
|
|
||||||
|
conn = @adapter.redis_connection_for_subscriptions
|
||||||
|
listen conn
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
def when_connected(&block)
|
||||||
|
if @raw_client
|
||||||
|
block.call
|
||||||
|
else
|
||||||
|
@when_connected << block
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
def send_command(*command)
|
||||||
|
@raw_client.write(command)
|
||||||
|
|
||||||
|
very_raw_connection =
|
||||||
|
@raw_client.connection.instance_variable_defined?(:@connection) &&
|
||||||
|
@raw_client.connection.instance_variable_get(:@connection)
|
||||||
|
|
||||||
|
if very_raw_connection && very_raw_connection.respond_to?(:flush)
|
||||||
|
very_raw_connection.flush
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
57
debian/gems-compat/actioncable-5.1.6.1/lib/action_cable/subscription_adapter/subscriber_map.rb
vendored
Normal file
57
debian/gems-compat/actioncable-5.1.6.1/lib/action_cable/subscription_adapter/subscriber_map.rb
vendored
Normal file
|
@ -0,0 +1,57 @@
|
||||||
|
module ActionCable
|
||||||
|
module SubscriptionAdapter
|
||||||
|
class SubscriberMap
|
||||||
|
def initialize
|
||||||
|
@subscribers = Hash.new { |h, k| h[k] = [] }
|
||||||
|
@sync = Mutex.new
|
||||||
|
end
|
||||||
|
|
||||||
|
def add_subscriber(channel, subscriber, on_success)
|
||||||
|
@sync.synchronize do
|
||||||
|
new_channel = !@subscribers.key?(channel)
|
||||||
|
|
||||||
|
@subscribers[channel] << subscriber
|
||||||
|
|
||||||
|
if new_channel
|
||||||
|
add_channel channel, on_success
|
||||||
|
elsif on_success
|
||||||
|
on_success.call
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
def remove_subscriber(channel, subscriber)
|
||||||
|
@sync.synchronize do
|
||||||
|
@subscribers[channel].delete(subscriber)
|
||||||
|
|
||||||
|
if @subscribers[channel].empty?
|
||||||
|
@subscribers.delete channel
|
||||||
|
remove_channel channel
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
def broadcast(channel, message)
|
||||||
|
list = @sync.synchronize do
|
||||||
|
return if !@subscribers.key?(channel)
|
||||||
|
@subscribers[channel].dup
|
||||||
|
end
|
||||||
|
|
||||||
|
list.each do |subscriber|
|
||||||
|
invoke_callback(subscriber, message)
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
def add_channel(channel, on_success)
|
||||||
|
on_success.call if on_success
|
||||||
|
end
|
||||||
|
|
||||||
|
def remove_channel(channel)
|
||||||
|
end
|
||||||
|
|
||||||
|
def invoke_callback(callback, message)
|
||||||
|
callback.call message
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
8
debian/gems-compat/actioncable-5.1.6.1/lib/action_cable/version.rb
vendored
Normal file
8
debian/gems-compat/actioncable-5.1.6.1/lib/action_cable/version.rb
vendored
Normal file
|
@ -0,0 +1,8 @@
|
||||||
|
require_relative "gem_version"
|
||||||
|
|
||||||
|
module ActionCable
|
||||||
|
# Returns the version of the currently loaded Action Cable as a <tt>Gem::Version</tt>
|
||||||
|
def self.version
|
||||||
|
gem_version
|
||||||
|
end
|
||||||
|
end
|
601
debian/gems-compat/actioncable-5.1.6.1/lib/assets/compiled/action_cable.js
vendored
Normal file
601
debian/gems-compat/actioncable-5.1.6.1/lib/assets/compiled/action_cable.js
vendored
Normal file
|
@ -0,0 +1,601 @@
|
||||||
|
(function() {
|
||||||
|
var context = this;
|
||||||
|
|
||||||
|
(function() {
|
||||||
|
(function() {
|
||||||
|
var slice = [].slice;
|
||||||
|
|
||||||
|
this.ActionCable = {
|
||||||
|
INTERNAL: {
|
||||||
|
"message_types": {
|
||||||
|
"welcome": "welcome",
|
||||||
|
"ping": "ping",
|
||||||
|
"confirmation": "confirm_subscription",
|
||||||
|
"rejection": "reject_subscription"
|
||||||
|
},
|
||||||
|
"default_mount_path": "/cable",
|
||||||
|
"protocols": ["actioncable-v1-json", "actioncable-unsupported"]
|
||||||
|
},
|
||||||
|
WebSocket: window.WebSocket,
|
||||||
|
logger: window.console,
|
||||||
|
createConsumer: function(url) {
|
||||||
|
var ref;
|
||||||
|
if (url == null) {
|
||||||
|
url = (ref = this.getConfig("url")) != null ? ref : this.INTERNAL.default_mount_path;
|
||||||
|
}
|
||||||
|
return new ActionCable.Consumer(this.createWebSocketURL(url));
|
||||||
|
},
|
||||||
|
getConfig: function(name) {
|
||||||
|
var element;
|
||||||
|
element = document.head.querySelector("meta[name='action-cable-" + name + "']");
|
||||||
|
return element != null ? element.getAttribute("content") : void 0;
|
||||||
|
},
|
||||||
|
createWebSocketURL: function(url) {
|
||||||
|
var a;
|
||||||
|
if (url && !/^wss?:/i.test(url)) {
|
||||||
|
a = document.createElement("a");
|
||||||
|
a.href = url;
|
||||||
|
a.href = a.href;
|
||||||
|
a.protocol = a.protocol.replace("http", "ws");
|
||||||
|
return a.href;
|
||||||
|
} else {
|
||||||
|
return url;
|
||||||
|
}
|
||||||
|
},
|
||||||
|
startDebugging: function() {
|
||||||
|
return this.debugging = true;
|
||||||
|
},
|
||||||
|
stopDebugging: function() {
|
||||||
|
return this.debugging = null;
|
||||||
|
},
|
||||||
|
log: function() {
|
||||||
|
var messages, ref;
|
||||||
|
messages = 1 <= arguments.length ? slice.call(arguments, 0) : [];
|
||||||
|
if (this.debugging) {
|
||||||
|
messages.push(Date.now());
|
||||||
|
return (ref = this.logger).log.apply(ref, ["[ActionCable]"].concat(slice.call(messages)));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
}).call(this);
|
||||||
|
}).call(context);
|
||||||
|
|
||||||
|
var ActionCable = context.ActionCable;
|
||||||
|
|
||||||
|
(function() {
|
||||||
|
(function() {
|
||||||
|
var bind = function(fn, me){ return function(){ return fn.apply(me, arguments); }; };
|
||||||
|
|
||||||
|
ActionCable.ConnectionMonitor = (function() {
|
||||||
|
var clamp, now, secondsSince;
|
||||||
|
|
||||||
|
ConnectionMonitor.pollInterval = {
|
||||||
|
min: 3,
|
||||||
|
max: 30
|
||||||
|
};
|
||||||
|
|
||||||
|
ConnectionMonitor.staleThreshold = 6;
|
||||||
|
|
||||||
|
function ConnectionMonitor(connection) {
|
||||||
|
this.connection = connection;
|
||||||
|
this.visibilityDidChange = bind(this.visibilityDidChange, this);
|
||||||
|
this.reconnectAttempts = 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
ConnectionMonitor.prototype.start = function() {
|
||||||
|
if (!this.isRunning()) {
|
||||||
|
this.startedAt = now();
|
||||||
|
delete this.stoppedAt;
|
||||||
|
this.startPolling();
|
||||||
|
document.addEventListener("visibilitychange", this.visibilityDidChange);
|
||||||
|
return ActionCable.log("ConnectionMonitor started. pollInterval = " + (this.getPollInterval()) + " ms");
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
ConnectionMonitor.prototype.stop = function() {
|
||||||
|
if (this.isRunning()) {
|
||||||
|
this.stoppedAt = now();
|
||||||
|
this.stopPolling();
|
||||||
|
document.removeEventListener("visibilitychange", this.visibilityDidChange);
|
||||||
|
return ActionCable.log("ConnectionMonitor stopped");
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
ConnectionMonitor.prototype.isRunning = function() {
|
||||||
|
return (this.startedAt != null) && (this.stoppedAt == null);
|
||||||
|
};
|
||||||
|
|
||||||
|
ConnectionMonitor.prototype.recordPing = function() {
|
||||||
|
return this.pingedAt = now();
|
||||||
|
};
|
||||||
|
|
||||||
|
ConnectionMonitor.prototype.recordConnect = function() {
|
||||||
|
this.reconnectAttempts = 0;
|
||||||
|
this.recordPing();
|
||||||
|
delete this.disconnectedAt;
|
||||||
|
return ActionCable.log("ConnectionMonitor recorded connect");
|
||||||
|
};
|
||||||
|
|
||||||
|
ConnectionMonitor.prototype.recordDisconnect = function() {
|
||||||
|
this.disconnectedAt = now();
|
||||||
|
return ActionCable.log("ConnectionMonitor recorded disconnect");
|
||||||
|
};
|
||||||
|
|
||||||
|
ConnectionMonitor.prototype.startPolling = function() {
|
||||||
|
this.stopPolling();
|
||||||
|
return this.poll();
|
||||||
|
};
|
||||||
|
|
||||||
|
ConnectionMonitor.prototype.stopPolling = function() {
|
||||||
|
return clearTimeout(this.pollTimeout);
|
||||||
|
};
|
||||||
|
|
||||||
|
ConnectionMonitor.prototype.poll = function() {
|
||||||
|
return this.pollTimeout = setTimeout((function(_this) {
|
||||||
|
return function() {
|
||||||
|
_this.reconnectIfStale();
|
||||||
|
return _this.poll();
|
||||||
|
};
|
||||||
|
})(this), this.getPollInterval());
|
||||||
|
};
|
||||||
|
|
||||||
|
ConnectionMonitor.prototype.getPollInterval = function() {
|
||||||
|
var interval, max, min, ref;
|
||||||
|
ref = this.constructor.pollInterval, min = ref.min, max = ref.max;
|
||||||
|
interval = 5 * Math.log(this.reconnectAttempts + 1);
|
||||||
|
return Math.round(clamp(interval, min, max) * 1000);
|
||||||
|
};
|
||||||
|
|
||||||
|
ConnectionMonitor.prototype.reconnectIfStale = function() {
|
||||||
|
if (this.connectionIsStale()) {
|
||||||
|
ActionCable.log("ConnectionMonitor detected stale connection. reconnectAttempts = " + this.reconnectAttempts + ", pollInterval = " + (this.getPollInterval()) + " ms, time disconnected = " + (secondsSince(this.disconnectedAt)) + " s, stale threshold = " + this.constructor.staleThreshold + " s");
|
||||||
|
this.reconnectAttempts++;
|
||||||
|
if (this.disconnectedRecently()) {
|
||||||
|
return ActionCable.log("ConnectionMonitor skipping reopening recent disconnect");
|
||||||
|
} else {
|
||||||
|
ActionCable.log("ConnectionMonitor reopening");
|
||||||
|
return this.connection.reopen();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
ConnectionMonitor.prototype.connectionIsStale = function() {
|
||||||
|
var ref;
|
||||||
|
return secondsSince((ref = this.pingedAt) != null ? ref : this.startedAt) > this.constructor.staleThreshold;
|
||||||
|
};
|
||||||
|
|
||||||
|
ConnectionMonitor.prototype.disconnectedRecently = function() {
|
||||||
|
return this.disconnectedAt && secondsSince(this.disconnectedAt) < this.constructor.staleThreshold;
|
||||||
|
};
|
||||||
|
|
||||||
|
ConnectionMonitor.prototype.visibilityDidChange = function() {
|
||||||
|
if (document.visibilityState === "visible") {
|
||||||
|
return setTimeout((function(_this) {
|
||||||
|
return function() {
|
||||||
|
if (_this.connectionIsStale() || !_this.connection.isOpen()) {
|
||||||
|
ActionCable.log("ConnectionMonitor reopening stale connection on visibilitychange. visbilityState = " + document.visibilityState);
|
||||||
|
return _this.connection.reopen();
|
||||||
|
}
|
||||||
|
};
|
||||||
|
})(this), 200);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
now = function() {
|
||||||
|
return new Date().getTime();
|
||||||
|
};
|
||||||
|
|
||||||
|
secondsSince = function(time) {
|
||||||
|
return (now() - time) / 1000;
|
||||||
|
};
|
||||||
|
|
||||||
|
clamp = function(number, min, max) {
|
||||||
|
return Math.max(min, Math.min(max, number));
|
||||||
|
};
|
||||||
|
|
||||||
|
return ConnectionMonitor;
|
||||||
|
|
||||||
|
})();
|
||||||
|
|
||||||
|
}).call(this);
|
||||||
|
(function() {
|
||||||
|
var i, message_types, protocols, ref, supportedProtocols, unsupportedProtocol,
|
||||||
|
slice = [].slice,
|
||||||
|
bind = function(fn, me){ return function(){ return fn.apply(me, arguments); }; },
|
||||||
|
indexOf = [].indexOf || function(item) { for (var i = 0, l = this.length; i < l; i++) { if (i in this && this[i] === item) return i; } return -1; };
|
||||||
|
|
||||||
|
ref = ActionCable.INTERNAL, message_types = ref.message_types, protocols = ref.protocols;
|
||||||
|
|
||||||
|
supportedProtocols = 2 <= protocols.length ? slice.call(protocols, 0, i = protocols.length - 1) : (i = 0, []), unsupportedProtocol = protocols[i++];
|
||||||
|
|
||||||
|
ActionCable.Connection = (function() {
|
||||||
|
Connection.reopenDelay = 500;
|
||||||
|
|
||||||
|
function Connection(consumer) {
|
||||||
|
this.consumer = consumer;
|
||||||
|
this.open = bind(this.open, this);
|
||||||
|
this.subscriptions = this.consumer.subscriptions;
|
||||||
|
this.monitor = new ActionCable.ConnectionMonitor(this);
|
||||||
|
this.disconnected = true;
|
||||||
|
}
|
||||||
|
|
||||||
|
Connection.prototype.send = function(data) {
|
||||||
|
if (this.isOpen()) {
|
||||||
|
this.webSocket.send(JSON.stringify(data));
|
||||||
|
return true;
|
||||||
|
} else {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
Connection.prototype.open = function() {
|
||||||
|
if (this.isActive()) {
|
||||||
|
ActionCable.log("Attempted to open WebSocket, but existing socket is " + (this.getState()));
|
||||||
|
return false;
|
||||||
|
} else {
|
||||||
|
ActionCable.log("Opening WebSocket, current state is " + (this.getState()) + ", subprotocols: " + protocols);
|
||||||
|
if (this.webSocket != null) {
|
||||||
|
this.uninstallEventHandlers();
|
||||||
|
}
|
||||||
|
this.webSocket = new ActionCable.WebSocket(this.consumer.url, protocols);
|
||||||
|
this.installEventHandlers();
|
||||||
|
this.monitor.start();
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
Connection.prototype.close = function(arg) {
|
||||||
|
var allowReconnect, ref1;
|
||||||
|
allowReconnect = (arg != null ? arg : {
|
||||||
|
allowReconnect: true
|
||||||
|
}).allowReconnect;
|
||||||
|
if (!allowReconnect) {
|
||||||
|
this.monitor.stop();
|
||||||
|
}
|
||||||
|
if (this.isActive()) {
|
||||||
|
return (ref1 = this.webSocket) != null ? ref1.close() : void 0;
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
Connection.prototype.reopen = function() {
|
||||||
|
var error;
|
||||||
|
ActionCable.log("Reopening WebSocket, current state is " + (this.getState()));
|
||||||
|
if (this.isActive()) {
|
||||||
|
try {
|
||||||
|
return this.close();
|
||||||
|
} catch (error1) {
|
||||||
|
error = error1;
|
||||||
|
return ActionCable.log("Failed to reopen WebSocket", error);
|
||||||
|
} finally {
|
||||||
|
ActionCable.log("Reopening WebSocket in " + this.constructor.reopenDelay + "ms");
|
||||||
|
setTimeout(this.open, this.constructor.reopenDelay);
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
return this.open();
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
Connection.prototype.getProtocol = function() {
|
||||||
|
var ref1;
|
||||||
|
return (ref1 = this.webSocket) != null ? ref1.protocol : void 0;
|
||||||
|
};
|
||||||
|
|
||||||
|
Connection.prototype.isOpen = function() {
|
||||||
|
return this.isState("open");
|
||||||
|
};
|
||||||
|
|
||||||
|
Connection.prototype.isActive = function() {
|
||||||
|
return this.isState("open", "connecting");
|
||||||
|
};
|
||||||
|
|
||||||
|
Connection.prototype.isProtocolSupported = function() {
|
||||||
|
var ref1;
|
||||||
|
return ref1 = this.getProtocol(), indexOf.call(supportedProtocols, ref1) >= 0;
|
||||||
|
};
|
||||||
|
|
||||||
|
Connection.prototype.isState = function() {
|
||||||
|
var ref1, states;
|
||||||
|
states = 1 <= arguments.length ? slice.call(arguments, 0) : [];
|
||||||
|
return ref1 = this.getState(), indexOf.call(states, ref1) >= 0;
|
||||||
|
};
|
||||||
|
|
||||||
|
Connection.prototype.getState = function() {
|
||||||
|
var ref1, state, value;
|
||||||
|
for (state in WebSocket) {
|
||||||
|
value = WebSocket[state];
|
||||||
|
if (value === ((ref1 = this.webSocket) != null ? ref1.readyState : void 0)) {
|
||||||
|
return state.toLowerCase();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return null;
|
||||||
|
};
|
||||||
|
|
||||||
|
Connection.prototype.installEventHandlers = function() {
|
||||||
|
var eventName, handler;
|
||||||
|
for (eventName in this.events) {
|
||||||
|
handler = this.events[eventName].bind(this);
|
||||||
|
this.webSocket["on" + eventName] = handler;
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
Connection.prototype.uninstallEventHandlers = function() {
|
||||||
|
var eventName;
|
||||||
|
for (eventName in this.events) {
|
||||||
|
this.webSocket["on" + eventName] = function() {};
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
Connection.prototype.events = {
|
||||||
|
message: function(event) {
|
||||||
|
var identifier, message, ref1, type;
|
||||||
|
if (!this.isProtocolSupported()) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
ref1 = JSON.parse(event.data), identifier = ref1.identifier, message = ref1.message, type = ref1.type;
|
||||||
|
switch (type) {
|
||||||
|
case message_types.welcome:
|
||||||
|
this.monitor.recordConnect();
|
||||||
|
return this.subscriptions.reload();
|
||||||
|
case message_types.ping:
|
||||||
|
return this.monitor.recordPing();
|
||||||
|
case message_types.confirmation:
|
||||||
|
return this.subscriptions.notify(identifier, "connected");
|
||||||
|
case message_types.rejection:
|
||||||
|
return this.subscriptions.reject(identifier);
|
||||||
|
default:
|
||||||
|
return this.subscriptions.notify(identifier, "received", message);
|
||||||
|
}
|
||||||
|
},
|
||||||
|
open: function() {
|
||||||
|
ActionCable.log("WebSocket onopen event, using '" + (this.getProtocol()) + "' subprotocol");
|
||||||
|
this.disconnected = false;
|
||||||
|
if (!this.isProtocolSupported()) {
|
||||||
|
ActionCable.log("Protocol is unsupported. Stopping monitor and disconnecting.");
|
||||||
|
return this.close({
|
||||||
|
allowReconnect: false
|
||||||
|
});
|
||||||
|
}
|
||||||
|
},
|
||||||
|
close: function(event) {
|
||||||
|
ActionCable.log("WebSocket onclose event");
|
||||||
|
if (this.disconnected) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
this.disconnected = true;
|
||||||
|
this.monitor.recordDisconnect();
|
||||||
|
return this.subscriptions.notifyAll("disconnected", {
|
||||||
|
willAttemptReconnect: this.monitor.isRunning()
|
||||||
|
});
|
||||||
|
},
|
||||||
|
error: function() {
|
||||||
|
return ActionCable.log("WebSocket onerror event");
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
return Connection;
|
||||||
|
|
||||||
|
})();
|
||||||
|
|
||||||
|
}).call(this);
|
||||||
|
(function() {
|
||||||
|
var slice = [].slice;
|
||||||
|
|
||||||
|
ActionCable.Subscriptions = (function() {
|
||||||
|
function Subscriptions(consumer) {
|
||||||
|
this.consumer = consumer;
|
||||||
|
this.subscriptions = [];
|
||||||
|
}
|
||||||
|
|
||||||
|
Subscriptions.prototype.create = function(channelName, mixin) {
|
||||||
|
var channel, params, subscription;
|
||||||
|
channel = channelName;
|
||||||
|
params = typeof channel === "object" ? channel : {
|
||||||
|
channel: channel
|
||||||
|
};
|
||||||
|
subscription = new ActionCable.Subscription(this.consumer, params, mixin);
|
||||||
|
return this.add(subscription);
|
||||||
|
};
|
||||||
|
|
||||||
|
Subscriptions.prototype.add = function(subscription) {
|
||||||
|
this.subscriptions.push(subscription);
|
||||||
|
this.consumer.ensureActiveConnection();
|
||||||
|
this.notify(subscription, "initialized");
|
||||||
|
this.sendCommand(subscription, "subscribe");
|
||||||
|
return subscription;
|
||||||
|
};
|
||||||
|
|
||||||
|
Subscriptions.prototype.remove = function(subscription) {
|
||||||
|
this.forget(subscription);
|
||||||
|
if (!this.findAll(subscription.identifier).length) {
|
||||||
|
this.sendCommand(subscription, "unsubscribe");
|
||||||
|
}
|
||||||
|
return subscription;
|
||||||
|
};
|
||||||
|
|
||||||
|
Subscriptions.prototype.reject = function(identifier) {
|
||||||
|
var i, len, ref, results, subscription;
|
||||||
|
ref = this.findAll(identifier);
|
||||||
|
results = [];
|
||||||
|
for (i = 0, len = ref.length; i < len; i++) {
|
||||||
|
subscription = ref[i];
|
||||||
|
this.forget(subscription);
|
||||||
|
this.notify(subscription, "rejected");
|
||||||
|
results.push(subscription);
|
||||||
|
}
|
||||||
|
return results;
|
||||||
|
};
|
||||||
|
|
||||||
|
Subscriptions.prototype.forget = function(subscription) {
|
||||||
|
var s;
|
||||||
|
this.subscriptions = (function() {
|
||||||
|
var i, len, ref, results;
|
||||||
|
ref = this.subscriptions;
|
||||||
|
results = [];
|
||||||
|
for (i = 0, len = ref.length; i < len; i++) {
|
||||||
|
s = ref[i];
|
||||||
|
if (s !== subscription) {
|
||||||
|
results.push(s);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return results;
|
||||||
|
}).call(this);
|
||||||
|
return subscription;
|
||||||
|
};
|
||||||
|
|
||||||
|
Subscriptions.prototype.findAll = function(identifier) {
|
||||||
|
var i, len, ref, results, s;
|
||||||
|
ref = this.subscriptions;
|
||||||
|
results = [];
|
||||||
|
for (i = 0, len = ref.length; i < len; i++) {
|
||||||
|
s = ref[i];
|
||||||
|
if (s.identifier === identifier) {
|
||||||
|
results.push(s);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return results;
|
||||||
|
};
|
||||||
|
|
||||||
|
Subscriptions.prototype.reload = function() {
|
||||||
|
var i, len, ref, results, subscription;
|
||||||
|
ref = this.subscriptions;
|
||||||
|
results = [];
|
||||||
|
for (i = 0, len = ref.length; i < len; i++) {
|
||||||
|
subscription = ref[i];
|
||||||
|
results.push(this.sendCommand(subscription, "subscribe"));
|
||||||
|
}
|
||||||
|
return results;
|
||||||
|
};
|
||||||
|
|
||||||
|
Subscriptions.prototype.notifyAll = function() {
|
||||||
|
var args, callbackName, i, len, ref, results, subscription;
|
||||||
|
callbackName = arguments[0], args = 2 <= arguments.length ? slice.call(arguments, 1) : [];
|
||||||
|
ref = this.subscriptions;
|
||||||
|
results = [];
|
||||||
|
for (i = 0, len = ref.length; i < len; i++) {
|
||||||
|
subscription = ref[i];
|
||||||
|
results.push(this.notify.apply(this, [subscription, callbackName].concat(slice.call(args))));
|
||||||
|
}
|
||||||
|
return results;
|
||||||
|
};
|
||||||
|
|
||||||
|
Subscriptions.prototype.notify = function() {
|
||||||
|
var args, callbackName, i, len, results, subscription, subscriptions;
|
||||||
|
subscription = arguments[0], callbackName = arguments[1], args = 3 <= arguments.length ? slice.call(arguments, 2) : [];
|
||||||
|
if (typeof subscription === "string") {
|
||||||
|
subscriptions = this.findAll(subscription);
|
||||||
|
} else {
|
||||||
|
subscriptions = [subscription];
|
||||||
|
}
|
||||||
|
results = [];
|
||||||
|
for (i = 0, len = subscriptions.length; i < len; i++) {
|
||||||
|
subscription = subscriptions[i];
|
||||||
|
results.push(typeof subscription[callbackName] === "function" ? subscription[callbackName].apply(subscription, args) : void 0);
|
||||||
|
}
|
||||||
|
return results;
|
||||||
|
};
|
||||||
|
|
||||||
|
Subscriptions.prototype.sendCommand = function(subscription, command) {
|
||||||
|
var identifier;
|
||||||
|
identifier = subscription.identifier;
|
||||||
|
return this.consumer.send({
|
||||||
|
command: command,
|
||||||
|
identifier: identifier
|
||||||
|
});
|
||||||
|
};
|
||||||
|
|
||||||
|
return Subscriptions;
|
||||||
|
|
||||||
|
})();
|
||||||
|
|
||||||
|
}).call(this);
|
||||||
|
(function() {
|
||||||
|
ActionCable.Subscription = (function() {
|
||||||
|
var extend;
|
||||||
|
|
||||||
|
function Subscription(consumer, params, mixin) {
|
||||||
|
this.consumer = consumer;
|
||||||
|
if (params == null) {
|
||||||
|
params = {};
|
||||||
|
}
|
||||||
|
this.identifier = JSON.stringify(params);
|
||||||
|
extend(this, mixin);
|
||||||
|
}
|
||||||
|
|
||||||
|
Subscription.prototype.perform = function(action, data) {
|
||||||
|
if (data == null) {
|
||||||
|
data = {};
|
||||||
|
}
|
||||||
|
data.action = action;
|
||||||
|
return this.send(data);
|
||||||
|
};
|
||||||
|
|
||||||
|
Subscription.prototype.send = function(data) {
|
||||||
|
return this.consumer.send({
|
||||||
|
command: "message",
|
||||||
|
identifier: this.identifier,
|
||||||
|
data: JSON.stringify(data)
|
||||||
|
});
|
||||||
|
};
|
||||||
|
|
||||||
|
Subscription.prototype.unsubscribe = function() {
|
||||||
|
return this.consumer.subscriptions.remove(this);
|
||||||
|
};
|
||||||
|
|
||||||
|
extend = function(object, properties) {
|
||||||
|
var key, value;
|
||||||
|
if (properties != null) {
|
||||||
|
for (key in properties) {
|
||||||
|
value = properties[key];
|
||||||
|
object[key] = value;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return object;
|
||||||
|
};
|
||||||
|
|
||||||
|
return Subscription;
|
||||||
|
|
||||||
|
})();
|
||||||
|
|
||||||
|
}).call(this);
|
||||||
|
(function() {
|
||||||
|
ActionCable.Consumer = (function() {
|
||||||
|
function Consumer(url) {
|
||||||
|
this.url = url;
|
||||||
|
this.subscriptions = new ActionCable.Subscriptions(this);
|
||||||
|
this.connection = new ActionCable.Connection(this);
|
||||||
|
}
|
||||||
|
|
||||||
|
Consumer.prototype.send = function(data) {
|
||||||
|
return this.connection.send(data);
|
||||||
|
};
|
||||||
|
|
||||||
|
Consumer.prototype.connect = function() {
|
||||||
|
return this.connection.open();
|
||||||
|
};
|
||||||
|
|
||||||
|
Consumer.prototype.disconnect = function() {
|
||||||
|
return this.connection.close({
|
||||||
|
allowReconnect: false
|
||||||
|
});
|
||||||
|
};
|
||||||
|
|
||||||
|
Consumer.prototype.ensureActiveConnection = function() {
|
||||||
|
if (!this.connection.isActive()) {
|
||||||
|
return this.connection.open();
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
return Consumer;
|
||||||
|
|
||||||
|
})();
|
||||||
|
|
||||||
|
}).call(this);
|
||||||
|
}).call(this);
|
||||||
|
|
||||||
|
if (typeof module === "object" && module.exports) {
|
||||||
|
module.exports = ActionCable;
|
||||||
|
} else if (typeof define === "function" && define.amd) {
|
||||||
|
define(ActionCable);
|
||||||
|
}
|
||||||
|
}).call(this);
|
14
debian/gems-compat/actioncable-5.1.6.1/lib/rails/generators/channel/USAGE
vendored
Normal file
14
debian/gems-compat/actioncable-5.1.6.1/lib/rails/generators/channel/USAGE
vendored
Normal file
|
@ -0,0 +1,14 @@
|
||||||
|
Description:
|
||||||
|
============
|
||||||
|
Stubs out a new cable channel for the server (in Ruby) and client (in CoffeeScript).
|
||||||
|
Pass the channel name, either CamelCased or under_scored, and an optional list of channel actions as arguments.
|
||||||
|
|
||||||
|
Note: Turn on the cable connection in app/assets/javascripts/cable.js after generating any channels.
|
||||||
|
|
||||||
|
Example:
|
||||||
|
========
|
||||||
|
rails generate channel Chat speak
|
||||||
|
|
||||||
|
creates a Chat channel class and CoffeeScript asset:
|
||||||
|
Channel: app/channels/chat_channel.rb
|
||||||
|
Assets: app/assets/javascripts/channels/chat.coffee
|
47
debian/gems-compat/actioncable-5.1.6.1/lib/rails/generators/channel/channel_generator.rb
vendored
Normal file
47
debian/gems-compat/actioncable-5.1.6.1/lib/rails/generators/channel/channel_generator.rb
vendored
Normal file
|
@ -0,0 +1,47 @@
|
||||||
|
module Rails
|
||||||
|
module Generators
|
||||||
|
class ChannelGenerator < NamedBase
|
||||||
|
source_root File.expand_path("../templates", __FILE__)
|
||||||
|
|
||||||
|
argument :actions, type: :array, default: [], banner: "method method"
|
||||||
|
|
||||||
|
class_option :assets, type: :boolean
|
||||||
|
|
||||||
|
check_class_collision suffix: "Channel"
|
||||||
|
|
||||||
|
def create_channel_file
|
||||||
|
template "channel.rb", File.join("app/channels", class_path, "#{file_name}_channel.rb")
|
||||||
|
|
||||||
|
if options[:assets]
|
||||||
|
if behavior == :invoke
|
||||||
|
template "assets/cable.js", "app/assets/javascripts/cable.js"
|
||||||
|
end
|
||||||
|
|
||||||
|
js_template "assets/channel", File.join("app/assets/javascripts/channels", class_path, "#{file_name}")
|
||||||
|
end
|
||||||
|
|
||||||
|
generate_application_cable_files
|
||||||
|
end
|
||||||
|
|
||||||
|
private
|
||||||
|
def file_name
|
||||||
|
@_file_name ||= super.gsub(/_channel/i, "")
|
||||||
|
end
|
||||||
|
|
||||||
|
# FIXME: Change these files to symlinks once RubyGems 2.5.0 is required.
|
||||||
|
def generate_application_cable_files
|
||||||
|
return if behavior != :invoke
|
||||||
|
|
||||||
|
files = [
|
||||||
|
"application_cable/channel.rb",
|
||||||
|
"application_cable/connection.rb"
|
||||||
|
]
|
||||||
|
|
||||||
|
files.each do |name|
|
||||||
|
path = File.join("app/channels/", name)
|
||||||
|
template(name, path) if !File.exist?(path)
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
|
@ -0,0 +1,4 @@
|
||||||
|
module ApplicationCable
|
||||||
|
class Channel < ActionCable::Channel::Base
|
||||||
|
end
|
||||||
|
end
|
|
@ -0,0 +1,4 @@
|
||||||
|
module ApplicationCable
|
||||||
|
class Connection < ActionCable::Connection::Base
|
||||||
|
end
|
||||||
|
end
|
13
debian/gems-compat/actioncable-5.1.6.1/lib/rails/generators/channel/templates/assets/cable.js
vendored
Normal file
13
debian/gems-compat/actioncable-5.1.6.1/lib/rails/generators/channel/templates/assets/cable.js
vendored
Normal file
|
@ -0,0 +1,13 @@
|
||||||
|
// Action Cable provides the framework to deal with WebSockets in Rails.
|
||||||
|
// You can generate new channels where WebSocket features live using the `rails generate channel` command.
|
||||||
|
//
|
||||||
|
//= require action_cable
|
||||||
|
//= require_self
|
||||||
|
//= require_tree ./channels
|
||||||
|
|
||||||
|
(function() {
|
||||||
|
this.App || (this.App = {});
|
||||||
|
|
||||||
|
App.cable = ActionCable.createConsumer();
|
||||||
|
|
||||||
|
}).call(this);
|
|
@ -0,0 +1,14 @@
|
||||||
|
App.<%= class_name.underscore %> = App.cable.subscriptions.create "<%= class_name %>Channel",
|
||||||
|
connected: ->
|
||||||
|
# Called when the subscription is ready for use on the server
|
||||||
|
|
||||||
|
disconnected: ->
|
||||||
|
# Called when the subscription has been terminated by the server
|
||||||
|
|
||||||
|
received: (data) ->
|
||||||
|
# Called when there's incoming data on the websocket for this channel
|
||||||
|
<% actions.each do |action| -%>
|
||||||
|
|
||||||
|
<%= action %>: ->
|
||||||
|
@perform '<%= action %>'
|
||||||
|
<% end -%>
|
18
debian/gems-compat/actioncable-5.1.6.1/lib/rails/generators/channel/templates/assets/channel.js
vendored
Normal file
18
debian/gems-compat/actioncable-5.1.6.1/lib/rails/generators/channel/templates/assets/channel.js
vendored
Normal file
|
@ -0,0 +1,18 @@
|
||||||
|
App.<%= class_name.underscore %> = App.cable.subscriptions.create("<%= class_name %>Channel", {
|
||||||
|
connected: function() {
|
||||||
|
// Called when the subscription is ready for use on the server
|
||||||
|
},
|
||||||
|
|
||||||
|
disconnected: function() {
|
||||||
|
// Called when the subscription has been terminated by the server
|
||||||
|
},
|
||||||
|
|
||||||
|
received: function(data) {
|
||||||
|
// Called when there's incoming data on the websocket for this channel
|
||||||
|
}<%= actions.any? ? ",\n" : '' %>
|
||||||
|
<% actions.each do |action| -%>
|
||||||
|
<%=action %>: function() {
|
||||||
|
return this.perform('<%= action %>');
|
||||||
|
}<%= action == actions[-1] ? '' : ",\n" %>
|
||||||
|
<% end -%>
|
||||||
|
});
|
16
debian/gems-compat/actioncable-5.1.6.1/lib/rails/generators/channel/templates/channel.rb
vendored
Normal file
16
debian/gems-compat/actioncable-5.1.6.1/lib/rails/generators/channel/templates/channel.rb
vendored
Normal file
|
@ -0,0 +1,16 @@
|
||||||
|
<% module_namespacing do -%>
|
||||||
|
class <%= class_name %>Channel < ApplicationCable::Channel
|
||||||
|
def subscribed
|
||||||
|
# stream_from "some_channel"
|
||||||
|
end
|
||||||
|
|
||||||
|
def unsubscribed
|
||||||
|
# Any cleanup needed when channel is unsubscribed
|
||||||
|
end
|
||||||
|
<% actions.each do |action| -%>
|
||||||
|
|
||||||
|
def <%= action %>
|
||||||
|
end
|
||||||
|
<% end -%>
|
||||||
|
end
|
||||||
|
<% end -%>
|
91
debian/gems-compat/actionmailer-5.1.6.1/CHANGELOG.md
vendored
Normal file
91
debian/gems-compat/actionmailer-5.1.6.1/CHANGELOG.md
vendored
Normal file
|
@ -0,0 +1,91 @@
|
||||||
|
## Rails 5.1.6.1 (November 27, 2018) ##
|
||||||
|
|
||||||
|
* No changes.
|
||||||
|
|
||||||
|
|
||||||
|
## Rails 5.1.6 (March 29, 2018) ##
|
||||||
|
|
||||||
|
* No changes.
|
||||||
|
|
||||||
|
|
||||||
|
## Rails 5.1.5 (February 14, 2018) ##
|
||||||
|
|
||||||
|
* Bring back proc with arity of 1 in `ActionMailer::Base.default` proc
|
||||||
|
since it was supported in Rails 5.0 but not deprecated.
|
||||||
|
|
||||||
|
*Jimmy Bourassa*
|
||||||
|
|
||||||
|
|
||||||
|
## Rails 5.1.4 (September 07, 2017) ##
|
||||||
|
|
||||||
|
* No changes.
|
||||||
|
|
||||||
|
|
||||||
|
## Rails 5.1.4.rc1 (August 24, 2017) ##
|
||||||
|
|
||||||
|
* No changes.
|
||||||
|
|
||||||
|
|
||||||
|
## Rails 5.1.3 (August 03, 2017) ##
|
||||||
|
|
||||||
|
* No changes.
|
||||||
|
|
||||||
|
|
||||||
|
## Rails 5.1.3.rc3 (July 31, 2017) ##
|
||||||
|
|
||||||
|
* No changes.
|
||||||
|
|
||||||
|
|
||||||
|
## Rails 5.1.3.rc2 (July 25, 2017) ##
|
||||||
|
|
||||||
|
* No changes.
|
||||||
|
|
||||||
|
|
||||||
|
## Rails 5.1.3.rc1 (July 19, 2017) ##
|
||||||
|
|
||||||
|
* No changes.
|
||||||
|
|
||||||
|
|
||||||
|
## Rails 5.1.2 (June 26, 2017) ##
|
||||||
|
|
||||||
|
* No changes.
|
||||||
|
|
||||||
|
|
||||||
|
## Rails 5.1.1 (May 12, 2017) ##
|
||||||
|
|
||||||
|
* No changes.
|
||||||
|
|
||||||
|
|
||||||
|
## Rails 5.1.0 (April 27, 2017) ##
|
||||||
|
|
||||||
|
* Add `:args` to `process.action_mailer` event.
|
||||||
|
|
||||||
|
*Yuji Yaginuma*
|
||||||
|
|
||||||
|
* Add parameterized invocation of mailers as a way to share before filters and defaults between actions.
|
||||||
|
See `ActionMailer::Parameterized` for a full example of the benefit.
|
||||||
|
|
||||||
|
*DHH*
|
||||||
|
|
||||||
|
* Allow lambdas to be used as lazy defaults in addition to procs.
|
||||||
|
|
||||||
|
*DHH*
|
||||||
|
|
||||||
|
* Mime type: allow to custom content type when setting body in headers
|
||||||
|
and attachments.
|
||||||
|
|
||||||
|
Example:
|
||||||
|
|
||||||
|
def test_emails
|
||||||
|
attachments["invoice.pdf"] = "This is test File content"
|
||||||
|
mail(body: "Hello there", content_type: "text/html")
|
||||||
|
end
|
||||||
|
|
||||||
|
*Minh Quy*
|
||||||
|
|
||||||
|
* Exception handling: use `rescue_from` to handle exceptions raised by
|
||||||
|
mailer actions, by message delivery, and by deferred delivery jobs.
|
||||||
|
|
||||||
|
*Jeremy Daer*
|
||||||
|
|
||||||
|
Please check [5-0-stable](https://github.com/rails/rails/blob/5-0-stable/actionmailer/CHANGELOG.md) for previous changes.
|
21
debian/gems-compat/actionmailer-5.1.6.1/MIT-LICENSE
vendored
Normal file
21
debian/gems-compat/actionmailer-5.1.6.1/MIT-LICENSE
vendored
Normal file
|
@ -0,0 +1,21 @@
|
||||||
|
Copyright (c) 2004-2017 David Heinemeier Hansson
|
||||||
|
|
||||||
|
Permission is hereby granted, free of charge, to any person obtaining
|
||||||
|
a copy of this software and associated documentation files (the
|
||||||
|
"Software"), to deal in the Software without restriction, including
|
||||||
|
without limitation the rights to use, copy, modify, merge, publish,
|
||||||
|
distribute, sublicense, and/or sell copies of the Software, and to
|
||||||
|
permit persons to whom the Software is furnished to do so, subject to
|
||||||
|
the following conditions:
|
||||||
|
|
||||||
|
The above copyright notice and this permission notice shall be
|
||||||
|
included in all copies or substantial portions of the Software.
|
||||||
|
|
||||||
|
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
|
||||||
|
EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
|
||||||
|
MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
|
||||||
|
NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
|
||||||
|
LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
|
||||||
|
OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
|
||||||
|
WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
||||||
|
|
175
debian/gems-compat/actionmailer-5.1.6.1/README.rdoc
vendored
Normal file
175
debian/gems-compat/actionmailer-5.1.6.1/README.rdoc
vendored
Normal file
|
@ -0,0 +1,175 @@
|
||||||
|
= Action Mailer -- Easy email delivery and testing
|
||||||
|
|
||||||
|
Action Mailer is a framework for designing email service layers. These layers
|
||||||
|
are used to consolidate code for sending out forgotten passwords, welcome
|
||||||
|
wishes on signup, invoices for billing, and any other use case that requires
|
||||||
|
a written notification to either a person or another system.
|
||||||
|
|
||||||
|
Action Mailer is in essence a wrapper around Action Controller and the
|
||||||
|
Mail gem. It provides a way to make emails using templates in the same
|
||||||
|
way that Action Controller renders views using templates.
|
||||||
|
|
||||||
|
Additionally, an Action Mailer class can be used to process incoming email,
|
||||||
|
such as allowing a blog to accept new posts from an email (which could even
|
||||||
|
have been sent from a phone).
|
||||||
|
|
||||||
|
== Sending emails
|
||||||
|
|
||||||
|
The framework works by initializing any instance variables you want to be
|
||||||
|
available in the email template, followed by a call to +mail+ to deliver
|
||||||
|
the email.
|
||||||
|
|
||||||
|
This can be as simple as:
|
||||||
|
|
||||||
|
class Notifier < ActionMailer::Base
|
||||||
|
default from: 'system@loudthinking.com'
|
||||||
|
|
||||||
|
def welcome(recipient)
|
||||||
|
@recipient = recipient
|
||||||
|
mail(to: recipient,
|
||||||
|
subject: "[Signed up] Welcome #{recipient}")
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
The body of the email is created by using an Action View template (regular
|
||||||
|
ERB) that has the instance variables that are declared in the mailer action.
|
||||||
|
|
||||||
|
So the corresponding body template for the method above could look like this:
|
||||||
|
|
||||||
|
Hello there,
|
||||||
|
|
||||||
|
Mr. <%= @recipient %>
|
||||||
|
|
||||||
|
Thank you for signing up!
|
||||||
|
|
||||||
|
If the recipient was given as "david@loudthinking.com", the email
|
||||||
|
generated would look like this:
|
||||||
|
|
||||||
|
Date: Mon, 25 Jan 2010 22:48:09 +1100
|
||||||
|
From: system@loudthinking.com
|
||||||
|
To: david@loudthinking.com
|
||||||
|
Message-ID: <4b5d84f9dd6a5_7380800b81ac29578@void.loudthinking.com.mail>
|
||||||
|
Subject: [Signed up] Welcome david@loudthinking.com
|
||||||
|
Mime-Version: 1.0
|
||||||
|
Content-Type: text/plain;
|
||||||
|
charset="US-ASCII";
|
||||||
|
Content-Transfer-Encoding: 7bit
|
||||||
|
|
||||||
|
Hello there,
|
||||||
|
|
||||||
|
Mr. david@loudthinking.com
|
||||||
|
|
||||||
|
Thank you for signing up!
|
||||||
|
|
||||||
|
In order to send mails, you simply call the method and then call +deliver_now+ on the return value.
|
||||||
|
|
||||||
|
Calling the method returns a Mail Message object:
|
||||||
|
|
||||||
|
message = Notifier.welcome("david@loudthinking.com") # => Returns a Mail::Message object
|
||||||
|
message.deliver_now # => delivers the email
|
||||||
|
|
||||||
|
Or you can just chain the methods together like:
|
||||||
|
|
||||||
|
Notifier.welcome("david@loudthinking.com").deliver_now # Creates the email and sends it immediately
|
||||||
|
|
||||||
|
== Setting defaults
|
||||||
|
|
||||||
|
It is possible to set default values that will be used in every method in your
|
||||||
|
Action Mailer class. To implement this functionality, you just call the public
|
||||||
|
class method +default+ which you get for free from <tt>ActionMailer::Base</tt>.
|
||||||
|
This method accepts a Hash as the parameter. You can use any of the headers,
|
||||||
|
email messages have, like +:from+ as the key. You can also pass in a string as
|
||||||
|
the key, like "Content-Type", but Action Mailer does this out of the box for you,
|
||||||
|
so you won't need to worry about that. Finally, it is also possible to pass in a
|
||||||
|
Proc that will get evaluated when it is needed.
|
||||||
|
|
||||||
|
Note that every value you set with this method will get overwritten if you use the
|
||||||
|
same key in your mailer method.
|
||||||
|
|
||||||
|
Example:
|
||||||
|
|
||||||
|
class AuthenticationMailer < ActionMailer::Base
|
||||||
|
default from: "awesome@application.com", subject: Proc.new { "E-mail was generated at #{Time.now}" }
|
||||||
|
.....
|
||||||
|
end
|
||||||
|
|
||||||
|
== Receiving emails
|
||||||
|
|
||||||
|
To receive emails, you need to implement a public instance method called
|
||||||
|
+receive+ that takes an email object as its single parameter. The Action Mailer
|
||||||
|
framework has a corresponding class method, which is also called +receive+, that
|
||||||
|
accepts a raw, unprocessed email as a string, which it then turns into the email
|
||||||
|
object and calls the receive instance method.
|
||||||
|
|
||||||
|
Example:
|
||||||
|
|
||||||
|
class Mailman < ActionMailer::Base
|
||||||
|
def receive(email)
|
||||||
|
page = Page.find_by(address: email.to.first)
|
||||||
|
page.emails.create(
|
||||||
|
subject: email.subject, body: email.body
|
||||||
|
)
|
||||||
|
|
||||||
|
if email.has_attachments?
|
||||||
|
email.attachments.each do |attachment|
|
||||||
|
page.attachments.create({
|
||||||
|
file: attachment, description: email.subject
|
||||||
|
})
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
This Mailman can be the target for Postfix or other MTAs. In Rails, you would use
|
||||||
|
the runner in the trivial case like this:
|
||||||
|
|
||||||
|
rails runner 'Mailman.receive(STDIN.read)'
|
||||||
|
|
||||||
|
However, invoking Rails in the runner for each mail to be received is very
|
||||||
|
resource intensive. A single instance of Rails should be run within a daemon, if
|
||||||
|
it is going to process more than just a limited amount of email.
|
||||||
|
|
||||||
|
== Configuration
|
||||||
|
|
||||||
|
The Base class has the full list of configuration options. Here's an example:
|
||||||
|
|
||||||
|
ActionMailer::Base.smtp_settings = {
|
||||||
|
address: 'smtp.yourserver.com', # default: localhost
|
||||||
|
port: '25', # default: 25
|
||||||
|
user_name: 'user',
|
||||||
|
password: 'pass',
|
||||||
|
authentication: :plain # :plain, :login or :cram_md5
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
== Download and installation
|
||||||
|
|
||||||
|
The latest version of Action Mailer can be installed with RubyGems:
|
||||||
|
|
||||||
|
$ gem install actionmailer
|
||||||
|
|
||||||
|
Source code can be downloaded as part of the Rails project on GitHub
|
||||||
|
|
||||||
|
* https://github.com/rails/rails/tree/master/actionmailer
|
||||||
|
|
||||||
|
|
||||||
|
== License
|
||||||
|
|
||||||
|
Action Mailer is released under the MIT license:
|
||||||
|
|
||||||
|
* http://www.opensource.org/licenses/MIT
|
||||||
|
|
||||||
|
|
||||||
|
== Support
|
||||||
|
|
||||||
|
API documentation is at
|
||||||
|
|
||||||
|
* http://api.rubyonrails.org
|
||||||
|
|
||||||
|
Bug reports can be filed for the Ruby on Rails project here:
|
||||||
|
|
||||||
|
* https://github.com/rails/rails/issues
|
||||||
|
|
||||||
|
Feature requests should be discussed on the rails-core mailing list here:
|
||||||
|
|
||||||
|
* https://groups.google.com/forum/?fromgroups#!forum/rubyonrails-core
|
49
debian/gems-compat/actionmailer-5.1.6.1/actionmailer.gemspec
vendored
Normal file
49
debian/gems-compat/actionmailer-5.1.6.1/actionmailer.gemspec
vendored
Normal file
|
@ -0,0 +1,49 @@
|
||||||
|
#########################################################
|
||||||
|
# This file has been automatically generated by gem2tgz #
|
||||||
|
#########################################################
|
||||||
|
# -*- encoding: utf-8 -*-
|
||||||
|
# stub: actionmailer 5.1.6.1 ruby lib
|
||||||
|
|
||||||
|
Gem::Specification.new do |s|
|
||||||
|
s.name = "actionmailer".freeze
|
||||||
|
s.version = "5.1.6.1"
|
||||||
|
|
||||||
|
s.required_rubygems_version = Gem::Requirement.new(">= 0".freeze) if s.respond_to? :required_rubygems_version=
|
||||||
|
s.metadata = { "changelog_uri" => "https://github.com/rails/rails/blob/v5.1.6.1/actionmailer/CHANGELOG.md", "source_code_uri" => "https://github.com/rails/rails/tree/v5.1.6.1/actionmailer" } if s.respond_to? :metadata=
|
||||||
|
s.require_paths = ["lib".freeze]
|
||||||
|
s.authors = ["David Heinemeier Hansson".freeze]
|
||||||
|
s.date = "2018-11-27"
|
||||||
|
s.description = "Email on Rails. Compose, deliver, receive, and test emails using the familiar controller/view pattern. First-class support for multipart email and attachments.".freeze
|
||||||
|
s.email = "david@loudthinking.com".freeze
|
||||||
|
s.files = ["CHANGELOG.md".freeze, "MIT-LICENSE".freeze, "README.rdoc".freeze, "lib/action_mailer.rb".freeze, "lib/action_mailer/base.rb".freeze, "lib/action_mailer/collector.rb".freeze, "lib/action_mailer/delivery_job.rb".freeze, "lib/action_mailer/delivery_methods.rb".freeze, "lib/action_mailer/gem_version.rb".freeze, "lib/action_mailer/inline_preview_interceptor.rb".freeze, "lib/action_mailer/log_subscriber.rb".freeze, "lib/action_mailer/mail_helper.rb".freeze, "lib/action_mailer/message_delivery.rb".freeze, "lib/action_mailer/parameterized.rb".freeze, "lib/action_mailer/preview.rb".freeze, "lib/action_mailer/railtie.rb".freeze, "lib/action_mailer/rescuable.rb".freeze, "lib/action_mailer/test_case.rb".freeze, "lib/action_mailer/test_helper.rb".freeze, "lib/action_mailer/version.rb".freeze, "lib/rails/generators/mailer/USAGE".freeze, "lib/rails/generators/mailer/mailer_generator.rb".freeze, "lib/rails/generators/mailer/templates/application_mailer.rb".freeze, "lib/rails/generators/mailer/templates/mailer.rb".freeze]
|
||||||
|
s.homepage = "http://rubyonrails.org".freeze
|
||||||
|
s.licenses = ["MIT".freeze]
|
||||||
|
s.required_ruby_version = Gem::Requirement.new(">= 2.2.2".freeze)
|
||||||
|
s.requirements = ["none".freeze]
|
||||||
|
s.rubygems_version = "2.7.6".freeze
|
||||||
|
s.summary = "Email composition, delivery, and receiving framework (part of Rails).".freeze
|
||||||
|
|
||||||
|
if s.respond_to? :specification_version then
|
||||||
|
s.specification_version = 4
|
||||||
|
|
||||||
|
if Gem::Version.new(Gem::VERSION) >= Gem::Version.new('1.2.0') then
|
||||||
|
s.add_runtime_dependency(%q<actionpack>.freeze, ["= 5.1.6.1"])
|
||||||
|
s.add_runtime_dependency(%q<actionview>.freeze, ["= 5.1.6.1"])
|
||||||
|
s.add_runtime_dependency(%q<activejob>.freeze, ["= 5.1.6.1"])
|
||||||
|
s.add_runtime_dependency(%q<mail>.freeze, [">= 2.5.4", "~> 2.5"])
|
||||||
|
s.add_runtime_dependency(%q<rails-dom-testing>.freeze, ["~> 2.0"])
|
||||||
|
else
|
||||||
|
s.add_dependency(%q<actionpack>.freeze, ["= 5.1.6.1"])
|
||||||
|
s.add_dependency(%q<actionview>.freeze, ["= 5.1.6.1"])
|
||||||
|
s.add_dependency(%q<activejob>.freeze, ["= 5.1.6.1"])
|
||||||
|
s.add_dependency(%q<mail>.freeze, [">= 2.5.4", "~> 2.5"])
|
||||||
|
s.add_dependency(%q<rails-dom-testing>.freeze, ["~> 2.0"])
|
||||||
|
end
|
||||||
|
else
|
||||||
|
s.add_dependency(%q<actionpack>.freeze, ["= 5.1.6.1"])
|
||||||
|
s.add_dependency(%q<actionview>.freeze, ["= 5.1.6.1"])
|
||||||
|
s.add_dependency(%q<activejob>.freeze, ["= 5.1.6.1"])
|
||||||
|
s.add_dependency(%q<mail>.freeze, [">= 2.5.4", "~> 2.5"])
|
||||||
|
s.add_dependency(%q<rails-dom-testing>.freeze, ["~> 2.0"])
|
||||||
|
end
|
||||||
|
end
|
60
debian/gems-compat/actionmailer-5.1.6.1/lib/action_mailer.rb
vendored
Normal file
60
debian/gems-compat/actionmailer-5.1.6.1/lib/action_mailer.rb
vendored
Normal file
|
@ -0,0 +1,60 @@
|
||||||
|
#--
|
||||||
|
# Copyright (c) 2004-2017 David Heinemeier Hansson
|
||||||
|
#
|
||||||
|
# Permission is hereby granted, free of charge, to any person obtaining
|
||||||
|
# a copy of this software and associated documentation files (the
|
||||||
|
# "Software"), to deal in the Software without restriction, including
|
||||||
|
# without limitation the rights to use, copy, modify, merge, publish,
|
||||||
|
# distribute, sublicense, and/or sell copies of the Software, and to
|
||||||
|
# permit persons to whom the Software is furnished to do so, subject to
|
||||||
|
# the following conditions:
|
||||||
|
#
|
||||||
|
# The above copyright notice and this permission notice shall be
|
||||||
|
# included in all copies or substantial portions of the Software.
|
||||||
|
#
|
||||||
|
# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
|
||||||
|
# EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
|
||||||
|
# MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
|
||||||
|
# NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
|
||||||
|
# LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
|
||||||
|
# OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
|
||||||
|
# WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
||||||
|
#++
|
||||||
|
|
||||||
|
require "abstract_controller"
|
||||||
|
require "action_mailer/version"
|
||||||
|
|
||||||
|
# Common Active Support usage in Action Mailer
|
||||||
|
require "active_support"
|
||||||
|
require "active_support/rails"
|
||||||
|
require "active_support/core_ext/class"
|
||||||
|
require "active_support/core_ext/module/attr_internal"
|
||||||
|
require "active_support/core_ext/string/inflections"
|
||||||
|
require "active_support/lazy_load_hooks"
|
||||||
|
|
||||||
|
module ActionMailer
|
||||||
|
extend ::ActiveSupport::Autoload
|
||||||
|
|
||||||
|
eager_autoload do
|
||||||
|
autoload :Collector
|
||||||
|
end
|
||||||
|
|
||||||
|
autoload :Base
|
||||||
|
autoload :DeliveryMethods
|
||||||
|
autoload :InlinePreviewInterceptor
|
||||||
|
autoload :MailHelper
|
||||||
|
autoload :Parameterized
|
||||||
|
autoload :Preview
|
||||||
|
autoload :Previews, "action_mailer/preview"
|
||||||
|
autoload :TestCase
|
||||||
|
autoload :TestHelper
|
||||||
|
autoload :MessageDelivery
|
||||||
|
autoload :DeliveryJob
|
||||||
|
end
|
||||||
|
|
||||||
|
autoload :Mime, "action_dispatch/http/mime_type"
|
||||||
|
|
||||||
|
ActiveSupport.on_load(:action_view) do
|
||||||
|
ActionView::Base.default_formats ||= Mime::SET.symbols
|
||||||
|
ActionView::Template::Types.delegate_to Mime
|
||||||
|
end
|
993
debian/gems-compat/actionmailer-5.1.6.1/lib/action_mailer/base.rb
vendored
Normal file
993
debian/gems-compat/actionmailer-5.1.6.1/lib/action_mailer/base.rb
vendored
Normal file
|
@ -0,0 +1,993 @@
|
||||||
|
require "mail"
|
||||||
|
require "action_mailer/collector"
|
||||||
|
require "active_support/core_ext/string/inflections"
|
||||||
|
require "active_support/core_ext/hash/except"
|
||||||
|
require "active_support/core_ext/module/anonymous"
|
||||||
|
|
||||||
|
require "action_mailer/log_subscriber"
|
||||||
|
require "action_mailer/rescuable"
|
||||||
|
|
||||||
|
module ActionMailer
|
||||||
|
# Action Mailer allows you to send email from your application using a mailer model and views.
|
||||||
|
#
|
||||||
|
# = Mailer Models
|
||||||
|
#
|
||||||
|
# To use Action Mailer, you need to create a mailer model.
|
||||||
|
#
|
||||||
|
# $ rails generate mailer Notifier
|
||||||
|
#
|
||||||
|
# The generated model inherits from <tt>ApplicationMailer</tt> which in turn
|
||||||
|
# inherits from <tt>ActionMailer::Base</tt>. A mailer model defines methods
|
||||||
|
# used to generate an email message. In these methods, you can setup variables to be used in
|
||||||
|
# the mailer views, options on the mail itself such as the <tt>:from</tt> address, and attachments.
|
||||||
|
#
|
||||||
|
# class ApplicationMailer < ActionMailer::Base
|
||||||
|
# default from: 'from@example.com'
|
||||||
|
# layout 'mailer'
|
||||||
|
# end
|
||||||
|
#
|
||||||
|
# class NotifierMailer < ApplicationMailer
|
||||||
|
# default from: 'no-reply@example.com',
|
||||||
|
# return_path: 'system@example.com'
|
||||||
|
#
|
||||||
|
# def welcome(recipient)
|
||||||
|
# @account = recipient
|
||||||
|
# mail(to: recipient.email_address_with_name,
|
||||||
|
# bcc: ["bcc@example.com", "Order Watcher <watcher@example.com>"])
|
||||||
|
# end
|
||||||
|
# end
|
||||||
|
#
|
||||||
|
# Within the mailer method, you have access to the following methods:
|
||||||
|
#
|
||||||
|
# * <tt>attachments[]=</tt> - Allows you to add attachments to your email in an intuitive
|
||||||
|
# manner; <tt>attachments['filename.png'] = File.read('path/to/filename.png')</tt>
|
||||||
|
#
|
||||||
|
# * <tt>attachments.inline[]=</tt> - Allows you to add an inline attachment to your email
|
||||||
|
# in the same manner as <tt>attachments[]=</tt>
|
||||||
|
#
|
||||||
|
# * <tt>headers[]=</tt> - Allows you to specify any header field in your email such
|
||||||
|
# as <tt>headers['X-No-Spam'] = 'True'</tt>. Note that declaring a header multiple times
|
||||||
|
# will add many fields of the same name. Read #headers doc for more information.
|
||||||
|
#
|
||||||
|
# * <tt>headers(hash)</tt> - Allows you to specify multiple headers in your email such
|
||||||
|
# as <tt>headers({'X-No-Spam' => 'True', 'In-Reply-To' => '1234@message.id'})</tt>
|
||||||
|
#
|
||||||
|
# * <tt>mail</tt> - Allows you to specify email to be sent.
|
||||||
|
#
|
||||||
|
# The hash passed to the mail method allows you to specify any header that a <tt>Mail::Message</tt>
|
||||||
|
# will accept (any valid email header including optional fields).
|
||||||
|
#
|
||||||
|
# The mail method, if not passed a block, will inspect your views and send all the views with
|
||||||
|
# the same name as the method, so the above action would send the +welcome.text.erb+ view
|
||||||
|
# file as well as the +welcome.html.erb+ view file in a +multipart/alternative+ email.
|
||||||
|
#
|
||||||
|
# If you want to explicitly render only certain templates, pass a block:
|
||||||
|
#
|
||||||
|
# mail(to: user.email) do |format|
|
||||||
|
# format.text
|
||||||
|
# format.html
|
||||||
|
# end
|
||||||
|
#
|
||||||
|
# The block syntax is also useful in providing information specific to a part:
|
||||||
|
#
|
||||||
|
# mail(to: user.email) do |format|
|
||||||
|
# format.text(content_transfer_encoding: "base64")
|
||||||
|
# format.html
|
||||||
|
# end
|
||||||
|
#
|
||||||
|
# Or even to render a special view:
|
||||||
|
#
|
||||||
|
# mail(to: user.email) do |format|
|
||||||
|
# format.text
|
||||||
|
# format.html { render "some_other_template" }
|
||||||
|
# end
|
||||||
|
#
|
||||||
|
# = Mailer views
|
||||||
|
#
|
||||||
|
# Like Action Controller, each mailer class has a corresponding view directory in which each
|
||||||
|
# method of the class looks for a template with its name.
|
||||||
|
#
|
||||||
|
# To define a template to be used with a mailer, create an <tt>.erb</tt> file with the same
|
||||||
|
# name as the method in your mailer model. For example, in the mailer defined above, the template at
|
||||||
|
# <tt>app/views/notifier_mailer/welcome.text.erb</tt> would be used to generate the email.
|
||||||
|
#
|
||||||
|
# Variables defined in the methods of your mailer model are accessible as instance variables in their
|
||||||
|
# corresponding view.
|
||||||
|
#
|
||||||
|
# Emails by default are sent in plain text, so a sample view for our model example might look like this:
|
||||||
|
#
|
||||||
|
# Hi <%= @account.name %>,
|
||||||
|
# Thanks for joining our service! Please check back often.
|
||||||
|
#
|
||||||
|
# You can even use Action View helpers in these views. For example:
|
||||||
|
#
|
||||||
|
# You got a new note!
|
||||||
|
# <%= truncate(@note.body, length: 25) %>
|
||||||
|
#
|
||||||
|
# If you need to access the subject, from or the recipients in the view, you can do that through message object:
|
||||||
|
#
|
||||||
|
# You got a new note from <%= message.from %>!
|
||||||
|
# <%= truncate(@note.body, length: 25) %>
|
||||||
|
#
|
||||||
|
#
|
||||||
|
# = Generating URLs
|
||||||
|
#
|
||||||
|
# URLs can be generated in mailer views using <tt>url_for</tt> or named routes. Unlike controllers from
|
||||||
|
# Action Pack, the mailer instance doesn't have any context about the incoming request, so you'll need
|
||||||
|
# to provide all of the details needed to generate a URL.
|
||||||
|
#
|
||||||
|
# When using <tt>url_for</tt> you'll need to provide the <tt>:host</tt>, <tt>:controller</tt>, and <tt>:action</tt>:
|
||||||
|
#
|
||||||
|
# <%= url_for(host: "example.com", controller: "welcome", action: "greeting") %>
|
||||||
|
#
|
||||||
|
# When using named routes you only need to supply the <tt>:host</tt>:
|
||||||
|
#
|
||||||
|
# <%= users_url(host: "example.com") %>
|
||||||
|
#
|
||||||
|
# You should use the <tt>named_route_url</tt> style (which generates absolute URLs) and avoid using the
|
||||||
|
# <tt>named_route_path</tt> style (which generates relative URLs), since clients reading the mail will
|
||||||
|
# have no concept of a current URL from which to determine a relative path.
|
||||||
|
#
|
||||||
|
# It is also possible to set a default host that will be used in all mailers by setting the <tt>:host</tt>
|
||||||
|
# option as a configuration option in <tt>config/application.rb</tt>:
|
||||||
|
#
|
||||||
|
# config.action_mailer.default_url_options = { host: "example.com" }
|
||||||
|
#
|
||||||
|
# You can also define a <tt>default_url_options</tt> method on individual mailers to override these
|
||||||
|
# default settings per-mailer.
|
||||||
|
#
|
||||||
|
# By default when <tt>config.force_ssl</tt> is true, URLs generated for hosts will use the HTTPS protocol.
|
||||||
|
#
|
||||||
|
# = Sending mail
|
||||||
|
#
|
||||||
|
# Once a mailer action and template are defined, you can deliver your message or defer its creation and
|
||||||
|
# delivery for later:
|
||||||
|
#
|
||||||
|
# NotifierMailer.welcome(User.first).deliver_now # sends the email
|
||||||
|
# mail = NotifierMailer.welcome(User.first) # => an ActionMailer::MessageDelivery object
|
||||||
|
# mail.deliver_now # generates and sends the email now
|
||||||
|
#
|
||||||
|
# The <tt>ActionMailer::MessageDelivery</tt> class is a wrapper around a delegate that will call
|
||||||
|
# your method to generate the mail. If you want direct access to the delegator, or <tt>Mail::Message</tt>,
|
||||||
|
# you can call the <tt>message</tt> method on the <tt>ActionMailer::MessageDelivery</tt> object.
|
||||||
|
#
|
||||||
|
# NotifierMailer.welcome(User.first).message # => a Mail::Message object
|
||||||
|
#
|
||||||
|
# Action Mailer is nicely integrated with Active Job so you can generate and send emails in the background
|
||||||
|
# (example: outside of the request-response cycle, so the user doesn't have to wait on it):
|
||||||
|
#
|
||||||
|
# NotifierMailer.welcome(User.first).deliver_later # enqueue the email sending to Active Job
|
||||||
|
#
|
||||||
|
# Note that <tt>deliver_later</tt> will execute your method from the background job.
|
||||||
|
#
|
||||||
|
# You never instantiate your mailer class. Rather, you just call the method you defined on the class itself.
|
||||||
|
# All instance methods are expected to return a message object to be sent.
|
||||||
|
#
|
||||||
|
# = Multipart Emails
|
||||||
|
#
|
||||||
|
# Multipart messages can also be used implicitly because Action Mailer will automatically detect and use
|
||||||
|
# multipart templates, where each template is named after the name of the action, followed by the content
|
||||||
|
# type. Each such detected template will be added to the message, as a separate part.
|
||||||
|
#
|
||||||
|
# For example, if the following templates exist:
|
||||||
|
# * signup_notification.text.erb
|
||||||
|
# * signup_notification.html.erb
|
||||||
|
# * signup_notification.xml.builder
|
||||||
|
# * signup_notification.yml.erb
|
||||||
|
#
|
||||||
|
# Each would be rendered and added as a separate part to the message, with the corresponding content
|
||||||
|
# type. The content type for the entire message is automatically set to <tt>multipart/alternative</tt>,
|
||||||
|
# which indicates that the email contains multiple different representations of the same email
|
||||||
|
# body. The same instance variables defined in the action are passed to all email templates.
|
||||||
|
#
|
||||||
|
# Implicit template rendering is not performed if any attachments or parts have been added to the email.
|
||||||
|
# This means that you'll have to manually add each part to the email and set the content type of the email
|
||||||
|
# to <tt>multipart/alternative</tt>.
|
||||||
|
#
|
||||||
|
# = Attachments
|
||||||
|
#
|
||||||
|
# Sending attachment in emails is easy:
|
||||||
|
#
|
||||||
|
# class NotifierMailer < ApplicationMailer
|
||||||
|
# def welcome(recipient)
|
||||||
|
# attachments['free_book.pdf'] = File.read('path/to/file.pdf')
|
||||||
|
# mail(to: recipient, subject: "New account information")
|
||||||
|
# end
|
||||||
|
# end
|
||||||
|
#
|
||||||
|
# Which will (if it had both a <tt>welcome.text.erb</tt> and <tt>welcome.html.erb</tt>
|
||||||
|
# template in the view directory), send a complete <tt>multipart/mixed</tt> email with two parts,
|
||||||
|
# the first part being a <tt>multipart/alternative</tt> with the text and HTML email parts inside,
|
||||||
|
# and the second being a <tt>application/pdf</tt> with a Base64 encoded copy of the file.pdf book
|
||||||
|
# with the filename +free_book.pdf+.
|
||||||
|
#
|
||||||
|
# If you need to send attachments with no content, you need to create an empty view for it,
|
||||||
|
# or add an empty body parameter like this:
|
||||||
|
#
|
||||||
|
# class NotifierMailer < ApplicationMailer
|
||||||
|
# def welcome(recipient)
|
||||||
|
# attachments['free_book.pdf'] = File.read('path/to/file.pdf')
|
||||||
|
# mail(to: recipient, subject: "New account information", body: "")
|
||||||
|
# end
|
||||||
|
# end
|
||||||
|
#
|
||||||
|
# You can also send attachments with html template, in this case you need to add body, attachments,
|
||||||
|
# and custom content type like this:
|
||||||
|
#
|
||||||
|
# class NotifierMailer < ApplicationMailer
|
||||||
|
# def welcome(recipient)
|
||||||
|
# attachments["free_book.pdf"] = File.read("path/to/file.pdf")
|
||||||
|
# mail(to: recipient,
|
||||||
|
# subject: "New account information",
|
||||||
|
# content_type: "text/html",
|
||||||
|
# body: "<html><body>Hello there</body></html>")
|
||||||
|
# end
|
||||||
|
# end
|
||||||
|
#
|
||||||
|
# = Inline Attachments
|
||||||
|
#
|
||||||
|
# You can also specify that a file should be displayed inline with other HTML. This is useful
|
||||||
|
# if you want to display a corporate logo or a photo.
|
||||||
|
#
|
||||||
|
# class NotifierMailer < ApplicationMailer
|
||||||
|
# def welcome(recipient)
|
||||||
|
# attachments.inline['photo.png'] = File.read('path/to/photo.png')
|
||||||
|
# mail(to: recipient, subject: "Here is what we look like")
|
||||||
|
# end
|
||||||
|
# end
|
||||||
|
#
|
||||||
|
# And then to reference the image in the view, you create a <tt>welcome.html.erb</tt> file and
|
||||||
|
# make a call to +image_tag+ passing in the attachment you want to display and then call
|
||||||
|
# +url+ on the attachment to get the relative content id path for the image source:
|
||||||
|
#
|
||||||
|
# <h1>Please Don't Cringe</h1>
|
||||||
|
#
|
||||||
|
# <%= image_tag attachments['photo.png'].url -%>
|
||||||
|
#
|
||||||
|
# As we are using Action View's +image_tag+ method, you can pass in any other options you want:
|
||||||
|
#
|
||||||
|
# <h1>Please Don't Cringe</h1>
|
||||||
|
#
|
||||||
|
# <%= image_tag attachments['photo.png'].url, alt: 'Our Photo', class: 'photo' -%>
|
||||||
|
#
|
||||||
|
# = Observing and Intercepting Mails
|
||||||
|
#
|
||||||
|
# Action Mailer provides hooks into the Mail observer and interceptor methods. These allow you to
|
||||||
|
# register classes that are called during the mail delivery life cycle.
|
||||||
|
#
|
||||||
|
# An observer class must implement the <tt>:delivered_email(message)</tt> method which will be
|
||||||
|
# called once for every email sent after the email has been sent.
|
||||||
|
#
|
||||||
|
# An interceptor class must implement the <tt>:delivering_email(message)</tt> method which will be
|
||||||
|
# called before the email is sent, allowing you to make modifications to the email before it hits
|
||||||
|
# the delivery agents. Your class should make any needed modifications directly to the passed
|
||||||
|
# in <tt>Mail::Message</tt> instance.
|
||||||
|
#
|
||||||
|
# = Default Hash
|
||||||
|
#
|
||||||
|
# Action Mailer provides some intelligent defaults for your emails, these are usually specified in a
|
||||||
|
# default method inside the class definition:
|
||||||
|
#
|
||||||
|
# class NotifierMailer < ApplicationMailer
|
||||||
|
# default sender: 'system@example.com'
|
||||||
|
# end
|
||||||
|
#
|
||||||
|
# You can pass in any header value that a <tt>Mail::Message</tt> accepts. Out of the box,
|
||||||
|
# <tt>ActionMailer::Base</tt> sets the following:
|
||||||
|
#
|
||||||
|
# * <tt>mime_version: "1.0"</tt>
|
||||||
|
# * <tt>charset: "UTF-8"</tt>
|
||||||
|
# * <tt>content_type: "text/plain"</tt>
|
||||||
|
# * <tt>parts_order: [ "text/plain", "text/enriched", "text/html" ]</tt>
|
||||||
|
#
|
||||||
|
# <tt>parts_order</tt> and <tt>charset</tt> are not actually valid <tt>Mail::Message</tt> header fields,
|
||||||
|
# but Action Mailer translates them appropriately and sets the correct values.
|
||||||
|
#
|
||||||
|
# As you can pass in any header, you need to either quote the header as a string, or pass it in as
|
||||||
|
# an underscored symbol, so the following will work:
|
||||||
|
#
|
||||||
|
# class NotifierMailer < ApplicationMailer
|
||||||
|
# default 'Content-Transfer-Encoding' => '7bit',
|
||||||
|
# content_description: 'This is a description'
|
||||||
|
# end
|
||||||
|
#
|
||||||
|
# Finally, Action Mailer also supports passing <tt>Proc</tt> and <tt>Lambda</tt> objects into the default hash,
|
||||||
|
# so you can define methods that evaluate as the message is being generated:
|
||||||
|
#
|
||||||
|
# class NotifierMailer < ApplicationMailer
|
||||||
|
# default 'X-Special-Header' => Proc.new { my_method }, to: -> { @inviter.email_address }
|
||||||
|
#
|
||||||
|
# private
|
||||||
|
# def my_method
|
||||||
|
# 'some complex call'
|
||||||
|
# end
|
||||||
|
# end
|
||||||
|
#
|
||||||
|
# Note that the proc/lambda is evaluated right at the start of the mail message generation, so if you
|
||||||
|
# set something in the default hash using a proc, and then set the same thing inside of your
|
||||||
|
# mailer method, it will get overwritten by the mailer method.
|
||||||
|
#
|
||||||
|
# It is also possible to set these default options that will be used in all mailers through
|
||||||
|
# the <tt>default_options=</tt> configuration in <tt>config/application.rb</tt>:
|
||||||
|
#
|
||||||
|
# config.action_mailer.default_options = { from: "no-reply@example.org" }
|
||||||
|
#
|
||||||
|
# = Callbacks
|
||||||
|
#
|
||||||
|
# You can specify callbacks using before_action and after_action for configuring your messages.
|
||||||
|
# This may be useful, for example, when you want to add default inline attachments for all
|
||||||
|
# messages sent out by a certain mailer class:
|
||||||
|
#
|
||||||
|
# class NotifierMailer < ApplicationMailer
|
||||||
|
# before_action :add_inline_attachment!
|
||||||
|
#
|
||||||
|
# def welcome
|
||||||
|
# mail
|
||||||
|
# end
|
||||||
|
#
|
||||||
|
# private
|
||||||
|
# def add_inline_attachment!
|
||||||
|
# attachments.inline["footer.jpg"] = File.read('/path/to/filename.jpg')
|
||||||
|
# end
|
||||||
|
# end
|
||||||
|
#
|
||||||
|
# Callbacks in Action Mailer are implemented using
|
||||||
|
# <tt>AbstractController::Callbacks</tt>, so you can define and configure
|
||||||
|
# callbacks in the same manner that you would use callbacks in classes that
|
||||||
|
# inherit from <tt>ActionController::Base</tt>.
|
||||||
|
#
|
||||||
|
# Note that unless you have a specific reason to do so, you should prefer
|
||||||
|
# using <tt>before_action</tt> rather than <tt>after_action</tt> in your
|
||||||
|
# Action Mailer classes so that headers are parsed properly.
|
||||||
|
#
|
||||||
|
# = Previewing emails
|
||||||
|
#
|
||||||
|
# You can preview your email templates visually by adding a mailer preview file to the
|
||||||
|
# <tt>ActionMailer::Base.preview_path</tt>. Since most emails do something interesting
|
||||||
|
# with database data, you'll need to write some scenarios to load messages with fake data:
|
||||||
|
#
|
||||||
|
# class NotifierMailerPreview < ActionMailer::Preview
|
||||||
|
# def welcome
|
||||||
|
# NotifierMailer.welcome(User.first)
|
||||||
|
# end
|
||||||
|
# end
|
||||||
|
#
|
||||||
|
# Methods must return a <tt>Mail::Message</tt> object which can be generated by calling the mailer
|
||||||
|
# method without the additional <tt>deliver_now</tt> / <tt>deliver_later</tt>. The location of the
|
||||||
|
# mailer previews directory can be configured using the <tt>preview_path</tt> option which has a default
|
||||||
|
# of <tt>test/mailers/previews</tt>:
|
||||||
|
#
|
||||||
|
# config.action_mailer.preview_path = "#{Rails.root}/lib/mailer_previews"
|
||||||
|
#
|
||||||
|
# An overview of all previews is accessible at <tt>http://localhost:3000/rails/mailers</tt>
|
||||||
|
# on a running development server instance.
|
||||||
|
#
|
||||||
|
# Previews can also be intercepted in a similar manner as deliveries can be by registering
|
||||||
|
# a preview interceptor that has a <tt>previewing_email</tt> method:
|
||||||
|
#
|
||||||
|
# class CssInlineStyler
|
||||||
|
# def self.previewing_email(message)
|
||||||
|
# # inline CSS styles
|
||||||
|
# end
|
||||||
|
# end
|
||||||
|
#
|
||||||
|
# config.action_mailer.preview_interceptors :css_inline_styler
|
||||||
|
#
|
||||||
|
# Note that interceptors need to be registered both with <tt>register_interceptor</tt>
|
||||||
|
# and <tt>register_preview_interceptor</tt> if they should operate on both sending and
|
||||||
|
# previewing emails.
|
||||||
|
#
|
||||||
|
# = Configuration options
|
||||||
|
#
|
||||||
|
# These options are specified on the class level, like
|
||||||
|
# <tt>ActionMailer::Base.raise_delivery_errors = true</tt>
|
||||||
|
#
|
||||||
|
# * <tt>default_options</tt> - You can pass this in at a class level as well as within the class itself as
|
||||||
|
# per the above section.
|
||||||
|
#
|
||||||
|
# * <tt>logger</tt> - the logger is used for generating information on the mailing run if available.
|
||||||
|
# Can be set to +nil+ for no logging. Compatible with both Ruby's own +Logger+ and Log4r loggers.
|
||||||
|
#
|
||||||
|
# * <tt>smtp_settings</tt> - Allows detailed configuration for <tt>:smtp</tt> delivery method:
|
||||||
|
# * <tt>:address</tt> - Allows you to use a remote mail server. Just change it from its default
|
||||||
|
# "localhost" setting.
|
||||||
|
# * <tt>:port</tt> - On the off chance that your mail server doesn't run on port 25, you can change it.
|
||||||
|
# * <tt>:domain</tt> - If you need to specify a HELO domain, you can do it here.
|
||||||
|
# * <tt>:user_name</tt> - If your mail server requires authentication, set the username in this setting.
|
||||||
|
# * <tt>:password</tt> - If your mail server requires authentication, set the password in this setting.
|
||||||
|
# * <tt>:authentication</tt> - If your mail server requires authentication, you need to specify the
|
||||||
|
# authentication type here.
|
||||||
|
# This is a symbol and one of <tt>:plain</tt> (will send the password Base64 encoded), <tt>:login</tt> (will
|
||||||
|
# send the password Base64 encoded) or <tt>:cram_md5</tt> (combines a Challenge/Response mechanism to exchange
|
||||||
|
# information and a cryptographic Message Digest 5 algorithm to hash important information)
|
||||||
|
# * <tt>:enable_starttls_auto</tt> - Detects if STARTTLS is enabled in your SMTP server and starts
|
||||||
|
# to use it. Defaults to <tt>true</tt>.
|
||||||
|
# * <tt>:openssl_verify_mode</tt> - When using TLS, you can set how OpenSSL checks the certificate. This is
|
||||||
|
# really useful if you need to validate a self-signed and/or a wildcard certificate. You can use the name
|
||||||
|
# of an OpenSSL verify constant (<tt>'none'</tt> or <tt>'peer'</tt>) or directly the constant
|
||||||
|
# (<tt>OpenSSL::SSL::VERIFY_NONE</tt> or <tt>OpenSSL::SSL::VERIFY_PEER</tt>).
|
||||||
|
# <tt>:ssl/:tls</tt> Enables the SMTP connection to use SMTP/TLS (SMTPS: SMTP over direct TLS connection)
|
||||||
|
#
|
||||||
|
# * <tt>sendmail_settings</tt> - Allows you to override options for the <tt>:sendmail</tt> delivery method.
|
||||||
|
# * <tt>:location</tt> - The location of the sendmail executable. Defaults to <tt>/usr/sbin/sendmail</tt>.
|
||||||
|
# * <tt>:arguments</tt> - The command line arguments. Defaults to <tt>-i</tt> with <tt>-f sender@address</tt>
|
||||||
|
# added automatically before the message is sent.
|
||||||
|
#
|
||||||
|
# * <tt>file_settings</tt> - Allows you to override options for the <tt>:file</tt> delivery method.
|
||||||
|
# * <tt>:location</tt> - The directory into which emails will be written. Defaults to the application
|
||||||
|
# <tt>tmp/mails</tt>.
|
||||||
|
#
|
||||||
|
# * <tt>raise_delivery_errors</tt> - Whether or not errors should be raised if the email fails to be delivered.
|
||||||
|
#
|
||||||
|
# * <tt>delivery_method</tt> - Defines a delivery method. Possible values are <tt>:smtp</tt> (default),
|
||||||
|
# <tt>:sendmail</tt>, <tt>:test</tt>, and <tt>:file</tt>. Or you may provide a custom delivery method
|
||||||
|
# object e.g. +MyOwnDeliveryMethodClass+. See the Mail gem documentation on the interface you need to
|
||||||
|
# implement for a custom delivery agent.
|
||||||
|
#
|
||||||
|
# * <tt>perform_deliveries</tt> - Determines whether emails are actually sent from Action Mailer when you
|
||||||
|
# call <tt>.deliver</tt> on an email message or on an Action Mailer method. This is on by default but can
|
||||||
|
# be turned off to aid in functional testing.
|
||||||
|
#
|
||||||
|
# * <tt>deliveries</tt> - Keeps an array of all the emails sent out through the Action Mailer with
|
||||||
|
# <tt>delivery_method :test</tt>. Most useful for unit and functional testing.
|
||||||
|
#
|
||||||
|
# * <tt>deliver_later_queue_name</tt> - The name of the queue used with <tt>deliver_later</tt>. Defaults to +mailers+.
|
||||||
|
class Base < AbstractController::Base
|
||||||
|
include DeliveryMethods
|
||||||
|
include Rescuable
|
||||||
|
include Parameterized
|
||||||
|
include Previews
|
||||||
|
|
||||||
|
abstract!
|
||||||
|
|
||||||
|
include AbstractController::Rendering
|
||||||
|
|
||||||
|
include AbstractController::Logger
|
||||||
|
include AbstractController::Helpers
|
||||||
|
include AbstractController::Translation
|
||||||
|
include AbstractController::AssetPaths
|
||||||
|
include AbstractController::Callbacks
|
||||||
|
include AbstractController::Caching
|
||||||
|
|
||||||
|
include ActionView::Layouts
|
||||||
|
|
||||||
|
PROTECTED_IVARS = AbstractController::Rendering::DEFAULT_PROTECTED_INSTANCE_VARIABLES + [:@_action_has_layout]
|
||||||
|
|
||||||
|
def _protected_ivars # :nodoc:
|
||||||
|
PROTECTED_IVARS
|
||||||
|
end
|
||||||
|
|
||||||
|
helper ActionMailer::MailHelper
|
||||||
|
|
||||||
|
class_attribute :default_params
|
||||||
|
self.default_params = {
|
||||||
|
mime_version: "1.0",
|
||||||
|
charset: "UTF-8",
|
||||||
|
content_type: "text/plain",
|
||||||
|
parts_order: [ "text/plain", "text/enriched", "text/html" ]
|
||||||
|
}.freeze
|
||||||
|
|
||||||
|
class << self
|
||||||
|
# Register one or more Observers which will be notified when mail is delivered.
|
||||||
|
def register_observers(*observers)
|
||||||
|
observers.flatten.compact.each { |observer| register_observer(observer) }
|
||||||
|
end
|
||||||
|
|
||||||
|
# Register one or more Interceptors which will be called before mail is sent.
|
||||||
|
def register_interceptors(*interceptors)
|
||||||
|
interceptors.flatten.compact.each { |interceptor| register_interceptor(interceptor) }
|
||||||
|
end
|
||||||
|
|
||||||
|
# Register an Observer which will be notified when mail is delivered.
|
||||||
|
# Either a class, string or symbol can be passed in as the Observer.
|
||||||
|
# If a string or symbol is passed in it will be camelized and constantized.
|
||||||
|
def register_observer(observer)
|
||||||
|
Mail.register_observer(observer_class_for(observer))
|
||||||
|
end
|
||||||
|
|
||||||
|
# Register an Interceptor which will be called before mail is sent.
|
||||||
|
# Either a class, string or symbol can be passed in as the Interceptor.
|
||||||
|
# If a string or symbol is passed in it will be camelized and constantized.
|
||||||
|
def register_interceptor(interceptor)
|
||||||
|
Mail.register_interceptor(observer_class_for(interceptor))
|
||||||
|
end
|
||||||
|
|
||||||
|
def observer_class_for(value) # :nodoc:
|
||||||
|
case value
|
||||||
|
when String, Symbol
|
||||||
|
value.to_s.camelize.constantize
|
||||||
|
else
|
||||||
|
value
|
||||||
|
end
|
||||||
|
end
|
||||||
|
private :observer_class_for
|
||||||
|
|
||||||
|
# Returns the name of the current mailer. This method is also being used as a path for a view lookup.
|
||||||
|
# If this is an anonymous mailer, this method will return +anonymous+ instead.
|
||||||
|
def mailer_name
|
||||||
|
@mailer_name ||= anonymous? ? "anonymous" : name.underscore
|
||||||
|
end
|
||||||
|
# Allows to set the name of current mailer.
|
||||||
|
attr_writer :mailer_name
|
||||||
|
alias :controller_path :mailer_name
|
||||||
|
|
||||||
|
# Sets the defaults through app configuration:
|
||||||
|
#
|
||||||
|
# config.action_mailer.default(from: "no-reply@example.org")
|
||||||
|
#
|
||||||
|
# Aliased by ::default_options=
|
||||||
|
def default(value = nil)
|
||||||
|
self.default_params = default_params.merge(value).freeze if value
|
||||||
|
default_params
|
||||||
|
end
|
||||||
|
# Allows to set defaults through app configuration:
|
||||||
|
#
|
||||||
|
# config.action_mailer.default_options = { from: "no-reply@example.org" }
|
||||||
|
alias :default_options= :default
|
||||||
|
|
||||||
|
# Receives a raw email, parses it into an email object, decodes it,
|
||||||
|
# instantiates a new mailer, and passes the email object to the mailer
|
||||||
|
# object's +receive+ method.
|
||||||
|
#
|
||||||
|
# If you want your mailer to be able to process incoming messages, you'll
|
||||||
|
# need to implement a +receive+ method that accepts the raw email string
|
||||||
|
# as a parameter:
|
||||||
|
#
|
||||||
|
# class MyMailer < ActionMailer::Base
|
||||||
|
# def receive(mail)
|
||||||
|
# # ...
|
||||||
|
# end
|
||||||
|
# end
|
||||||
|
def receive(raw_mail)
|
||||||
|
ActiveSupport::Notifications.instrument("receive.action_mailer") do |payload|
|
||||||
|
mail = Mail.new(raw_mail)
|
||||||
|
set_payload_for_mail(payload, mail)
|
||||||
|
new.receive(mail)
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
# Wraps an email delivery inside of <tt>ActiveSupport::Notifications</tt> instrumentation.
|
||||||
|
#
|
||||||
|
# This method is actually called by the <tt>Mail::Message</tt> object itself
|
||||||
|
# through a callback when you call <tt>:deliver</tt> on the <tt>Mail::Message</tt>,
|
||||||
|
# calling +deliver_mail+ directly and passing a <tt>Mail::Message</tt> will do
|
||||||
|
# nothing except tell the logger you sent the email.
|
||||||
|
def deliver_mail(mail) #:nodoc:
|
||||||
|
ActiveSupport::Notifications.instrument("deliver.action_mailer") do |payload|
|
||||||
|
set_payload_for_mail(payload, mail)
|
||||||
|
yield # Let Mail do the delivery actions
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
private
|
||||||
|
|
||||||
|
def set_payload_for_mail(payload, mail)
|
||||||
|
payload[:mailer] = name
|
||||||
|
payload[:message_id] = mail.message_id
|
||||||
|
payload[:subject] = mail.subject
|
||||||
|
payload[:to] = mail.to
|
||||||
|
payload[:from] = mail.from
|
||||||
|
payload[:bcc] = mail.bcc if mail.bcc.present?
|
||||||
|
payload[:cc] = mail.cc if mail.cc.present?
|
||||||
|
payload[:date] = mail.date
|
||||||
|
payload[:mail] = mail.encoded
|
||||||
|
end
|
||||||
|
|
||||||
|
def method_missing(method_name, *args)
|
||||||
|
if action_methods.include?(method_name.to_s)
|
||||||
|
MessageDelivery.new(self, method_name, *args)
|
||||||
|
else
|
||||||
|
super
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
def respond_to_missing?(method, include_all = false)
|
||||||
|
action_methods.include?(method.to_s) || super
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
attr_internal :message
|
||||||
|
|
||||||
|
# Instantiate a new mailer object. If +method_name+ is not +nil+, the mailer
|
||||||
|
# will be initialized according to the named method. If not, the mailer will
|
||||||
|
# remain uninitialized (useful when you only need to invoke the "receive"
|
||||||
|
# method, for instance).
|
||||||
|
def initialize
|
||||||
|
super()
|
||||||
|
@_mail_was_called = false
|
||||||
|
@_message = Mail.new
|
||||||
|
end
|
||||||
|
|
||||||
|
def process(method_name, *args) #:nodoc:
|
||||||
|
payload = {
|
||||||
|
mailer: self.class.name,
|
||||||
|
action: method_name,
|
||||||
|
args: args
|
||||||
|
}
|
||||||
|
|
||||||
|
ActiveSupport::Notifications.instrument("process.action_mailer", payload) do
|
||||||
|
super
|
||||||
|
@_message = NullMail.new unless @_mail_was_called
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
class NullMail #:nodoc:
|
||||||
|
def body; "" end
|
||||||
|
def header; {} end
|
||||||
|
|
||||||
|
def respond_to?(string, include_all = false)
|
||||||
|
true
|
||||||
|
end
|
||||||
|
|
||||||
|
def method_missing(*args)
|
||||||
|
nil
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
# Returns the name of the mailer object.
|
||||||
|
def mailer_name
|
||||||
|
self.class.mailer_name
|
||||||
|
end
|
||||||
|
|
||||||
|
# Allows you to pass random and unusual headers to the new <tt>Mail::Message</tt>
|
||||||
|
# object which will add them to itself.
|
||||||
|
#
|
||||||
|
# headers['X-Special-Domain-Specific-Header'] = "SecretValue"
|
||||||
|
#
|
||||||
|
# You can also pass a hash into headers of header field names and values,
|
||||||
|
# which will then be set on the <tt>Mail::Message</tt> object:
|
||||||
|
#
|
||||||
|
# headers 'X-Special-Domain-Specific-Header' => "SecretValue",
|
||||||
|
# 'In-Reply-To' => incoming.message_id
|
||||||
|
#
|
||||||
|
# The resulting <tt>Mail::Message</tt> will have the following in its header:
|
||||||
|
#
|
||||||
|
# X-Special-Domain-Specific-Header: SecretValue
|
||||||
|
#
|
||||||
|
# Note about replacing already defined headers:
|
||||||
|
#
|
||||||
|
# * +subject+
|
||||||
|
# * +sender+
|
||||||
|
# * +from+
|
||||||
|
# * +to+
|
||||||
|
# * +cc+
|
||||||
|
# * +bcc+
|
||||||
|
# * +reply-to+
|
||||||
|
# * +orig-date+
|
||||||
|
# * +message-id+
|
||||||
|
# * +references+
|
||||||
|
#
|
||||||
|
# Fields can only appear once in email headers while other fields such as
|
||||||
|
# <tt>X-Anything</tt> can appear multiple times.
|
||||||
|
#
|
||||||
|
# If you want to replace any header which already exists, first set it to
|
||||||
|
# +nil+ in order to reset the value otherwise another field will be added
|
||||||
|
# for the same header.
|
||||||
|
def headers(args = nil)
|
||||||
|
if args
|
||||||
|
@_message.headers(args)
|
||||||
|
else
|
||||||
|
@_message
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
# Allows you to add attachments to an email, like so:
|
||||||
|
#
|
||||||
|
# mail.attachments['filename.jpg'] = File.read('/path/to/filename.jpg')
|
||||||
|
#
|
||||||
|
# If you do this, then Mail will take the file name and work out the mime type.
|
||||||
|
# It will also set the Content-Type, Content-Disposition, Content-Transfer-Encoding
|
||||||
|
# and encode the contents of the attachment in Base64.
|
||||||
|
#
|
||||||
|
# You can also specify overrides if you want by passing a hash instead of a string:
|
||||||
|
#
|
||||||
|
# mail.attachments['filename.jpg'] = {mime_type: 'application/gzip',
|
||||||
|
# content: File.read('/path/to/filename.jpg')}
|
||||||
|
#
|
||||||
|
# If you want to use encoding other than Base64 then you will need to pass encoding
|
||||||
|
# type along with the pre-encoded content as Mail doesn't know how to decode the
|
||||||
|
# data:
|
||||||
|
#
|
||||||
|
# file_content = SpecialEncode(File.read('/path/to/filename.jpg'))
|
||||||
|
# mail.attachments['filename.jpg'] = {mime_type: 'application/gzip',
|
||||||
|
# encoding: 'SpecialEncoding',
|
||||||
|
# content: file_content }
|
||||||
|
#
|
||||||
|
# You can also search for specific attachments:
|
||||||
|
#
|
||||||
|
# # By Filename
|
||||||
|
# mail.attachments['filename.jpg'] # => Mail::Part object or nil
|
||||||
|
#
|
||||||
|
# # or by index
|
||||||
|
# mail.attachments[0] # => Mail::Part (first attachment)
|
||||||
|
#
|
||||||
|
def attachments
|
||||||
|
if @_mail_was_called
|
||||||
|
LateAttachmentsProxy.new(@_message.attachments)
|
||||||
|
else
|
||||||
|
@_message.attachments
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
class LateAttachmentsProxy < SimpleDelegator
|
||||||
|
def inline; _raise_error end
|
||||||
|
def []=(_name, _content); _raise_error end
|
||||||
|
|
||||||
|
private
|
||||||
|
def _raise_error
|
||||||
|
raise RuntimeError, "Can't add attachments after `mail` was called.\n" \
|
||||||
|
"Make sure to use `attachments[]=` before calling `mail`."
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
# The main method that creates the message and renders the email templates. There are
|
||||||
|
# two ways to call this method, with a block, or without a block.
|
||||||
|
#
|
||||||
|
# It accepts a headers hash. This hash allows you to specify
|
||||||
|
# the most used headers in an email message, these are:
|
||||||
|
#
|
||||||
|
# * +:subject+ - The subject of the message, if this is omitted, Action Mailer will
|
||||||
|
# ask the Rails I18n class for a translated +:subject+ in the scope of
|
||||||
|
# <tt>[mailer_scope, action_name]</tt> or if this is missing, will translate the
|
||||||
|
# humanized version of the +action_name+
|
||||||
|
# * +:to+ - Who the message is destined for, can be a string of addresses, or an array
|
||||||
|
# of addresses.
|
||||||
|
# * +:from+ - Who the message is from
|
||||||
|
# * +:cc+ - Who you would like to Carbon-Copy on this email, can be a string of addresses,
|
||||||
|
# or an array of addresses.
|
||||||
|
# * +:bcc+ - Who you would like to Blind-Carbon-Copy on this email, can be a string of
|
||||||
|
# addresses, or an array of addresses.
|
||||||
|
# * +:reply_to+ - Who to set the Reply-To header of the email to.
|
||||||
|
# * +:date+ - The date to say the email was sent on.
|
||||||
|
#
|
||||||
|
# You can set default values for any of the above headers (except +:date+)
|
||||||
|
# by using the ::default class method:
|
||||||
|
#
|
||||||
|
# class Notifier < ActionMailer::Base
|
||||||
|
# default from: 'no-reply@test.lindsaar.net',
|
||||||
|
# bcc: 'email_logger@test.lindsaar.net',
|
||||||
|
# reply_to: 'bounces@test.lindsaar.net'
|
||||||
|
# end
|
||||||
|
#
|
||||||
|
# If you need other headers not listed above, you can either pass them in
|
||||||
|
# as part of the headers hash or use the <tt>headers['name'] = value</tt>
|
||||||
|
# method.
|
||||||
|
#
|
||||||
|
# When a +:return_path+ is specified as header, that value will be used as
|
||||||
|
# the 'envelope from' address for the Mail message. Setting this is useful
|
||||||
|
# when you want delivery notifications sent to a different address than the
|
||||||
|
# one in +:from+. Mail will actually use the +:return_path+ in preference
|
||||||
|
# to the +:sender+ in preference to the +:from+ field for the 'envelope
|
||||||
|
# from' value.
|
||||||
|
#
|
||||||
|
# If you do not pass a block to the +mail+ method, it will find all
|
||||||
|
# templates in the view paths using by default the mailer name and the
|
||||||
|
# method name that it is being called from, it will then create parts for
|
||||||
|
# each of these templates intelligently, making educated guesses on correct
|
||||||
|
# content type and sequence, and return a fully prepared <tt>Mail::Message</tt>
|
||||||
|
# ready to call <tt>:deliver</tt> on to send.
|
||||||
|
#
|
||||||
|
# For example:
|
||||||
|
#
|
||||||
|
# class Notifier < ActionMailer::Base
|
||||||
|
# default from: 'no-reply@test.lindsaar.net'
|
||||||
|
#
|
||||||
|
# def welcome
|
||||||
|
# mail(to: 'mikel@test.lindsaar.net')
|
||||||
|
# end
|
||||||
|
# end
|
||||||
|
#
|
||||||
|
# Will look for all templates at "app/views/notifier" with name "welcome".
|
||||||
|
# If no welcome template exists, it will raise an ActionView::MissingTemplate error.
|
||||||
|
#
|
||||||
|
# However, those can be customized:
|
||||||
|
#
|
||||||
|
# mail(template_path: 'notifications', template_name: 'another')
|
||||||
|
#
|
||||||
|
# And now it will look for all templates at "app/views/notifications" with name "another".
|
||||||
|
#
|
||||||
|
# If you do pass a block, you can render specific templates of your choice:
|
||||||
|
#
|
||||||
|
# mail(to: 'mikel@test.lindsaar.net') do |format|
|
||||||
|
# format.text
|
||||||
|
# format.html
|
||||||
|
# end
|
||||||
|
#
|
||||||
|
# You can even render plain text directly without using a template:
|
||||||
|
#
|
||||||
|
# mail(to: 'mikel@test.lindsaar.net') do |format|
|
||||||
|
# format.text { render plain: "Hello Mikel!" }
|
||||||
|
# format.html { render html: "<h1>Hello Mikel!</h1>".html_safe }
|
||||||
|
# end
|
||||||
|
#
|
||||||
|
# Which will render a +multipart/alternative+ email with +text/plain+ and
|
||||||
|
# +text/html+ parts.
|
||||||
|
#
|
||||||
|
# The block syntax also allows you to customize the part headers if desired:
|
||||||
|
#
|
||||||
|
# mail(to: 'mikel@test.lindsaar.net') do |format|
|
||||||
|
# format.text(content_transfer_encoding: "base64")
|
||||||
|
# format.html
|
||||||
|
# end
|
||||||
|
#
|
||||||
|
def mail(headers = {}, &block)
|
||||||
|
return message if @_mail_was_called && headers.blank? && !block
|
||||||
|
|
||||||
|
# At the beginning, do not consider class default for content_type
|
||||||
|
content_type = headers[:content_type]
|
||||||
|
|
||||||
|
headers = apply_defaults(headers)
|
||||||
|
|
||||||
|
# Apply charset at the beginning so all fields are properly quoted
|
||||||
|
message.charset = charset = headers[:charset]
|
||||||
|
|
||||||
|
# Set configure delivery behavior
|
||||||
|
wrap_delivery_behavior!(headers[:delivery_method], headers[:delivery_method_options])
|
||||||
|
|
||||||
|
assign_headers_to_message(message, headers)
|
||||||
|
|
||||||
|
# Render the templates and blocks
|
||||||
|
responses = collect_responses(headers, &block)
|
||||||
|
@_mail_was_called = true
|
||||||
|
|
||||||
|
create_parts_from_responses(message, responses)
|
||||||
|
|
||||||
|
# Setup content type, reapply charset and handle parts order
|
||||||
|
message.content_type = set_content_type(message, content_type, headers[:content_type])
|
||||||
|
message.charset = charset
|
||||||
|
|
||||||
|
if message.multipart?
|
||||||
|
message.body.set_sort_order(headers[:parts_order])
|
||||||
|
message.body.sort_parts!
|
||||||
|
end
|
||||||
|
|
||||||
|
message
|
||||||
|
end
|
||||||
|
|
||||||
|
private
|
||||||
|
|
||||||
|
# Used by #mail to set the content type of the message.
|
||||||
|
#
|
||||||
|
# It will use the given +user_content_type+, or multipart if the mail
|
||||||
|
# message has any attachments. If the attachments are inline, the content
|
||||||
|
# type will be "multipart/related", otherwise "multipart/mixed".
|
||||||
|
#
|
||||||
|
# If there is no content type passed in via headers, and there are no
|
||||||
|
# attachments, or the message is multipart, then the default content type is
|
||||||
|
# used.
|
||||||
|
def set_content_type(m, user_content_type, class_default) # :doc:
|
||||||
|
params = m.content_type_parameters || {}
|
||||||
|
case
|
||||||
|
when user_content_type.present?
|
||||||
|
user_content_type
|
||||||
|
when m.has_attachments?
|
||||||
|
if m.attachments.detect(&:inline?)
|
||||||
|
["multipart", "related", params]
|
||||||
|
else
|
||||||
|
["multipart", "mixed", params]
|
||||||
|
end
|
||||||
|
when m.multipart?
|
||||||
|
["multipart", "alternative", params]
|
||||||
|
else
|
||||||
|
m.content_type || class_default
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
# Translates the +subject+ using Rails I18n class under <tt>[mailer_scope, action_name]</tt> scope.
|
||||||
|
# If it does not find a translation for the +subject+ under the specified scope it will default to a
|
||||||
|
# humanized version of the <tt>action_name</tt>.
|
||||||
|
# If the subject has interpolations, you can pass them through the +interpolations+ parameter.
|
||||||
|
def default_i18n_subject(interpolations = {}) # :doc:
|
||||||
|
mailer_scope = self.class.mailer_name.tr("/", ".")
|
||||||
|
I18n.t(:subject, interpolations.merge(scope: [mailer_scope, action_name], default: action_name.humanize))
|
||||||
|
end
|
||||||
|
|
||||||
|
# Emails do not support relative path links.
|
||||||
|
def self.supports_path? # :doc:
|
||||||
|
false
|
||||||
|
end
|
||||||
|
|
||||||
|
def apply_defaults(headers)
|
||||||
|
default_values = self.class.default.map do |key, value|
|
||||||
|
[
|
||||||
|
key,
|
||||||
|
compute_default(value)
|
||||||
|
]
|
||||||
|
end.to_h
|
||||||
|
|
||||||
|
headers_with_defaults = headers.reverse_merge(default_values)
|
||||||
|
headers_with_defaults[:subject] ||= default_i18n_subject
|
||||||
|
headers_with_defaults
|
||||||
|
end
|
||||||
|
|
||||||
|
def compute_default(value)
|
||||||
|
return value unless value.is_a?(Proc)
|
||||||
|
|
||||||
|
if value.arity == 1
|
||||||
|
instance_exec(self, &value)
|
||||||
|
else
|
||||||
|
instance_exec(&value)
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
def assign_headers_to_message(message, headers)
|
||||||
|
assignable = headers.except(:parts_order, :content_type, :body, :template_name,
|
||||||
|
:template_path, :delivery_method, :delivery_method_options)
|
||||||
|
assignable.each { |k, v| message[k] = v }
|
||||||
|
end
|
||||||
|
|
||||||
|
def collect_responses(headers)
|
||||||
|
if block_given?
|
||||||
|
collector = ActionMailer::Collector.new(lookup_context) { render(action_name) }
|
||||||
|
yield(collector)
|
||||||
|
collector.responses
|
||||||
|
elsif headers[:body]
|
||||||
|
collect_responses_from_text(headers)
|
||||||
|
else
|
||||||
|
collect_responses_from_templates(headers)
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
def collect_responses_from_text(headers)
|
||||||
|
[{
|
||||||
|
body: headers.delete(:body),
|
||||||
|
content_type: headers[:content_type] || "text/plain"
|
||||||
|
}]
|
||||||
|
end
|
||||||
|
|
||||||
|
def collect_responses_from_templates(headers)
|
||||||
|
templates_path = headers[:template_path] || self.class.mailer_name
|
||||||
|
templates_name = headers[:template_name] || action_name
|
||||||
|
|
||||||
|
each_template(Array(templates_path), templates_name).map do |template|
|
||||||
|
self.formats = template.formats
|
||||||
|
{
|
||||||
|
body: render(template: template),
|
||||||
|
content_type: template.type.to_s
|
||||||
|
}
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
def each_template(paths, name, &block)
|
||||||
|
templates = lookup_context.find_all(name, paths)
|
||||||
|
if templates.empty?
|
||||||
|
raise ActionView::MissingTemplate.new(paths, name, paths, false, "mailer")
|
||||||
|
else
|
||||||
|
templates.uniq(&:formats).each(&block)
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
def create_parts_from_responses(m, responses)
|
||||||
|
if responses.size == 1 && !m.has_attachments?
|
||||||
|
responses[0].each { |k, v| m[k] = v }
|
||||||
|
elsif responses.size > 1 && m.has_attachments?
|
||||||
|
container = Mail::Part.new
|
||||||
|
container.content_type = "multipart/alternative"
|
||||||
|
responses.each { |r| insert_part(container, r, m.charset) }
|
||||||
|
m.add_part(container)
|
||||||
|
else
|
||||||
|
responses.each { |r| insert_part(m, r, m.charset) }
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
def insert_part(container, response, charset)
|
||||||
|
response[:charset] ||= charset
|
||||||
|
part = Mail::Part.new(response)
|
||||||
|
container.add_part(part)
|
||||||
|
end
|
||||||
|
|
||||||
|
# This and #instrument_name is for caching instrument
|
||||||
|
def instrument_payload(key)
|
||||||
|
{
|
||||||
|
mailer: mailer_name,
|
||||||
|
key: key
|
||||||
|
}
|
||||||
|
end
|
||||||
|
|
||||||
|
def instrument_name
|
||||||
|
"action_mailer".freeze
|
||||||
|
end
|
||||||
|
|
||||||
|
ActiveSupport.run_load_hooks(:action_mailer, self)
|
||||||
|
end
|
||||||
|
end
|
30
debian/gems-compat/actionmailer-5.1.6.1/lib/action_mailer/collector.rb
vendored
Normal file
30
debian/gems-compat/actionmailer-5.1.6.1/lib/action_mailer/collector.rb
vendored
Normal file
|
@ -0,0 +1,30 @@
|
||||||
|
require "abstract_controller/collector"
|
||||||
|
require "active_support/core_ext/hash/reverse_merge"
|
||||||
|
require "active_support/core_ext/array/extract_options"
|
||||||
|
|
||||||
|
module ActionMailer
|
||||||
|
class Collector
|
||||||
|
include AbstractController::Collector
|
||||||
|
attr_reader :responses
|
||||||
|
|
||||||
|
def initialize(context, &block)
|
||||||
|
@context = context
|
||||||
|
@responses = []
|
||||||
|
@default_render = block
|
||||||
|
end
|
||||||
|
|
||||||
|
def any(*args, &block)
|
||||||
|
options = args.extract_options!
|
||||||
|
raise ArgumentError, "You have to supply at least one format" if args.empty?
|
||||||
|
args.each { |type| send(type, options.dup, &block) }
|
||||||
|
end
|
||||||
|
alias :all :any
|
||||||
|
|
||||||
|
def custom(mime, options = {})
|
||||||
|
options.reverse_merge!(content_type: mime.to_s)
|
||||||
|
@context.formats = [mime.to_sym]
|
||||||
|
options[:body] = block_given? ? yield : @default_render.call
|
||||||
|
@responses << options
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
34
debian/gems-compat/actionmailer-5.1.6.1/lib/action_mailer/delivery_job.rb
vendored
Normal file
34
debian/gems-compat/actionmailer-5.1.6.1/lib/action_mailer/delivery_job.rb
vendored
Normal file
|
@ -0,0 +1,34 @@
|
||||||
|
require "active_job"
|
||||||
|
|
||||||
|
module ActionMailer
|
||||||
|
# The <tt>ActionMailer::DeliveryJob</tt> class is used when you
|
||||||
|
# want to send emails outside of the request-response cycle.
|
||||||
|
#
|
||||||
|
# Exceptions are rescued and handled by the mailer class.
|
||||||
|
class DeliveryJob < ActiveJob::Base # :nodoc:
|
||||||
|
queue_as { ActionMailer::Base.deliver_later_queue_name }
|
||||||
|
|
||||||
|
rescue_from StandardError, with: :handle_exception_with_mailer_class
|
||||||
|
|
||||||
|
def perform(mailer, mail_method, delivery_method, *args) #:nodoc:
|
||||||
|
mailer.constantize.public_send(mail_method, *args).send(delivery_method)
|
||||||
|
end
|
||||||
|
|
||||||
|
private
|
||||||
|
# "Deserialize" the mailer class name by hand in case another argument
|
||||||
|
# (like a Global ID reference) raised DeserializationError.
|
||||||
|
def mailer_class
|
||||||
|
if mailer = Array(@serialized_arguments).first || Array(arguments).first
|
||||||
|
mailer.constantize
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
def handle_exception_with_mailer_class(exception)
|
||||||
|
if klass = mailer_class
|
||||||
|
klass.handle_exception exception
|
||||||
|
else
|
||||||
|
raise exception
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
87
debian/gems-compat/actionmailer-5.1.6.1/lib/action_mailer/delivery_methods.rb
vendored
Normal file
87
debian/gems-compat/actionmailer-5.1.6.1/lib/action_mailer/delivery_methods.rb
vendored
Normal file
|
@ -0,0 +1,87 @@
|
||||||
|
require "tmpdir"
|
||||||
|
|
||||||
|
module ActionMailer
|
||||||
|
# This module handles everything related to mail delivery, from registering
|
||||||
|
# new delivery methods to configuring the mail object to be sent.
|
||||||
|
module DeliveryMethods
|
||||||
|
extend ActiveSupport::Concern
|
||||||
|
|
||||||
|
included do
|
||||||
|
class_attribute :delivery_methods, :delivery_method
|
||||||
|
|
||||||
|
# Do not make this inheritable, because we always want it to propagate
|
||||||
|
cattr_accessor :raise_delivery_errors
|
||||||
|
self.raise_delivery_errors = true
|
||||||
|
|
||||||
|
cattr_accessor :perform_deliveries
|
||||||
|
self.perform_deliveries = true
|
||||||
|
|
||||||
|
cattr_accessor :deliver_later_queue_name
|
||||||
|
self.deliver_later_queue_name = :mailers
|
||||||
|
|
||||||
|
self.delivery_methods = {}.freeze
|
||||||
|
self.delivery_method = :smtp
|
||||||
|
|
||||||
|
add_delivery_method :smtp, Mail::SMTP,
|
||||||
|
address: "localhost",
|
||||||
|
port: 25,
|
||||||
|
domain: "localhost.localdomain",
|
||||||
|
user_name: nil,
|
||||||
|
password: nil,
|
||||||
|
authentication: nil,
|
||||||
|
enable_starttls_auto: true
|
||||||
|
|
||||||
|
add_delivery_method :file, Mail::FileDelivery,
|
||||||
|
location: defined?(Rails.root) ? "#{Rails.root}/tmp/mails" : "#{Dir.tmpdir}/mails"
|
||||||
|
|
||||||
|
add_delivery_method :sendmail, Mail::Sendmail,
|
||||||
|
location: "/usr/sbin/sendmail",
|
||||||
|
arguments: "-i"
|
||||||
|
|
||||||
|
add_delivery_method :test, Mail::TestMailer
|
||||||
|
end
|
||||||
|
|
||||||
|
# Helpers for creating and wrapping delivery behavior, used by DeliveryMethods.
|
||||||
|
module ClassMethods
|
||||||
|
# Provides a list of emails that have been delivered by Mail::TestMailer
|
||||||
|
delegate :deliveries, :deliveries=, to: Mail::TestMailer
|
||||||
|
|
||||||
|
# Adds a new delivery method through the given class using the given
|
||||||
|
# symbol as alias and the default options supplied.
|
||||||
|
#
|
||||||
|
# add_delivery_method :sendmail, Mail::Sendmail,
|
||||||
|
# location: '/usr/sbin/sendmail',
|
||||||
|
# arguments: '-i'
|
||||||
|
def add_delivery_method(symbol, klass, default_options = {})
|
||||||
|
class_attribute(:"#{symbol}_settings") unless respond_to?(:"#{symbol}_settings")
|
||||||
|
send(:"#{symbol}_settings=", default_options)
|
||||||
|
self.delivery_methods = delivery_methods.merge(symbol.to_sym => klass).freeze
|
||||||
|
end
|
||||||
|
|
||||||
|
def wrap_delivery_behavior(mail, method = nil, options = nil) # :nodoc:
|
||||||
|
method ||= delivery_method
|
||||||
|
mail.delivery_handler = self
|
||||||
|
|
||||||
|
case method
|
||||||
|
when NilClass
|
||||||
|
raise "Delivery method cannot be nil"
|
||||||
|
when Symbol
|
||||||
|
if klass = delivery_methods[method]
|
||||||
|
mail.delivery_method(klass, (send(:"#{method}_settings") || {}).merge(options || {}))
|
||||||
|
else
|
||||||
|
raise "Invalid delivery method #{method.inspect}"
|
||||||
|
end
|
||||||
|
else
|
||||||
|
mail.delivery_method(method)
|
||||||
|
end
|
||||||
|
|
||||||
|
mail.perform_deliveries = perform_deliveries
|
||||||
|
mail.raise_delivery_errors = raise_delivery_errors
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
def wrap_delivery_behavior!(*args) # :nodoc:
|
||||||
|
self.class.wrap_delivery_behavior(message, *args)
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
15
debian/gems-compat/actionmailer-5.1.6.1/lib/action_mailer/gem_version.rb
vendored
Normal file
15
debian/gems-compat/actionmailer-5.1.6.1/lib/action_mailer/gem_version.rb
vendored
Normal file
|
@ -0,0 +1,15 @@
|
||||||
|
module ActionMailer
|
||||||
|
# Returns the version of the currently loaded Action Mailer as a <tt>Gem::Version</tt>.
|
||||||
|
def self.gem_version
|
||||||
|
Gem::Version.new VERSION::STRING
|
||||||
|
end
|
||||||
|
|
||||||
|
module VERSION
|
||||||
|
MAJOR = 5
|
||||||
|
MINOR = 1
|
||||||
|
TINY = 6
|
||||||
|
PRE = "1"
|
||||||
|
|
||||||
|
STRING = [MAJOR, MINOR, TINY, PRE].compact.join(".")
|
||||||
|
end
|
||||||
|
end
|
57
debian/gems-compat/actionmailer-5.1.6.1/lib/action_mailer/inline_preview_interceptor.rb
vendored
Normal file
57
debian/gems-compat/actionmailer-5.1.6.1/lib/action_mailer/inline_preview_interceptor.rb
vendored
Normal file
|
@ -0,0 +1,57 @@
|
||||||
|
require "base64"
|
||||||
|
|
||||||
|
module ActionMailer
|
||||||
|
# Implements a mailer preview interceptor that converts image tag src attributes
|
||||||
|
# that use inline cid: style urls to data: style urls so that they are visible
|
||||||
|
# when previewing an HTML email in a web browser.
|
||||||
|
#
|
||||||
|
# This interceptor is enabled by default. To disable it, delete it from the
|
||||||
|
# <tt>ActionMailer::Base.preview_interceptors</tt> array:
|
||||||
|
#
|
||||||
|
# ActionMailer::Base.preview_interceptors.delete(ActionMailer::InlinePreviewInterceptor)
|
||||||
|
#
|
||||||
|
class InlinePreviewInterceptor
|
||||||
|
PATTERN = /src=(?:"cid:[^"]+"|'cid:[^']+')/i
|
||||||
|
|
||||||
|
include Base64
|
||||||
|
|
||||||
|
def self.previewing_email(message) #:nodoc:
|
||||||
|
new(message).transform!
|
||||||
|
end
|
||||||
|
|
||||||
|
def initialize(message) #:nodoc:
|
||||||
|
@message = message
|
||||||
|
end
|
||||||
|
|
||||||
|
def transform! #:nodoc:
|
||||||
|
return message if html_part.blank?
|
||||||
|
|
||||||
|
html_part.body = html_part.decoded.gsub(PATTERN) do |match|
|
||||||
|
if part = find_part(match[9..-2])
|
||||||
|
%[src="#{data_url(part)}"]
|
||||||
|
else
|
||||||
|
match
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
message
|
||||||
|
end
|
||||||
|
|
||||||
|
private
|
||||||
|
def message
|
||||||
|
@message
|
||||||
|
end
|
||||||
|
|
||||||
|
def html_part
|
||||||
|
@html_part ||= message.html_part
|
||||||
|
end
|
||||||
|
|
||||||
|
def data_url(part)
|
||||||
|
"data:#{part.mime_type};base64,#{strict_encode64(part.body.raw_source)}"
|
||||||
|
end
|
||||||
|
|
||||||
|
def find_part(cid)
|
||||||
|
message.all_parts.find { |p| p.attachment? && p.cid == cid }
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
39
debian/gems-compat/actionmailer-5.1.6.1/lib/action_mailer/log_subscriber.rb
vendored
Normal file
39
debian/gems-compat/actionmailer-5.1.6.1/lib/action_mailer/log_subscriber.rb
vendored
Normal file
|
@ -0,0 +1,39 @@
|
||||||
|
require "active_support/log_subscriber"
|
||||||
|
|
||||||
|
module ActionMailer
|
||||||
|
# Implements the ActiveSupport::LogSubscriber for logging notifications when
|
||||||
|
# email is delivered or received.
|
||||||
|
class LogSubscriber < ActiveSupport::LogSubscriber
|
||||||
|
# An email was delivered.
|
||||||
|
def deliver(event)
|
||||||
|
info do
|
||||||
|
recipients = Array(event.payload[:to]).join(", ")
|
||||||
|
"Sent mail to #{recipients} (#{event.duration.round(1)}ms)"
|
||||||
|
end
|
||||||
|
|
||||||
|
debug { event.payload[:mail] }
|
||||||
|
end
|
||||||
|
|
||||||
|
# An email was received.
|
||||||
|
def receive(event)
|
||||||
|
info { "Received mail (#{event.duration.round(1)}ms)" }
|
||||||
|
debug { event.payload[:mail] }
|
||||||
|
end
|
||||||
|
|
||||||
|
# An email was generated.
|
||||||
|
def process(event)
|
||||||
|
debug do
|
||||||
|
mailer = event.payload[:mailer]
|
||||||
|
action = event.payload[:action]
|
||||||
|
"#{mailer}##{action}: processed outbound mail in #{event.duration.round(1)}ms"
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
# Use the logger configured for ActionMailer::Base.
|
||||||
|
def logger
|
||||||
|
ActionMailer::Base.logger
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
ActionMailer::LogSubscriber.attach_to :action_mailer
|
70
debian/gems-compat/actionmailer-5.1.6.1/lib/action_mailer/mail_helper.rb
vendored
Normal file
70
debian/gems-compat/actionmailer-5.1.6.1/lib/action_mailer/mail_helper.rb
vendored
Normal file
|
@ -0,0 +1,70 @@
|
||||||
|
module ActionMailer
|
||||||
|
# Provides helper methods for ActionMailer::Base that can be used for easily
|
||||||
|
# formatting messages, accessing mailer or message instances, and the
|
||||||
|
# attachments list.
|
||||||
|
module MailHelper
|
||||||
|
# Take the text and format it, indented two spaces for each line, and
|
||||||
|
# wrapped at 72 columns:
|
||||||
|
#
|
||||||
|
# text = <<-TEXT
|
||||||
|
# This is
|
||||||
|
# the paragraph.
|
||||||
|
#
|
||||||
|
# * item1 * item2
|
||||||
|
# TEXT
|
||||||
|
#
|
||||||
|
# block_format text
|
||||||
|
# # => " This is the paragraph.\n\n * item1\n * item2\n"
|
||||||
|
def block_format(text)
|
||||||
|
formatted = text.split(/\n\r?\n/).collect { |paragraph|
|
||||||
|
format_paragraph(paragraph)
|
||||||
|
}.join("\n\n")
|
||||||
|
|
||||||
|
# Make list points stand on their own line
|
||||||
|
formatted.gsub!(/[ ]*([*]+) ([^*]*)/) { " #{$1} #{$2.strip}\n" }
|
||||||
|
formatted.gsub!(/[ ]*([#]+) ([^#]*)/) { " #{$1} #{$2.strip}\n" }
|
||||||
|
|
||||||
|
formatted
|
||||||
|
end
|
||||||
|
|
||||||
|
# Access the mailer instance.
|
||||||
|
def mailer
|
||||||
|
@_controller
|
||||||
|
end
|
||||||
|
|
||||||
|
# Access the message instance.
|
||||||
|
def message
|
||||||
|
@_message
|
||||||
|
end
|
||||||
|
|
||||||
|
# Access the message attachments list.
|
||||||
|
def attachments
|
||||||
|
mailer.attachments
|
||||||
|
end
|
||||||
|
|
||||||
|
# Returns +text+ wrapped at +len+ columns and indented +indent+ spaces.
|
||||||
|
# By default column length +len+ equals 72 characters and indent
|
||||||
|
# +indent+ equal two spaces.
|
||||||
|
#
|
||||||
|
# my_text = 'Here is a sample text with more than 40 characters'
|
||||||
|
#
|
||||||
|
# format_paragraph(my_text, 25, 4)
|
||||||
|
# # => " Here is a sample text with\n more than 40 characters"
|
||||||
|
def format_paragraph(text, len = 72, indent = 2)
|
||||||
|
sentences = [[]]
|
||||||
|
|
||||||
|
text.split.each do |word|
|
||||||
|
if sentences.first.present? && (sentences.last + [word]).join(" ").length > len
|
||||||
|
sentences << [word]
|
||||||
|
else
|
||||||
|
sentences.last << word
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
indentation = " " * indent
|
||||||
|
sentences.map! { |sentence|
|
||||||
|
"#{indentation}#{sentence.join(' ')}"
|
||||||
|
}.join "\n"
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
125
debian/gems-compat/actionmailer-5.1.6.1/lib/action_mailer/message_delivery.rb
vendored
Normal file
125
debian/gems-compat/actionmailer-5.1.6.1/lib/action_mailer/message_delivery.rb
vendored
Normal file
|
@ -0,0 +1,125 @@
|
||||||
|
require "delegate"
|
||||||
|
|
||||||
|
module ActionMailer
|
||||||
|
# The <tt>ActionMailer::MessageDelivery</tt> class is used by
|
||||||
|
# <tt>ActionMailer::Base</tt> when creating a new mailer.
|
||||||
|
# <tt>MessageDelivery</tt> is a wrapper (+Delegator+ subclass) around a lazy
|
||||||
|
# created <tt>Mail::Message</tt>. You can get direct access to the
|
||||||
|
# <tt>Mail::Message</tt>, deliver the email or schedule the email to be sent
|
||||||
|
# through Active Job.
|
||||||
|
#
|
||||||
|
# Notifier.welcome(User.first) # an ActionMailer::MessageDelivery object
|
||||||
|
# Notifier.welcome(User.first).deliver_now # sends the email
|
||||||
|
# Notifier.welcome(User.first).deliver_later # enqueue email delivery as a job through Active Job
|
||||||
|
# Notifier.welcome(User.first).message # a Mail::Message object
|
||||||
|
class MessageDelivery < Delegator
|
||||||
|
def initialize(mailer_class, action, *args) #:nodoc:
|
||||||
|
@mailer_class, @action, @args = mailer_class, action, args
|
||||||
|
|
||||||
|
# The mail is only processed if we try to call any methods on it.
|
||||||
|
# Typical usage will leave it unloaded and call deliver_later.
|
||||||
|
@processed_mailer = nil
|
||||||
|
@mail_message = nil
|
||||||
|
end
|
||||||
|
|
||||||
|
# Method calls are delegated to the Mail::Message that's ready to deliver.
|
||||||
|
def __getobj__ #:nodoc:
|
||||||
|
@mail_message ||= processed_mailer.message
|
||||||
|
end
|
||||||
|
|
||||||
|
# Unused except for delegator internals (dup, marshaling).
|
||||||
|
def __setobj__(mail_message) #:nodoc:
|
||||||
|
@mail_message = mail_message
|
||||||
|
end
|
||||||
|
|
||||||
|
# Returns the resulting Mail::Message
|
||||||
|
def message
|
||||||
|
__getobj__
|
||||||
|
end
|
||||||
|
|
||||||
|
# Was the delegate loaded, causing the mailer action to be processed?
|
||||||
|
def processed?
|
||||||
|
@processed_mailer || @mail_message
|
||||||
|
end
|
||||||
|
|
||||||
|
# Enqueues the email to be delivered through Active Job. When the
|
||||||
|
# job runs it will send the email using +deliver_now!+. That means
|
||||||
|
# that the message will be sent bypassing checking +perform_deliveries+
|
||||||
|
# and +raise_delivery_errors+, so use with caution.
|
||||||
|
#
|
||||||
|
# Notifier.welcome(User.first).deliver_later!
|
||||||
|
# Notifier.welcome(User.first).deliver_later!(wait: 1.hour)
|
||||||
|
# Notifier.welcome(User.first).deliver_later!(wait_until: 10.hours.from_now)
|
||||||
|
#
|
||||||
|
# Options:
|
||||||
|
#
|
||||||
|
# * <tt>:wait</tt> - Enqueue the email to be delivered with a delay
|
||||||
|
# * <tt>:wait_until</tt> - Enqueue the email to be delivered at (after) a specific date / time
|
||||||
|
# * <tt>:queue</tt> - Enqueue the email on the specified queue
|
||||||
|
def deliver_later!(options = {})
|
||||||
|
enqueue_delivery :deliver_now!, options
|
||||||
|
end
|
||||||
|
|
||||||
|
# Enqueues the email to be delivered through Active Job. When the
|
||||||
|
# job runs it will send the email using +deliver_now+.
|
||||||
|
#
|
||||||
|
# Notifier.welcome(User.first).deliver_later
|
||||||
|
# Notifier.welcome(User.first).deliver_later(wait: 1.hour)
|
||||||
|
# Notifier.welcome(User.first).deliver_later(wait_until: 10.hours.from_now)
|
||||||
|
#
|
||||||
|
# Options:
|
||||||
|
#
|
||||||
|
# * <tt>:wait</tt> - Enqueue the email to be delivered with a delay.
|
||||||
|
# * <tt>:wait_until</tt> - Enqueue the email to be delivered at (after) a specific date / time.
|
||||||
|
# * <tt>:queue</tt> - Enqueue the email on the specified queue.
|
||||||
|
def deliver_later(options = {})
|
||||||
|
enqueue_delivery :deliver_now, options
|
||||||
|
end
|
||||||
|
|
||||||
|
# Delivers an email without checking +perform_deliveries+ and +raise_delivery_errors+,
|
||||||
|
# so use with caution.
|
||||||
|
#
|
||||||
|
# Notifier.welcome(User.first).deliver_now!
|
||||||
|
#
|
||||||
|
def deliver_now!
|
||||||
|
processed_mailer.handle_exceptions do
|
||||||
|
message.deliver!
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
# Delivers an email:
|
||||||
|
#
|
||||||
|
# Notifier.welcome(User.first).deliver_now
|
||||||
|
#
|
||||||
|
def deliver_now
|
||||||
|
processed_mailer.handle_exceptions do
|
||||||
|
message.deliver
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
private
|
||||||
|
# Returns the processed Mailer instance. We keep this instance
|
||||||
|
# on hand so we can delegate exception handling to it.
|
||||||
|
def processed_mailer
|
||||||
|
@processed_mailer ||= @mailer_class.new.tap do |mailer|
|
||||||
|
mailer.process @action, *@args
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
def enqueue_delivery(delivery_method, options = {})
|
||||||
|
if processed?
|
||||||
|
::Kernel.raise "You've accessed the message before asking to " \
|
||||||
|
"deliver it later, so you may have made local changes that would " \
|
||||||
|
"be silently lost if we enqueued a job to deliver it. Why? Only " \
|
||||||
|
"the mailer method *arguments* are passed with the delivery job! " \
|
||||||
|
"Do not access the message in any way if you mean to deliver it " \
|
||||||
|
"later. Workarounds: 1. don't touch the message before calling " \
|
||||||
|
"#deliver_later, 2. only touch the message *within your mailer " \
|
||||||
|
"method*, or 3. use a custom Active Job instead of #deliver_later."
|
||||||
|
else
|
||||||
|
args = @mailer_class.name, @action.to_s, delivery_method.to_s, *@args
|
||||||
|
::ActionMailer::DeliveryJob.set(options).perform_later(*args)
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
152
debian/gems-compat/actionmailer-5.1.6.1/lib/action_mailer/parameterized.rb
vendored
Normal file
152
debian/gems-compat/actionmailer-5.1.6.1/lib/action_mailer/parameterized.rb
vendored
Normal file
|
@ -0,0 +1,152 @@
|
||||||
|
module ActionMailer
|
||||||
|
# Provides the option to parameterize mailers in order to share instance variable
|
||||||
|
# setup, processing, and common headers.
|
||||||
|
#
|
||||||
|
# Consider this example that does not use parameterization:
|
||||||
|
#
|
||||||
|
# class InvitationsMailer < ApplicationMailer
|
||||||
|
# def account_invitation(inviter, invitee)
|
||||||
|
# @account = inviter.account
|
||||||
|
# @inviter = inviter
|
||||||
|
# @invitee = invitee
|
||||||
|
#
|
||||||
|
# subject = "#{@inviter.name} invited you to their Basecamp (#{@account.name})"
|
||||||
|
#
|
||||||
|
# mail \
|
||||||
|
# subject: subject,
|
||||||
|
# to: invitee.email_address,
|
||||||
|
# from: common_address(inviter),
|
||||||
|
# reply_to: inviter.email_address_with_name
|
||||||
|
# end
|
||||||
|
#
|
||||||
|
# def project_invitation(project, inviter, invitee)
|
||||||
|
# @account = inviter.account
|
||||||
|
# @project = project
|
||||||
|
# @inviter = inviter
|
||||||
|
# @invitee = invitee
|
||||||
|
# @summarizer = ProjectInvitationSummarizer.new(@project.bucket)
|
||||||
|
#
|
||||||
|
# subject = "#{@inviter.name.familiar} added you to a project in Basecamp (#{@account.name})"
|
||||||
|
#
|
||||||
|
# mail \
|
||||||
|
# subject: subject,
|
||||||
|
# to: invitee.email_address,
|
||||||
|
# from: common_address(inviter),
|
||||||
|
# reply_to: inviter.email_address_with_name
|
||||||
|
# end
|
||||||
|
#
|
||||||
|
# def bulk_project_invitation(projects, inviter, invitee)
|
||||||
|
# @account = inviter.account
|
||||||
|
# @projects = projects.sort_by(&:name)
|
||||||
|
# @inviter = inviter
|
||||||
|
# @invitee = invitee
|
||||||
|
#
|
||||||
|
# subject = "#{@inviter.name.familiar} added you to some new stuff in Basecamp (#{@account.name})"
|
||||||
|
#
|
||||||
|
# mail \
|
||||||
|
# subject: subject,
|
||||||
|
# to: invitee.email_address,
|
||||||
|
# from: common_address(inviter),
|
||||||
|
# reply_to: inviter.email_address_with_name
|
||||||
|
# end
|
||||||
|
# end
|
||||||
|
#
|
||||||
|
# InvitationsMailer.account_invitation(person_a, person_b).deliver_later
|
||||||
|
#
|
||||||
|
# Using parameterized mailers, this can be rewritten as:
|
||||||
|
#
|
||||||
|
# class InvitationsMailer < ApplicationMailer
|
||||||
|
# before_action { @inviter, @invitee = params[:inviter], params[:invitee] }
|
||||||
|
# before_action { @account = params[:inviter].account }
|
||||||
|
#
|
||||||
|
# default to: -> { @invitee.email_address },
|
||||||
|
# from: -> { common_address(@inviter) },
|
||||||
|
# reply_to: -> { @inviter.email_address_with_name }
|
||||||
|
#
|
||||||
|
# def account_invitation
|
||||||
|
# mail subject: "#{@inviter.name} invited you to their Basecamp (#{@account.name})"
|
||||||
|
# end
|
||||||
|
#
|
||||||
|
# def project_invitation
|
||||||
|
# @project = params[:project]
|
||||||
|
# @summarizer = ProjectInvitationSummarizer.new(@project.bucket)
|
||||||
|
#
|
||||||
|
# mail subject: "#{@inviter.name.familiar} added you to a project in Basecamp (#{@account.name})"
|
||||||
|
# end
|
||||||
|
#
|
||||||
|
# def bulk_project_invitation
|
||||||
|
# @projects = params[:projects].sort_by(&:name)
|
||||||
|
#
|
||||||
|
# mail subject: "#{@inviter.name.familiar} added you to some new stuff in Basecamp (#{@account.name})"
|
||||||
|
# end
|
||||||
|
# end
|
||||||
|
#
|
||||||
|
# InvitationsMailer.with(inviter: person_a, invitee: person_b).account_invitation.deliver_later
|
||||||
|
module Parameterized
|
||||||
|
extend ActiveSupport::Concern
|
||||||
|
|
||||||
|
included do
|
||||||
|
attr_accessor :params
|
||||||
|
end
|
||||||
|
|
||||||
|
module ClassMethods
|
||||||
|
# Provide the parameters to the mailer in order to use them in the instance methods and callbacks.
|
||||||
|
#
|
||||||
|
# InvitationsMailer.with(inviter: person_a, invitee: person_b).account_invitation.deliver_later
|
||||||
|
#
|
||||||
|
# See Parameterized documentation for full example.
|
||||||
|
def with(params)
|
||||||
|
ActionMailer::Parameterized::Mailer.new(self, params)
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
class Mailer # :nodoc:
|
||||||
|
def initialize(mailer, params)
|
||||||
|
@mailer, @params = mailer, params
|
||||||
|
end
|
||||||
|
|
||||||
|
private
|
||||||
|
def method_missing(method_name, *args)
|
||||||
|
if @mailer.action_methods.include?(method_name.to_s)
|
||||||
|
ActionMailer::Parameterized::MessageDelivery.new(@mailer, method_name, @params, *args)
|
||||||
|
else
|
||||||
|
super
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
def respond_to_missing?(method, include_all = false)
|
||||||
|
@mailer.respond_to?(method, include_all)
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
class MessageDelivery < ActionMailer::MessageDelivery # :nodoc:
|
||||||
|
def initialize(mailer_class, action, params, *args)
|
||||||
|
super(mailer_class, action, *args)
|
||||||
|
@params = params
|
||||||
|
end
|
||||||
|
|
||||||
|
private
|
||||||
|
def processed_mailer
|
||||||
|
@processed_mailer ||= @mailer_class.new.tap do |mailer|
|
||||||
|
mailer.params = @params
|
||||||
|
mailer.process @action, *@args
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
def enqueue_delivery(delivery_method, options = {})
|
||||||
|
if processed?
|
||||||
|
super
|
||||||
|
else
|
||||||
|
args = @mailer_class.name, @action.to_s, delivery_method.to_s, @params, *@args
|
||||||
|
ActionMailer::Parameterized::DeliveryJob.set(options).perform_later(*args)
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
class DeliveryJob < ActionMailer::DeliveryJob # :nodoc:
|
||||||
|
def perform(mailer, mail_method, delivery_method, params, *args)
|
||||||
|
mailer.constantize.with(params).public_send(mail_method, *args).send(delivery_method)
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
119
debian/gems-compat/actionmailer-5.1.6.1/lib/action_mailer/preview.rb
vendored
Normal file
119
debian/gems-compat/actionmailer-5.1.6.1/lib/action_mailer/preview.rb
vendored
Normal file
|
@ -0,0 +1,119 @@
|
||||||
|
require "active_support/descendants_tracker"
|
||||||
|
|
||||||
|
module ActionMailer
|
||||||
|
module Previews #:nodoc:
|
||||||
|
extend ActiveSupport::Concern
|
||||||
|
|
||||||
|
included do
|
||||||
|
# Set the location of mailer previews through app configuration:
|
||||||
|
#
|
||||||
|
# config.action_mailer.preview_path = "#{Rails.root}/lib/mailer_previews"
|
||||||
|
#
|
||||||
|
mattr_accessor :preview_path, instance_writer: false
|
||||||
|
|
||||||
|
# Enable or disable mailer previews through app configuration:
|
||||||
|
#
|
||||||
|
# config.action_mailer.show_previews = true
|
||||||
|
#
|
||||||
|
# Defaults to true for development environment
|
||||||
|
#
|
||||||
|
mattr_accessor :show_previews, instance_writer: false
|
||||||
|
|
||||||
|
# :nodoc:
|
||||||
|
mattr_accessor :preview_interceptors, instance_writer: false
|
||||||
|
self.preview_interceptors = [ActionMailer::InlinePreviewInterceptor]
|
||||||
|
end
|
||||||
|
|
||||||
|
module ClassMethods
|
||||||
|
# Register one or more Interceptors which will be called before mail is previewed.
|
||||||
|
def register_preview_interceptors(*interceptors)
|
||||||
|
interceptors.flatten.compact.each { |interceptor| register_preview_interceptor(interceptor) }
|
||||||
|
end
|
||||||
|
|
||||||
|
# Register an Interceptor which will be called before mail is previewed.
|
||||||
|
# Either a class or a string can be passed in as the Interceptor. If a
|
||||||
|
# string is passed in it will be <tt>constantize</tt>d.
|
||||||
|
def register_preview_interceptor(interceptor)
|
||||||
|
preview_interceptor = \
|
||||||
|
case interceptor
|
||||||
|
when String, Symbol
|
||||||
|
interceptor.to_s.camelize.constantize
|
||||||
|
else
|
||||||
|
interceptor
|
||||||
|
end
|
||||||
|
|
||||||
|
unless preview_interceptors.include?(preview_interceptor)
|
||||||
|
preview_interceptors << preview_interceptor
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
class Preview
|
||||||
|
extend ActiveSupport::DescendantsTracker
|
||||||
|
|
||||||
|
class << self
|
||||||
|
# Returns all mailer preview classes.
|
||||||
|
def all
|
||||||
|
load_previews if descendants.empty?
|
||||||
|
descendants
|
||||||
|
end
|
||||||
|
|
||||||
|
# Returns the mail object for the given email name. The registered preview
|
||||||
|
# interceptors will be informed so that they can transform the message
|
||||||
|
# as they would if the mail was actually being delivered.
|
||||||
|
def call(email)
|
||||||
|
preview = new
|
||||||
|
message = preview.public_send(email)
|
||||||
|
inform_preview_interceptors(message)
|
||||||
|
message
|
||||||
|
end
|
||||||
|
|
||||||
|
# Returns all of the available email previews.
|
||||||
|
def emails
|
||||||
|
public_instance_methods(false).map(&:to_s).sort
|
||||||
|
end
|
||||||
|
|
||||||
|
# Returns true if the email exists.
|
||||||
|
def email_exists?(email)
|
||||||
|
emails.include?(email)
|
||||||
|
end
|
||||||
|
|
||||||
|
# Returns true if the preview exists.
|
||||||
|
def exists?(preview)
|
||||||
|
all.any? { |p| p.preview_name == preview }
|
||||||
|
end
|
||||||
|
|
||||||
|
# Find a mailer preview by its underscored class name.
|
||||||
|
def find(preview)
|
||||||
|
all.find { |p| p.preview_name == preview }
|
||||||
|
end
|
||||||
|
|
||||||
|
# Returns the underscored name of the mailer preview without the suffix.
|
||||||
|
def preview_name
|
||||||
|
name.sub(/Preview$/, "").underscore
|
||||||
|
end
|
||||||
|
|
||||||
|
private
|
||||||
|
def load_previews
|
||||||
|
if preview_path
|
||||||
|
Dir["#{preview_path}/**/*_preview.rb"].each { |file| require_dependency file }
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
def preview_path
|
||||||
|
Base.preview_path
|
||||||
|
end
|
||||||
|
|
||||||
|
def show_previews
|
||||||
|
Base.show_previews
|
||||||
|
end
|
||||||
|
|
||||||
|
def inform_preview_interceptors(message)
|
||||||
|
Base.preview_interceptors.each do |interceptor|
|
||||||
|
interceptor.previewing_email(message)
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
74
debian/gems-compat/actionmailer-5.1.6.1/lib/action_mailer/railtie.rb
vendored
Normal file
74
debian/gems-compat/actionmailer-5.1.6.1/lib/action_mailer/railtie.rb
vendored
Normal file
|
@ -0,0 +1,74 @@
|
||||||
|
require "active_job/railtie"
|
||||||
|
require "action_mailer"
|
||||||
|
require "rails"
|
||||||
|
require "abstract_controller/railties/routes_helpers"
|
||||||
|
|
||||||
|
module ActionMailer
|
||||||
|
class Railtie < Rails::Railtie # :nodoc:
|
||||||
|
config.action_mailer = ActiveSupport::OrderedOptions.new
|
||||||
|
config.eager_load_namespaces << ActionMailer
|
||||||
|
|
||||||
|
initializer "action_mailer.logger" do
|
||||||
|
ActiveSupport.on_load(:action_mailer) { self.logger ||= Rails.logger }
|
||||||
|
end
|
||||||
|
|
||||||
|
initializer "action_mailer.set_configs" do |app|
|
||||||
|
paths = app.config.paths
|
||||||
|
options = app.config.action_mailer
|
||||||
|
|
||||||
|
if app.config.force_ssl
|
||||||
|
options.default_url_options ||= {}
|
||||||
|
options.default_url_options[:protocol] ||= "https"
|
||||||
|
end
|
||||||
|
|
||||||
|
options.assets_dir ||= paths["public"].first
|
||||||
|
options.javascripts_dir ||= paths["public/javascripts"].first
|
||||||
|
options.stylesheets_dir ||= paths["public/stylesheets"].first
|
||||||
|
options.show_previews = Rails.env.development? if options.show_previews.nil?
|
||||||
|
options.cache_store ||= Rails.cache
|
||||||
|
|
||||||
|
if options.show_previews
|
||||||
|
options.preview_path ||= defined?(Rails.root) ? "#{Rails.root}/test/mailers/previews" : nil
|
||||||
|
end
|
||||||
|
|
||||||
|
# make sure readers methods get compiled
|
||||||
|
options.asset_host ||= app.config.asset_host
|
||||||
|
options.relative_url_root ||= app.config.relative_url_root
|
||||||
|
|
||||||
|
ActiveSupport.on_load(:action_mailer) do
|
||||||
|
include AbstractController::UrlFor
|
||||||
|
extend ::AbstractController::Railties::RoutesHelpers.with(app.routes, false)
|
||||||
|
include app.routes.mounted_helpers
|
||||||
|
|
||||||
|
register_interceptors(options.delete(:interceptors))
|
||||||
|
register_preview_interceptors(options.delete(:preview_interceptors))
|
||||||
|
register_observers(options.delete(:observers))
|
||||||
|
|
||||||
|
options.each { |k, v| send("#{k}=", v) }
|
||||||
|
end
|
||||||
|
|
||||||
|
ActiveSupport.on_load(:action_dispatch_integration_test) { include ActionMailer::TestCase::ClearTestDeliveries }
|
||||||
|
end
|
||||||
|
|
||||||
|
initializer "action_mailer.compile_config_methods" do
|
||||||
|
ActiveSupport.on_load(:action_mailer) do
|
||||||
|
config.compile_methods! if config.respond_to?(:compile_methods!)
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
config.after_initialize do |app|
|
||||||
|
options = app.config.action_mailer
|
||||||
|
|
||||||
|
if options.show_previews
|
||||||
|
app.routes.prepend do
|
||||||
|
get "/rails/mailers" => "rails/mailers#index", internal: true
|
||||||
|
get "/rails/mailers/*path" => "rails/mailers#preview", internal: true
|
||||||
|
end
|
||||||
|
|
||||||
|
if options.preview_path
|
||||||
|
ActiveSupport::Dependencies.autoload_paths << options.preview_path
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
27
debian/gems-compat/actionmailer-5.1.6.1/lib/action_mailer/rescuable.rb
vendored
Normal file
27
debian/gems-compat/actionmailer-5.1.6.1/lib/action_mailer/rescuable.rb
vendored
Normal file
|
@ -0,0 +1,27 @@
|
||||||
|
module ActionMailer #:nodoc:
|
||||||
|
# Provides `rescue_from` for mailers. Wraps mailer action processing,
|
||||||
|
# mail job processing, and mail delivery.
|
||||||
|
module Rescuable
|
||||||
|
extend ActiveSupport::Concern
|
||||||
|
include ActiveSupport::Rescuable
|
||||||
|
|
||||||
|
class_methods do
|
||||||
|
def handle_exception(exception) #:nodoc:
|
||||||
|
rescue_with_handler(exception) || raise(exception)
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
def handle_exceptions #:nodoc:
|
||||||
|
yield
|
||||||
|
rescue => exception
|
||||||
|
rescue_with_handler(exception) || raise
|
||||||
|
end
|
||||||
|
|
||||||
|
private
|
||||||
|
def process(*)
|
||||||
|
handle_exceptions do
|
||||||
|
super
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
121
debian/gems-compat/actionmailer-5.1.6.1/lib/action_mailer/test_case.rb
vendored
Normal file
121
debian/gems-compat/actionmailer-5.1.6.1/lib/action_mailer/test_case.rb
vendored
Normal file
|
@ -0,0 +1,121 @@
|
||||||
|
require "active_support/test_case"
|
||||||
|
require "rails-dom-testing"
|
||||||
|
|
||||||
|
module ActionMailer
|
||||||
|
class NonInferrableMailerError < ::StandardError
|
||||||
|
def initialize(name)
|
||||||
|
super "Unable to determine the mailer to test from #{name}. " \
|
||||||
|
"You'll need to specify it using tests YourMailer in your " \
|
||||||
|
"test case definition"
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
class TestCase < ActiveSupport::TestCase
|
||||||
|
module ClearTestDeliveries
|
||||||
|
extend ActiveSupport::Concern
|
||||||
|
|
||||||
|
included do
|
||||||
|
setup :clear_test_deliveries
|
||||||
|
teardown :clear_test_deliveries
|
||||||
|
end
|
||||||
|
|
||||||
|
private
|
||||||
|
|
||||||
|
def clear_test_deliveries
|
||||||
|
if ActionMailer::Base.delivery_method == :test
|
||||||
|
ActionMailer::Base.deliveries.clear
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
module Behavior
|
||||||
|
extend ActiveSupport::Concern
|
||||||
|
|
||||||
|
include ActiveSupport::Testing::ConstantLookup
|
||||||
|
include TestHelper
|
||||||
|
include Rails::Dom::Testing::Assertions::SelectorAssertions
|
||||||
|
include Rails::Dom::Testing::Assertions::DomAssertions
|
||||||
|
|
||||||
|
included do
|
||||||
|
class_attribute :_mailer_class
|
||||||
|
setup :initialize_test_deliveries
|
||||||
|
setup :set_expected_mail
|
||||||
|
teardown :restore_test_deliveries
|
||||||
|
ActiveSupport.run_load_hooks(:action_mailer_test_case, self)
|
||||||
|
end
|
||||||
|
|
||||||
|
module ClassMethods
|
||||||
|
def tests(mailer)
|
||||||
|
case mailer
|
||||||
|
when String, Symbol
|
||||||
|
self._mailer_class = mailer.to_s.camelize.constantize
|
||||||
|
when Module
|
||||||
|
self._mailer_class = mailer
|
||||||
|
else
|
||||||
|
raise NonInferrableMailerError.new(mailer)
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
def mailer_class
|
||||||
|
if mailer = _mailer_class
|
||||||
|
mailer
|
||||||
|
else
|
||||||
|
tests determine_default_mailer(name)
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
def determine_default_mailer(name)
|
||||||
|
mailer = determine_constant_from_test_name(name) do |constant|
|
||||||
|
Class === constant && constant < ActionMailer::Base
|
||||||
|
end
|
||||||
|
raise NonInferrableMailerError.new(name) if mailer.nil?
|
||||||
|
mailer
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
private
|
||||||
|
|
||||||
|
def initialize_test_deliveries
|
||||||
|
set_delivery_method :test
|
||||||
|
@old_perform_deliveries = ActionMailer::Base.perform_deliveries
|
||||||
|
ActionMailer::Base.perform_deliveries = true
|
||||||
|
ActionMailer::Base.deliveries.clear
|
||||||
|
end
|
||||||
|
|
||||||
|
def restore_test_deliveries
|
||||||
|
restore_delivery_method
|
||||||
|
ActionMailer::Base.perform_deliveries = @old_perform_deliveries
|
||||||
|
end
|
||||||
|
|
||||||
|
def set_delivery_method(method)
|
||||||
|
@old_delivery_method = ActionMailer::Base.delivery_method
|
||||||
|
ActionMailer::Base.delivery_method = method
|
||||||
|
end
|
||||||
|
|
||||||
|
def restore_delivery_method
|
||||||
|
ActionMailer::Base.deliveries.clear
|
||||||
|
ActionMailer::Base.delivery_method = @old_delivery_method
|
||||||
|
end
|
||||||
|
|
||||||
|
def set_expected_mail
|
||||||
|
@expected = Mail.new
|
||||||
|
@expected.content_type ["text", "plain", { "charset" => charset }]
|
||||||
|
@expected.mime_version = "1.0"
|
||||||
|
end
|
||||||
|
|
||||||
|
def charset
|
||||||
|
"UTF-8"
|
||||||
|
end
|
||||||
|
|
||||||
|
def encode(subject)
|
||||||
|
Mail::Encodings.q_value_encode(subject, charset)
|
||||||
|
end
|
||||||
|
|
||||||
|
def read_fixture(action)
|
||||||
|
IO.readlines(File.join(Rails.root, "test", "fixtures", self.class.mailer_class.name.underscore, action))
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
include Behavior
|
||||||
|
end
|
||||||
|
end
|
113
debian/gems-compat/actionmailer-5.1.6.1/lib/action_mailer/test_helper.rb
vendored
Normal file
113
debian/gems-compat/actionmailer-5.1.6.1/lib/action_mailer/test_helper.rb
vendored
Normal file
|
@ -0,0 +1,113 @@
|
||||||
|
require "active_job"
|
||||||
|
|
||||||
|
module ActionMailer
|
||||||
|
# Provides helper methods for testing Action Mailer, including #assert_emails
|
||||||
|
# and #assert_no_emails.
|
||||||
|
module TestHelper
|
||||||
|
include ActiveJob::TestHelper
|
||||||
|
|
||||||
|
# Asserts that the number of emails sent matches the given number.
|
||||||
|
#
|
||||||
|
# def test_emails
|
||||||
|
# assert_emails 0
|
||||||
|
# ContactMailer.welcome.deliver_now
|
||||||
|
# assert_emails 1
|
||||||
|
# ContactMailer.welcome.deliver_now
|
||||||
|
# assert_emails 2
|
||||||
|
# end
|
||||||
|
#
|
||||||
|
# If a block is passed, that block should cause the specified number of
|
||||||
|
# emails to be sent.
|
||||||
|
#
|
||||||
|
# def test_emails_again
|
||||||
|
# assert_emails 1 do
|
||||||
|
# ContactMailer.welcome.deliver_now
|
||||||
|
# end
|
||||||
|
#
|
||||||
|
# assert_emails 2 do
|
||||||
|
# ContactMailer.welcome.deliver_now
|
||||||
|
# ContactMailer.welcome.deliver_now
|
||||||
|
# end
|
||||||
|
# end
|
||||||
|
def assert_emails(number)
|
||||||
|
if block_given?
|
||||||
|
original_count = ActionMailer::Base.deliveries.size
|
||||||
|
yield
|
||||||
|
new_count = ActionMailer::Base.deliveries.size
|
||||||
|
assert_equal number, new_count - original_count, "#{number} emails expected, but #{new_count - original_count} were sent"
|
||||||
|
else
|
||||||
|
assert_equal number, ActionMailer::Base.deliveries.size
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
# Asserts that no emails have been sent.
|
||||||
|
#
|
||||||
|
# def test_emails
|
||||||
|
# assert_no_emails
|
||||||
|
# ContactMailer.welcome.deliver_now
|
||||||
|
# assert_emails 1
|
||||||
|
# end
|
||||||
|
#
|
||||||
|
# If a block is passed, that block should not cause any emails to be sent.
|
||||||
|
#
|
||||||
|
# def test_emails_again
|
||||||
|
# assert_no_emails do
|
||||||
|
# # No emails should be sent from this block
|
||||||
|
# end
|
||||||
|
# end
|
||||||
|
#
|
||||||
|
# Note: This assertion is simply a shortcut for:
|
||||||
|
#
|
||||||
|
# assert_emails 0
|
||||||
|
def assert_no_emails(&block)
|
||||||
|
assert_emails 0, &block
|
||||||
|
end
|
||||||
|
|
||||||
|
# Asserts that the number of emails enqueued for later delivery matches
|
||||||
|
# the given number.
|
||||||
|
#
|
||||||
|
# def test_emails
|
||||||
|
# assert_enqueued_emails 0
|
||||||
|
# ContactMailer.welcome.deliver_later
|
||||||
|
# assert_enqueued_emails 1
|
||||||
|
# ContactMailer.welcome.deliver_later
|
||||||
|
# assert_enqueued_emails 2
|
||||||
|
# end
|
||||||
|
#
|
||||||
|
# If a block is passed, that block should cause the specified number of
|
||||||
|
# emails to be enqueued.
|
||||||
|
#
|
||||||
|
# def test_emails_again
|
||||||
|
# assert_enqueued_emails 1 do
|
||||||
|
# ContactMailer.welcome.deliver_later
|
||||||
|
# end
|
||||||
|
#
|
||||||
|
# assert_enqueued_emails 2 do
|
||||||
|
# ContactMailer.welcome.deliver_later
|
||||||
|
# ContactMailer.welcome.deliver_later
|
||||||
|
# end
|
||||||
|
# end
|
||||||
|
def assert_enqueued_emails(number, &block)
|
||||||
|
assert_enqueued_jobs number, only: [ ActionMailer::DeliveryJob, ActionMailer::Parameterized::DeliveryJob ], &block
|
||||||
|
end
|
||||||
|
|
||||||
|
# Asserts that no emails are enqueued for later delivery.
|
||||||
|
#
|
||||||
|
# def test_no_emails
|
||||||
|
# assert_no_enqueued_emails
|
||||||
|
# ContactMailer.welcome.deliver_later
|
||||||
|
# assert_enqueued_emails 1
|
||||||
|
# end
|
||||||
|
#
|
||||||
|
# If a block is provided, it should not cause any emails to be enqueued.
|
||||||
|
#
|
||||||
|
# def test_no_emails
|
||||||
|
# assert_no_enqueued_emails do
|
||||||
|
# # No emails should be enqueued from this block
|
||||||
|
# end
|
||||||
|
# end
|
||||||
|
def assert_no_enqueued_emails(&block)
|
||||||
|
assert_no_enqueued_jobs only: [ ActionMailer::DeliveryJob, ActionMailer::Parameterized::DeliveryJob ], &block
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
9
debian/gems-compat/actionmailer-5.1.6.1/lib/action_mailer/version.rb
vendored
Normal file
9
debian/gems-compat/actionmailer-5.1.6.1/lib/action_mailer/version.rb
vendored
Normal file
|
@ -0,0 +1,9 @@
|
||||||
|
require_relative "gem_version"
|
||||||
|
|
||||||
|
module ActionMailer
|
||||||
|
# Returns the version of the currently loaded Action Mailer as a
|
||||||
|
# <tt>Gem::Version</tt>.
|
||||||
|
def self.version
|
||||||
|
gem_version
|
||||||
|
end
|
||||||
|
end
|
17
debian/gems-compat/actionmailer-5.1.6.1/lib/rails/generators/mailer/USAGE
vendored
Normal file
17
debian/gems-compat/actionmailer-5.1.6.1/lib/rails/generators/mailer/USAGE
vendored
Normal file
|
@ -0,0 +1,17 @@
|
||||||
|
Description:
|
||||||
|
============
|
||||||
|
Stubs out a new mailer and its views. Passes the mailer name, either
|
||||||
|
CamelCased or under_scored, and an optional list of emails as arguments.
|
||||||
|
|
||||||
|
This generates a mailer class in app/mailers and invokes your template
|
||||||
|
engine and test framework generators.
|
||||||
|
|
||||||
|
Example:
|
||||||
|
========
|
||||||
|
rails generate mailer Notifications signup forgot_password invoice
|
||||||
|
|
||||||
|
creates a Notifications mailer class, views, and test:
|
||||||
|
Mailer: app/mailers/notifications_mailer.rb
|
||||||
|
Views: app/views/notifications_mailer/signup.text.erb [...]
|
||||||
|
Test: test/mailers/notifications_mailer_test.rb
|
||||||
|
|
36
debian/gems-compat/actionmailer-5.1.6.1/lib/rails/generators/mailer/mailer_generator.rb
vendored
Normal file
36
debian/gems-compat/actionmailer-5.1.6.1/lib/rails/generators/mailer/mailer_generator.rb
vendored
Normal file
|
@ -0,0 +1,36 @@
|
||||||
|
module Rails
|
||||||
|
module Generators
|
||||||
|
class MailerGenerator < NamedBase
|
||||||
|
source_root File.expand_path("../templates", __FILE__)
|
||||||
|
|
||||||
|
argument :actions, type: :array, default: [], banner: "method method"
|
||||||
|
|
||||||
|
check_class_collision suffix: "Mailer"
|
||||||
|
|
||||||
|
def create_mailer_file
|
||||||
|
template "mailer.rb", File.join("app/mailers", class_path, "#{file_name}_mailer.rb")
|
||||||
|
|
||||||
|
in_root do
|
||||||
|
if behavior == :invoke && !File.exist?(application_mailer_file_name)
|
||||||
|
template "application_mailer.rb", application_mailer_file_name
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
hook_for :template_engine, :test_framework
|
||||||
|
|
||||||
|
private
|
||||||
|
def file_name # :doc:
|
||||||
|
@_file_name ||= super.gsub(/_mailer/i, "")
|
||||||
|
end
|
||||||
|
|
||||||
|
def application_mailer_file_name
|
||||||
|
@_application_mailer_file_name ||= if mountable_engine?
|
||||||
|
"app/mailers/#{namespaced_path}/application_mailer.rb"
|
||||||
|
else
|
||||||
|
"app/mailers/application_mailer.rb"
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
|
@ -0,0 +1,6 @@
|
||||||
|
<% module_namespacing do -%>
|
||||||
|
class ApplicationMailer < ActionMailer::Base
|
||||||
|
default from: 'from@example.com'
|
||||||
|
layout 'mailer'
|
||||||
|
end
|
||||||
|
<% end %>
|
17
debian/gems-compat/actionmailer-5.1.6.1/lib/rails/generators/mailer/templates/mailer.rb
vendored
Normal file
17
debian/gems-compat/actionmailer-5.1.6.1/lib/rails/generators/mailer/templates/mailer.rb
vendored
Normal file
|
@ -0,0 +1,17 @@
|
||||||
|
<% module_namespacing do -%>
|
||||||
|
class <%= class_name %>Mailer < ApplicationMailer
|
||||||
|
<% actions.each do |action| -%>
|
||||||
|
|
||||||
|
# Subject can be set in your I18n file at config/locales/en.yml
|
||||||
|
# with the following lookup:
|
||||||
|
#
|
||||||
|
# en.<%= file_path.tr("/",".") %>_mailer.<%= action %>.subject
|
||||||
|
#
|
||||||
|
def <%= action %>
|
||||||
|
@greeting = "Hi"
|
||||||
|
|
||||||
|
mail to: "to@example.org"
|
||||||
|
end
|
||||||
|
<% end -%>
|
||||||
|
end
|
||||||
|
<% end -%>
|
549
debian/gems-compat/actionpack-5.1.6.1/CHANGELOG.md
vendored
Normal file
549
debian/gems-compat/actionpack-5.1.6.1/CHANGELOG.md
vendored
Normal file
|
@ -0,0 +1,549 @@
|
||||||
|
## Rails 5.1.6.1 (November 27, 2018) ##
|
||||||
|
|
||||||
|
* No changes.
|
||||||
|
|
||||||
|
|
||||||
|
## Rails 5.1.6 (March 29, 2018) ##
|
||||||
|
|
||||||
|
* Check exclude before flagging cookies as secure.
|
||||||
|
|
||||||
|
*Catherine Khuu*
|
||||||
|
|
||||||
|
|
||||||
|
## Rails 5.1.5 (February 14, 2018) ##
|
||||||
|
|
||||||
|
* Fix optimized url helpers when using relative url root
|
||||||
|
|
||||||
|
Fixes #31220.
|
||||||
|
|
||||||
|
*Andrew White*
|
||||||
|
|
||||||
|
* Ensure dev and prod puma configs do not clobber `ActionDispatch::SystemTesting` defaults. Adds workers: 0 and daemon: false
|
||||||
|
|
||||||
|
*Max Schwenk*
|
||||||
|
|
||||||
|
## Rails 5.1.4 (September 07, 2017) ##
|
||||||
|
|
||||||
|
* Make `take_failed_screenshot` work within engine.
|
||||||
|
|
||||||
|
Fixes #30405.
|
||||||
|
|
||||||
|
*Yuji Yaginuma*
|
||||||
|
|
||||||
|
## Rails 5.1.4.rc1 (August 24, 2017) ##
|
||||||
|
|
||||||
|
* No changes.
|
||||||
|
|
||||||
|
|
||||||
|
## Rails 5.1.3 (August 03, 2017) ##
|
||||||
|
|
||||||
|
* No changes.
|
||||||
|
|
||||||
|
|
||||||
|
## Rails 5.1.3.rc3 (July 31, 2017) ##
|
||||||
|
|
||||||
|
* No changes.
|
||||||
|
|
||||||
|
|
||||||
|
## Rails 5.1.3.rc2 (July 25, 2017) ##
|
||||||
|
|
||||||
|
* No changes.
|
||||||
|
|
||||||
|
|
||||||
|
## Rails 5.1.3.rc1 (July 19, 2017) ##
|
||||||
|
|
||||||
|
* No changes.
|
||||||
|
|
||||||
|
|
||||||
|
## Rails 5.1.2 (June 26, 2017) ##
|
||||||
|
|
||||||
|
* Fallback `ActionController::Parameters#to_s` to `Hash#to_s`.
|
||||||
|
|
||||||
|
*Kir Shatrov*
|
||||||
|
|
||||||
|
* `driven_by` now registers poltergeist and capybara-webkit
|
||||||
|
|
||||||
|
If driver poltergeist or capybara-webkit is set for System Tests,
|
||||||
|
`driven_by` will register the driver and set additional options passed via
|
||||||
|
`:options` param.
|
||||||
|
|
||||||
|
Refer to drivers documentation to learn what options can be passed.
|
||||||
|
|
||||||
|
*Mario Chavez*
|
||||||
|
|
||||||
|
## Rails 5.1.1 (May 12, 2017) ##
|
||||||
|
|
||||||
|
* No changes.
|
||||||
|
|
||||||
|
|
||||||
|
## Rails 5.1.0 (April 27, 2017) ##
|
||||||
|
|
||||||
|
* Raise exception when calling `to_h` and `to_hash` in an unpermitted Parameters.
|
||||||
|
|
||||||
|
Before we returned either an empty hash or only the always permitted parameters
|
||||||
|
(`:controller` and `:action` by default).
|
||||||
|
|
||||||
|
The previous behavior was dangerous because in order to get the attributes users
|
||||||
|
usually fallback to use `to_unsafe_h that` could potentially introduce security issues.
|
||||||
|
|
||||||
|
*Rafael Mendonça França*
|
||||||
|
|
||||||
|
* Deprecate `config.action_controller.raise_on_unfiltered_parameters`.
|
||||||
|
|
||||||
|
This option has no effect in Rails 5.1.
|
||||||
|
|
||||||
|
*Rafael Mendonça França*
|
||||||
|
|
||||||
|
* Use more specific check for :format in route path
|
||||||
|
|
||||||
|
The current check for whether to add an optional format to the path is very lax
|
||||||
|
and will match things like `:format_id` where there are nested resources, e.g:
|
||||||
|
|
||||||
|
``` ruby
|
||||||
|
resources :formats do
|
||||||
|
resources :items
|
||||||
|
end
|
||||||
|
```
|
||||||
|
|
||||||
|
Fix this by using a more restrictive regex pattern that looks for the patterns
|
||||||
|
`(.:format)`, `.:format` or `/` at the end of the path. Note that we need to
|
||||||
|
allow for multiple closing parenthesis since the route may be of this form:
|
||||||
|
|
||||||
|
``` ruby
|
||||||
|
get "/books(/:action(.:format))", controller: "books"
|
||||||
|
```
|
||||||
|
|
||||||
|
This probably isn't what's intended since it means that the default index action
|
||||||
|
route doesn't support a format but we have a test for it so we need to allow it.
|
||||||
|
|
||||||
|
Fixes #28517.
|
||||||
|
|
||||||
|
*Andrew White*
|
||||||
|
|
||||||
|
* Add `action_controller_api` and `action_controller_base` load hooks to be called in `ActiveSupport.on_load`
|
||||||
|
|
||||||
|
`ActionController::Base` and `ActionController::API` have differing implementations. This means that
|
||||||
|
the one umbrella hook `action_controller` is not able to address certain situations where a method
|
||||||
|
may not exist in a certain implementation.
|
||||||
|
|
||||||
|
This is fixed by adding two new hooks so you can target `ActionController::Base` vs `ActionController::API`
|
||||||
|
|
||||||
|
Fixes #27013.
|
||||||
|
|
||||||
|
*Julian Nadeau*
|
||||||
|
|
||||||
|
* Don't include default headers in `ActionController::Metal` responses
|
||||||
|
|
||||||
|
The commit e16afe6 introduced an unintentional change of behavior where the default
|
||||||
|
headers were included in responses from `ActionController::Metal` based controllers.
|
||||||
|
This is now reverted to the previous behavior of having no default headers.
|
||||||
|
|
||||||
|
Fixes #25820.
|
||||||
|
|
||||||
|
*Jon Moss*
|
||||||
|
|
||||||
|
* Fix `NameError` raised in `ActionController::Renderer#with_defaults`
|
||||||
|
|
||||||
|
*Hiroyuki Ishii*
|
||||||
|
|
||||||
|
* Added `#reverse_merge` and `#reverse_merge!` methods to `ActionController::Parameters`
|
||||||
|
|
||||||
|
*Edouard Chin*, *Mitsutaka Mimura*
|
||||||
|
|
||||||
|
* Fix malformed URLS when using `ApplicationController.renderer`
|
||||||
|
|
||||||
|
The Rack environment variable `rack.url_scheme` was not being set so `scheme` was
|
||||||
|
returning `nil`. This caused URLs to be malformed with the default settings.
|
||||||
|
Fix this by setting `rack.url_scheme` when the environment is normalized.
|
||||||
|
|
||||||
|
Fixes #28151.
|
||||||
|
|
||||||
|
*George Vrettos*
|
||||||
|
|
||||||
|
* Commit flash changes when using a redirect route.
|
||||||
|
|
||||||
|
Fixes #27992.
|
||||||
|
|
||||||
|
*Andrew White*
|
||||||
|
|
||||||
|
* Prefer `remove_method` over `undef_method` when reloading routes
|
||||||
|
|
||||||
|
When `undef_method` is used it prevents access to other implementations of that
|
||||||
|
url helper in the ancestor chain so use `remove_method` instead to restore access.
|
||||||
|
|
||||||
|
*Andrew White*
|
||||||
|
|
||||||
|
* Add the `resolve` method to the routing DSL
|
||||||
|
|
||||||
|
This new method allows customization of the polymorphic mapping of models:
|
||||||
|
|
||||||
|
``` ruby
|
||||||
|
resource :basket
|
||||||
|
resolve("Basket") { [:basket] }
|
||||||
|
```
|
||||||
|
|
||||||
|
``` erb
|
||||||
|
<%= form_for @basket do |form| %>
|
||||||
|
<!-- basket form -->
|
||||||
|
<% end %>
|
||||||
|
```
|
||||||
|
|
||||||
|
This generates the correct singular URL for the form instead of the default
|
||||||
|
resources member url, e.g. `/basket` vs. `/basket/:id`.
|
||||||
|
|
||||||
|
Fixes #1769.
|
||||||
|
|
||||||
|
*Andrew White*
|
||||||
|
|
||||||
|
* Add the `direct` method to the routing DSL
|
||||||
|
|
||||||
|
This new method allows creation of custom url helpers, e.g:
|
||||||
|
|
||||||
|
``` ruby
|
||||||
|
direct(:apple) { "http://www.apple.com" }
|
||||||
|
|
||||||
|
>> apple_url
|
||||||
|
=> "http://www.apple.com"
|
||||||
|
```
|
||||||
|
|
||||||
|
This has the advantage of being available everywhere url helpers are available
|
||||||
|
unlike custom url helpers defined in helper modules, etc.
|
||||||
|
|
||||||
|
*Andrew White*
|
||||||
|
|
||||||
|
* Add `ActionDispatch::SystemTestCase` to Action Pack
|
||||||
|
|
||||||
|
Adds Capybara integration directly into Rails through Action Pack!
|
||||||
|
|
||||||
|
See PR [#26703](https://github.com/rails/rails/pull/26703)
|
||||||
|
|
||||||
|
*Eileen M. Uchitelle*
|
||||||
|
|
||||||
|
* Remove deprecated `.to_prepare`, `.to_cleanup`, `.prepare!` and `.cleanup!` from `ActionDispatch::Reloader`.
|
||||||
|
|
||||||
|
*Rafael Mendonça França*
|
||||||
|
|
||||||
|
* Remove deprecated `ActionDispatch::Callbacks.to_prepare` and `ActionDispatch::Callbacks.to_cleanup`.
|
||||||
|
|
||||||
|
*Rafael Mendonça França*
|
||||||
|
|
||||||
|
* Remove deprecated `ActionController::Metal.call`.
|
||||||
|
|
||||||
|
*Rafael Mendonça França*
|
||||||
|
|
||||||
|
* Remove deprecated `ActionController::Metal#env`.
|
||||||
|
|
||||||
|
*Rafael Mendonça França*
|
||||||
|
|
||||||
|
* Make `with_routing` test helper work when testing controllers inheriting from `ActionController::API`
|
||||||
|
|
||||||
|
*Julia López*
|
||||||
|
|
||||||
|
* Use accept header in integration tests with `as: :json`
|
||||||
|
|
||||||
|
Instead of appending the `format` to the request path, Rails will figure
|
||||||
|
out the format from the header instead.
|
||||||
|
|
||||||
|
This allows devs to use `:as` on routes that don't have a format.
|
||||||
|
|
||||||
|
Fixes #27144.
|
||||||
|
|
||||||
|
*Kasper Timm Hansen*
|
||||||
|
|
||||||
|
* Reset a new session directly after its creation in `ActionDispatch::IntegrationTest#open_session`.
|
||||||
|
|
||||||
|
Fixes #22742.
|
||||||
|
|
||||||
|
*Tawan Sierek*
|
||||||
|
|
||||||
|
* Fixes incorrect output from `rails routes` when using singular resources.
|
||||||
|
|
||||||
|
Fixes #26606.
|
||||||
|
|
||||||
|
*Erick Reyna*
|
||||||
|
|
||||||
|
* Fixes multiple calls to `logger.fatal` instead of a single call,
|
||||||
|
for every line in an exception backtrace, when printing trace
|
||||||
|
from `DebugExceptions` middleware.
|
||||||
|
|
||||||
|
Fixes #26134.
|
||||||
|
|
||||||
|
*Vipul A M*
|
||||||
|
|
||||||
|
* Add support for arbitrary hashes in strong parameters:
|
||||||
|
|
||||||
|
```ruby
|
||||||
|
params.permit(preferences: {})
|
||||||
|
```
|
||||||
|
|
||||||
|
*Xavier Noria*
|
||||||
|
|
||||||
|
* Add `ActionController::Parameters#merge!`, which behaves the same as `Hash#merge!`.
|
||||||
|
|
||||||
|
*Yuji Yaginuma*
|
||||||
|
|
||||||
|
* Allow keys not found in `RACK_KEY_TRANSLATION` for setting the environment when rendering
|
||||||
|
arbitrary templates.
|
||||||
|
|
||||||
|
*Sammy Larbi*
|
||||||
|
|
||||||
|
* Remove deprecated support to non-keyword arguments in `ActionDispatch::IntegrationTest#process`,
|
||||||
|
`#get`, `#post`, `#patch`, `#put`, `#delete`, and `#head`.
|
||||||
|
|
||||||
|
*Rafael Mendonça França*
|
||||||
|
|
||||||
|
* Remove deprecated `ActionDispatch::IntegrationTest#*_via_redirect`.
|
||||||
|
|
||||||
|
*Rafael Mendonça França*
|
||||||
|
|
||||||
|
* Remove deprecated `ActionDispatch::IntegrationTest#xml_http_request`.
|
||||||
|
|
||||||
|
*Rafael Mendonça França*
|
||||||
|
|
||||||
|
* Remove deprecated support for passing `:path` and route path as strings in `ActionDispatch::Routing::Mapper#match`.
|
||||||
|
|
||||||
|
*Rafael Mendonça França*
|
||||||
|
|
||||||
|
* Remove deprecated support for passing path as `nil` in `ActionDispatch::Routing::Mapper#match`.
|
||||||
|
|
||||||
|
*Rafael Mendonça França*
|
||||||
|
|
||||||
|
* Remove deprecated `cache_control` argument from `ActionDispatch::Static#initialize`.
|
||||||
|
|
||||||
|
*Rafael Mendonça França*
|
||||||
|
|
||||||
|
* Remove deprecated support to passing strings or symbols to the middleware stack.
|
||||||
|
|
||||||
|
*Rafael Mendonça França*
|
||||||
|
|
||||||
|
* Change HSTS subdomain to true.
|
||||||
|
|
||||||
|
*Rafael Mendonça França*
|
||||||
|
|
||||||
|
* Remove deprecated `host` and `port` ssl options.
|
||||||
|
|
||||||
|
*Rafael Mendonça França*
|
||||||
|
|
||||||
|
* Remove deprecated `const_error` argument in
|
||||||
|
`ActionDispatch::Session::SessionRestoreError#initialize`.
|
||||||
|
|
||||||
|
*Rafael Mendonça França*
|
||||||
|
|
||||||
|
* Remove deprecated `#original_exception` in `ActionDispatch::Session::SessionRestoreError`.
|
||||||
|
|
||||||
|
*Rafael Mendonça França*
|
||||||
|
|
||||||
|
* Deprecate `ActionDispatch::ParamsParser::ParseError` in favor of
|
||||||
|
`ActionDispatch::Http::Parameters::ParseError`.
|
||||||
|
|
||||||
|
*Rafael Mendonça França*
|
||||||
|
|
||||||
|
* Remove deprecated `ActionDispatch::ParamsParser`.
|
||||||
|
|
||||||
|
*Rafael Mendonça França*
|
||||||
|
|
||||||
|
* Remove deprecated `original_exception` and `message` arguments in
|
||||||
|
`ActionDispatch::ParamsParser::ParseError#initialize`.
|
||||||
|
|
||||||
|
*Rafael Mendonça França*
|
||||||
|
|
||||||
|
* Remove deprecated `#original_exception` in `ActionDispatch::ParamsParser::ParseError`.
|
||||||
|
|
||||||
|
*Rafael Mendonça França*
|
||||||
|
|
||||||
|
* Remove deprecated access to mime types through constants.
|
||||||
|
|
||||||
|
*Rafael Mendonça França*
|
||||||
|
|
||||||
|
* Remove deprecated support to non-keyword arguments in `ActionController::TestCase#process`,
|
||||||
|
`#get`, `#post`, `#patch`, `#put`, `#delete`, and `#head`.
|
||||||
|
|
||||||
|
*Rafael Mendonça França*
|
||||||
|
|
||||||
|
* Remove deprecated `xml_http_request` and `xhr` methods in `ActionController::TestCase`.
|
||||||
|
|
||||||
|
*Rafael Mendonça França*
|
||||||
|
|
||||||
|
* Remove deprecated methods in `ActionController::Parameters`.
|
||||||
|
|
||||||
|
*Rafael Mendonça França*
|
||||||
|
|
||||||
|
* Remove deprecated support to comparing a `ActionController::Parameters`
|
||||||
|
with a `Hash`.
|
||||||
|
|
||||||
|
*Rafael Mendonça França*
|
||||||
|
|
||||||
|
* Remove deprecated support to `:text` in `render`.
|
||||||
|
|
||||||
|
*Rafael Mendonça França*
|
||||||
|
|
||||||
|
* Remove deprecated support to `:nothing` in `render`.
|
||||||
|
|
||||||
|
*Rafael Mendonça França*
|
||||||
|
|
||||||
|
* Remove deprecated support to `:back` in `redirect_to`.
|
||||||
|
|
||||||
|
*Rafael Mendonça França*
|
||||||
|
|
||||||
|
* Remove deprecated support to passing status as option `head`.
|
||||||
|
|
||||||
|
*Rafael Mendonça França*
|
||||||
|
|
||||||
|
* Remove deprecated support to passing original exception to `ActionController::BadRequest`
|
||||||
|
and the `ActionController::BadRequest#original_exception` method.
|
||||||
|
|
||||||
|
*Rafael Mendonça França*
|
||||||
|
|
||||||
|
* Remove deprecated methods `skip_action_callback`, `skip_filter`, `before_filter`,
|
||||||
|
`prepend_before_filter`, `skip_before_filter`, `append_before_filter`, `around_filter`
|
||||||
|
`prepend_around_filter`, `skip_around_filter`, `append_around_filter`, `after_filter`,
|
||||||
|
`prepend_after_filter`, `skip_after_filter` and `append_after_filter`.
|
||||||
|
|
||||||
|
*Rafael Mendonça França*
|
||||||
|
|
||||||
|
* Show an "unmatched constraints" error when params fail to match constraints
|
||||||
|
on a matched route, rather than a "missing keys" error.
|
||||||
|
|
||||||
|
Fixes #26470.
|
||||||
|
|
||||||
|
*Chris Carter*
|
||||||
|
|
||||||
|
* Fix adding implicitly rendered template digests to ETags.
|
||||||
|
|
||||||
|
Fixes a case when modifying an implicitly rendered template for a
|
||||||
|
controller action using `fresh_when` or `stale?` would not result in a new
|
||||||
|
`ETag` value.
|
||||||
|
|
||||||
|
*Javan Makhmali*
|
||||||
|
|
||||||
|
* Make `fixture_file_upload` work in integration tests.
|
||||||
|
|
||||||
|
*Yuji Yaginuma*
|
||||||
|
|
||||||
|
* Add `to_param` to `ActionController::Parameters` deprecations.
|
||||||
|
|
||||||
|
In the future `ActionController::Parameters` are discouraged from being used
|
||||||
|
in URLs without explicit whitelisting. Go through `to_h` to use `to_param`.
|
||||||
|
|
||||||
|
*Kir Shatrov*
|
||||||
|
|
||||||
|
* Fix nested multiple roots
|
||||||
|
|
||||||
|
The PR #20940 enabled the use of multiple roots with different constraints
|
||||||
|
at the top level but unfortunately didn't work when those roots were inside
|
||||||
|
a namespace and also broke the use of root inside a namespace after a top
|
||||||
|
level root was defined because the check for the existence of the named route
|
||||||
|
used the global :root name and not the namespaced name.
|
||||||
|
|
||||||
|
This is fixed by using the name_for_action method to expand the :root name to
|
||||||
|
the full namespaced name. We can pass nil for the second argument as we're not
|
||||||
|
dealing with resource definitions so don't need to handle the cases for edit
|
||||||
|
and new routes.
|
||||||
|
|
||||||
|
Fixes #26148.
|
||||||
|
|
||||||
|
*Ryo Hashimoto*, *Andrew White*
|
||||||
|
|
||||||
|
* Include the content of the flash in the auto-generated etag. This solves the following problem:
|
||||||
|
|
||||||
|
1. POST /messages
|
||||||
|
2. redirect_to messages_url, notice: 'Message was created'
|
||||||
|
3. GET /messages/1
|
||||||
|
4. GET /messages
|
||||||
|
|
||||||
|
Step 4 would before still include the flash message, even though it's no longer relevant,
|
||||||
|
because the etag cache was recorded with the flash in place and didn't change when it was gone.
|
||||||
|
|
||||||
|
*DHH*
|
||||||
|
|
||||||
|
* SSL: Changes redirect behavior for all non-GET and non-HEAD requests
|
||||||
|
(like POST/PUT/PATCH etc) to `http://` resources to redirect to `https://`
|
||||||
|
with a [307 status code](http://tools.ietf.org/html/rfc7231#section-6.4.7) instead of [301 status code](http://tools.ietf.org/html/rfc7231#section-6.4.2).
|
||||||
|
|
||||||
|
307 status code instructs the HTTP clients to preserve the original
|
||||||
|
request method while redirecting. It has been part of HTTP RFC since
|
||||||
|
1999 and is implemented/recognized by most (if not all) user agents.
|
||||||
|
|
||||||
|
# Before
|
||||||
|
POST http://example.com/articles (i.e. ArticlesContoller#create)
|
||||||
|
redirects to
|
||||||
|
GET https://example.com/articles (i.e. ArticlesContoller#index)
|
||||||
|
|
||||||
|
# After
|
||||||
|
POST http://example.com/articles (i.e. ArticlesContoller#create)
|
||||||
|
redirects to
|
||||||
|
POST https://example.com/articles (i.e. ArticlesContoller#create)
|
||||||
|
|
||||||
|
*Chirag Singhal*
|
||||||
|
|
||||||
|
* Add `:as` option to `ActionController:TestCase#process` and related methods.
|
||||||
|
|
||||||
|
Specifying `as: mime_type` allows the `CONTENT_TYPE` header to be specified
|
||||||
|
in controller tests without manually doing this through `@request.headers['CONTENT_TYPE']`.
|
||||||
|
|
||||||
|
*Everest Stefan Munro-Zeisberger*
|
||||||
|
|
||||||
|
* Show cache hits and misses when rendering partials.
|
||||||
|
|
||||||
|
Partials using the `cache` helper will show whether a render hit or missed
|
||||||
|
the cache:
|
||||||
|
|
||||||
|
```
|
||||||
|
Rendered messages/_message.html.erb in 1.2 ms [cache hit]
|
||||||
|
Rendered recordings/threads/_thread.html.erb in 1.5 ms [cache miss]
|
||||||
|
```
|
||||||
|
|
||||||
|
This removes the need for the old fragment cache logging:
|
||||||
|
|
||||||
|
```
|
||||||
|
Read fragment views/v1/2914079/v1/2914079/recordings/70182313-20160225015037000000/d0bdf2974e1ef6d31685c3b392ad0b74 (0.6ms)
|
||||||
|
Rendered messages/_message.html.erb in 1.2 ms [cache hit]
|
||||||
|
Write fragment views/v1/2914079/v1/2914079/recordings/70182313-20160225015037000000/3b4e249ac9d168c617e32e84b99218b5 (1.1ms)
|
||||||
|
Rendered recordings/threads/_thread.html.erb in 1.5 ms [cache miss]
|
||||||
|
```
|
||||||
|
|
||||||
|
Though that full output can be reenabled with
|
||||||
|
`config.action_controller.enable_fragment_cache_logging = true`.
|
||||||
|
|
||||||
|
*Stan Lo*
|
||||||
|
|
||||||
|
* Don't override the `Accept` header in integration tests when called with `xhr: true`.
|
||||||
|
|
||||||
|
Fixes #25859.
|
||||||
|
|
||||||
|
*David Chen*
|
||||||
|
|
||||||
|
* Fix `defaults` option for root route.
|
||||||
|
|
||||||
|
A regression from some refactoring for the 5.0 release, this change
|
||||||
|
fixes the use of `defaults` (default parameters) in the `root` routing method.
|
||||||
|
|
||||||
|
*Chris Arcand*
|
||||||
|
|
||||||
|
* Check `request.path_parameters` encoding at the point they're set.
|
||||||
|
|
||||||
|
Check for any non-UTF8 characters in path parameters at the point they're
|
||||||
|
set in `env`. Previously they were checked for when used to get a controller
|
||||||
|
class, but this meant routes that went directly to a Rack app, or skipped
|
||||||
|
controller instantiation for some other reason, had to defend against
|
||||||
|
non-UTF8 characters themselves.
|
||||||
|
|
||||||
|
*Grey Baker*
|
||||||
|
|
||||||
|
* Don't raise `ActionController::UnknownHttpMethod` from `ActionDispatch::Static`.
|
||||||
|
|
||||||
|
Pass `Rack::Request` objects to `ActionDispatch::FileHandler` to avoid it
|
||||||
|
raising `ActionController::UnknownHttpMethod`. If an unknown method is
|
||||||
|
passed, it should pass exception higher in the stack instead, once we've had a
|
||||||
|
chance to define exception handling behaviour.
|
||||||
|
|
||||||
|
*Grey Baker*
|
||||||
|
|
||||||
|
* Handle `Rack::QueryParser` errors in `ActionDispatch::ExceptionWrapper`.
|
||||||
|
|
||||||
|
Updated `ActionDispatch::ExceptionWrapper` to handle the Rack 2.0 namespace
|
||||||
|
for `ParameterTypeError` and `InvalidParameterError` errors.
|
||||||
|
|
||||||
|
*Grey Baker*
|
||||||
|
|
||||||
|
Please check [5-0-stable](https://github.com/rails/rails/blob/5-0-stable/actionpack/CHANGELOG.md) for previous changes.
|
21
debian/gems-compat/actionpack-5.1.6.1/MIT-LICENSE
vendored
Normal file
21
debian/gems-compat/actionpack-5.1.6.1/MIT-LICENSE
vendored
Normal file
|
@ -0,0 +1,21 @@
|
||||||
|
Copyright (c) 2004-2017 David Heinemeier Hansson
|
||||||
|
|
||||||
|
Permission is hereby granted, free of charge, to any person obtaining
|
||||||
|
a copy of this software and associated documentation files (the
|
||||||
|
"Software"), to deal in the Software without restriction, including
|
||||||
|
without limitation the rights to use, copy, modify, merge, publish,
|
||||||
|
distribute, sublicense, and/or sell copies of the Software, and to
|
||||||
|
permit persons to whom the Software is furnished to do so, subject to
|
||||||
|
the following conditions:
|
||||||
|
|
||||||
|
The above copyright notice and this permission notice shall be
|
||||||
|
included in all copies or substantial portions of the Software.
|
||||||
|
|
||||||
|
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
|
||||||
|
EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
|
||||||
|
MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
|
||||||
|
NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
|
||||||
|
LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
|
||||||
|
OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
|
||||||
|
WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
||||||
|
|
57
debian/gems-compat/actionpack-5.1.6.1/README.rdoc
vendored
Normal file
57
debian/gems-compat/actionpack-5.1.6.1/README.rdoc
vendored
Normal file
|
@ -0,0 +1,57 @@
|
||||||
|
= Action Pack -- From request to response
|
||||||
|
|
||||||
|
Action Pack is a framework for handling and responding to web requests. It
|
||||||
|
provides mechanisms for *routing* (mapping request URLs to actions), defining
|
||||||
|
*controllers* that implement actions, and generating responses by rendering
|
||||||
|
*views*, which are templates of various formats. In short, Action Pack
|
||||||
|
provides the view and controller layers in the MVC paradigm.
|
||||||
|
|
||||||
|
It consists of several modules:
|
||||||
|
|
||||||
|
* Action Dispatch, which parses information about the web request, handles
|
||||||
|
routing as defined by the user, and does advanced processing related to HTTP
|
||||||
|
such as MIME-type negotiation, decoding parameters in POST, PATCH, or PUT bodies,
|
||||||
|
handling HTTP caching logic, cookies and sessions.
|
||||||
|
|
||||||
|
* Action Controller, which provides a base controller class that can be
|
||||||
|
subclassed to implement filters and actions to handle requests. The result
|
||||||
|
of an action is typically content generated from views.
|
||||||
|
|
||||||
|
With the Ruby on Rails framework, users only directly interface with the
|
||||||
|
Action Controller module. Necessary Action Dispatch functionality is activated
|
||||||
|
by default and Action View rendering is implicitly triggered by Action
|
||||||
|
Controller. However, these modules are designed to function on their own and
|
||||||
|
can be used outside of Rails.
|
||||||
|
|
||||||
|
|
||||||
|
== Download and installation
|
||||||
|
|
||||||
|
The latest version of Action Pack can be installed with RubyGems:
|
||||||
|
|
||||||
|
$ gem install actionpack
|
||||||
|
|
||||||
|
Source code can be downloaded as part of the Rails project on GitHub
|
||||||
|
|
||||||
|
* https://github.com/rails/rails/tree/master/actionpack
|
||||||
|
|
||||||
|
|
||||||
|
== License
|
||||||
|
|
||||||
|
Action Pack is released under the MIT license:
|
||||||
|
|
||||||
|
* http://www.opensource.org/licenses/MIT
|
||||||
|
|
||||||
|
|
||||||
|
== Support
|
||||||
|
|
||||||
|
API documentation is at
|
||||||
|
|
||||||
|
* http://api.rubyonrails.org
|
||||||
|
|
||||||
|
Bug reports can be filed for the Ruby on Rails project here:
|
||||||
|
|
||||||
|
* https://github.com/rails/rails/issues
|
||||||
|
|
||||||
|
Feature requests should be discussed on the rails-core mailing list here:
|
||||||
|
|
||||||
|
* https://groups.google.com/forum/?fromgroups#!forum/rubyonrails-core
|
55
debian/gems-compat/actionpack-5.1.6.1/actionpack.gemspec
vendored
Normal file
55
debian/gems-compat/actionpack-5.1.6.1/actionpack.gemspec
vendored
Normal file
File diff suppressed because one or more lines are too long
24
debian/gems-compat/actionpack-5.1.6.1/lib/abstract_controller.rb
vendored
Normal file
24
debian/gems-compat/actionpack-5.1.6.1/lib/abstract_controller.rb
vendored
Normal file
|
@ -0,0 +1,24 @@
|
||||||
|
require "action_pack"
|
||||||
|
require "active_support/rails"
|
||||||
|
require "active_support/i18n"
|
||||||
|
|
||||||
|
module AbstractController
|
||||||
|
extend ActiveSupport::Autoload
|
||||||
|
|
||||||
|
autoload :Base
|
||||||
|
autoload :Caching
|
||||||
|
autoload :Callbacks
|
||||||
|
autoload :Collector
|
||||||
|
autoload :DoubleRenderError, "abstract_controller/rendering"
|
||||||
|
autoload :Helpers
|
||||||
|
autoload :Logger
|
||||||
|
autoload :Rendering
|
||||||
|
autoload :Translation
|
||||||
|
autoload :AssetPaths
|
||||||
|
autoload :UrlFor
|
||||||
|
|
||||||
|
def self.eager_load!
|
||||||
|
super
|
||||||
|
AbstractController::Caching.eager_load!
|
||||||
|
end
|
||||||
|
end
|
10
debian/gems-compat/actionpack-5.1.6.1/lib/abstract_controller/asset_paths.rb
vendored
Normal file
10
debian/gems-compat/actionpack-5.1.6.1/lib/abstract_controller/asset_paths.rb
vendored
Normal file
|
@ -0,0 +1,10 @@
|
||||||
|
module AbstractController
|
||||||
|
module AssetPaths #:nodoc:
|
||||||
|
extend ActiveSupport::Concern
|
||||||
|
|
||||||
|
included do
|
||||||
|
config_accessor :asset_host, :assets_dir, :javascripts_dir,
|
||||||
|
:stylesheets_dir, :default_asset_host_protocol, :relative_url_root
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
257
debian/gems-compat/actionpack-5.1.6.1/lib/abstract_controller/base.rb
vendored
Normal file
257
debian/gems-compat/actionpack-5.1.6.1/lib/abstract_controller/base.rb
vendored
Normal file
|
@ -0,0 +1,257 @@
|
||||||
|
require "abstract_controller/error"
|
||||||
|
require "active_support/configurable"
|
||||||
|
require "active_support/descendants_tracker"
|
||||||
|
require "active_support/core_ext/module/anonymous"
|
||||||
|
require "active_support/core_ext/module/attr_internal"
|
||||||
|
|
||||||
|
module AbstractController
|
||||||
|
# Raised when a non-existing controller action is triggered.
|
||||||
|
class ActionNotFound < StandardError
|
||||||
|
end
|
||||||
|
|
||||||
|
# AbstractController::Base is a low-level API. Nobody should be
|
||||||
|
# using it directly, and subclasses (like ActionController::Base) are
|
||||||
|
# expected to provide their own +render+ method, since rendering means
|
||||||
|
# different things depending on the context.
|
||||||
|
class Base
|
||||||
|
attr_internal :response_body
|
||||||
|
attr_internal :action_name
|
||||||
|
attr_internal :formats
|
||||||
|
|
||||||
|
include ActiveSupport::Configurable
|
||||||
|
extend ActiveSupport::DescendantsTracker
|
||||||
|
|
||||||
|
class << self
|
||||||
|
attr_reader :abstract
|
||||||
|
alias_method :abstract?, :abstract
|
||||||
|
|
||||||
|
# Define a controller as abstract. See internal_methods for more
|
||||||
|
# details.
|
||||||
|
def abstract!
|
||||||
|
@abstract = true
|
||||||
|
end
|
||||||
|
|
||||||
|
def inherited(klass) # :nodoc:
|
||||||
|
# Define the abstract ivar on subclasses so that we don't get
|
||||||
|
# uninitialized ivar warnings
|
||||||
|
unless klass.instance_variable_defined?(:@abstract)
|
||||||
|
klass.instance_variable_set(:@abstract, false)
|
||||||
|
end
|
||||||
|
super
|
||||||
|
end
|
||||||
|
|
||||||
|
# A list of all internal methods for a controller. This finds the first
|
||||||
|
# abstract superclass of a controller, and gets a list of all public
|
||||||
|
# instance methods on that abstract class. Public instance methods of
|
||||||
|
# a controller would normally be considered action methods, so methods
|
||||||
|
# declared on abstract classes are being removed.
|
||||||
|
# (<tt>ActionController::Metal</tt> and ActionController::Base are defined as abstract)
|
||||||
|
def internal_methods
|
||||||
|
controller = self
|
||||||
|
|
||||||
|
controller = controller.superclass until controller.abstract?
|
||||||
|
controller.public_instance_methods(true)
|
||||||
|
end
|
||||||
|
|
||||||
|
# A list of method names that should be considered actions. This
|
||||||
|
# includes all public instance methods on a controller, less
|
||||||
|
# any internal methods (see internal_methods), adding back in
|
||||||
|
# any methods that are internal, but still exist on the class
|
||||||
|
# itself.
|
||||||
|
#
|
||||||
|
# ==== Returns
|
||||||
|
# * <tt>Set</tt> - A set of all methods that should be considered actions.
|
||||||
|
def action_methods
|
||||||
|
@action_methods ||= begin
|
||||||
|
# All public instance methods of this class, including ancestors
|
||||||
|
methods = (public_instance_methods(true) -
|
||||||
|
# Except for public instance methods of Base and its ancestors
|
||||||
|
internal_methods +
|
||||||
|
# Be sure to include shadowed public instance methods of this class
|
||||||
|
public_instance_methods(false)).uniq.map(&:to_s)
|
||||||
|
|
||||||
|
methods.to_set
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
# action_methods are cached and there is sometimes a need to refresh
|
||||||
|
# them. ::clear_action_methods! allows you to do that, so next time
|
||||||
|
# you run action_methods, they will be recalculated.
|
||||||
|
def clear_action_methods!
|
||||||
|
@action_methods = nil
|
||||||
|
end
|
||||||
|
|
||||||
|
# Returns the full controller name, underscored, without the ending Controller.
|
||||||
|
#
|
||||||
|
# class MyApp::MyPostsController < AbstractController::Base
|
||||||
|
#
|
||||||
|
# end
|
||||||
|
#
|
||||||
|
# MyApp::MyPostsController.controller_path # => "my_app/my_posts"
|
||||||
|
#
|
||||||
|
# ==== Returns
|
||||||
|
# * <tt>String</tt>
|
||||||
|
def controller_path
|
||||||
|
@controller_path ||= name.sub(/Controller$/, "".freeze).underscore unless anonymous?
|
||||||
|
end
|
||||||
|
|
||||||
|
# Refresh the cached action_methods when a new action_method is added.
|
||||||
|
def method_added(name)
|
||||||
|
super
|
||||||
|
clear_action_methods!
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
abstract!
|
||||||
|
|
||||||
|
# Calls the action going through the entire action dispatch stack.
|
||||||
|
#
|
||||||
|
# The actual method that is called is determined by calling
|
||||||
|
# #method_for_action. If no method can handle the action, then an
|
||||||
|
# AbstractController::ActionNotFound error is raised.
|
||||||
|
#
|
||||||
|
# ==== Returns
|
||||||
|
# * <tt>self</tt>
|
||||||
|
def process(action, *args)
|
||||||
|
@_action_name = action.to_s
|
||||||
|
|
||||||
|
unless action_name = _find_action_name(@_action_name)
|
||||||
|
raise ActionNotFound, "The action '#{action}' could not be found for #{self.class.name}"
|
||||||
|
end
|
||||||
|
|
||||||
|
@_response_body = nil
|
||||||
|
|
||||||
|
process_action(action_name, *args)
|
||||||
|
end
|
||||||
|
|
||||||
|
# Delegates to the class' ::controller_path
|
||||||
|
def controller_path
|
||||||
|
self.class.controller_path
|
||||||
|
end
|
||||||
|
|
||||||
|
# Delegates to the class' ::action_methods
|
||||||
|
def action_methods
|
||||||
|
self.class.action_methods
|
||||||
|
end
|
||||||
|
|
||||||
|
# Returns true if a method for the action is available and
|
||||||
|
# can be dispatched, false otherwise.
|
||||||
|
#
|
||||||
|
# Notice that <tt>action_methods.include?("foo")</tt> may return
|
||||||
|
# false and <tt>available_action?("foo")</tt> returns true because
|
||||||
|
# this method considers actions that are also available
|
||||||
|
# through other means, for example, implicit render ones.
|
||||||
|
#
|
||||||
|
# ==== Parameters
|
||||||
|
# * <tt>action_name</tt> - The name of an action to be tested
|
||||||
|
def available_action?(action_name)
|
||||||
|
_find_action_name(action_name)
|
||||||
|
end
|
||||||
|
|
||||||
|
# Tests if a response body is set. Used to determine if the
|
||||||
|
# +process_action+ callback needs to be terminated in
|
||||||
|
# +AbstractController::Callbacks+.
|
||||||
|
def performed?
|
||||||
|
response_body
|
||||||
|
end
|
||||||
|
|
||||||
|
# Returns true if the given controller is capable of rendering
|
||||||
|
# a path. A subclass of +AbstractController::Base+
|
||||||
|
# may return false. An Email controller for example does not
|
||||||
|
# support paths, only full URLs.
|
||||||
|
def self.supports_path?
|
||||||
|
true
|
||||||
|
end
|
||||||
|
|
||||||
|
private
|
||||||
|
|
||||||
|
# Returns true if the name can be considered an action because
|
||||||
|
# it has a method defined in the controller.
|
||||||
|
#
|
||||||
|
# ==== Parameters
|
||||||
|
# * <tt>name</tt> - The name of an action to be tested
|
||||||
|
#
|
||||||
|
# :api: private
|
||||||
|
def action_method?(name)
|
||||||
|
self.class.action_methods.include?(name)
|
||||||
|
end
|
||||||
|
|
||||||
|
# Call the action. Override this in a subclass to modify the
|
||||||
|
# behavior around processing an action. This, and not #process,
|
||||||
|
# is the intended way to override action dispatching.
|
||||||
|
#
|
||||||
|
# Notice that the first argument is the method to be dispatched
|
||||||
|
# which is *not* necessarily the same as the action name.
|
||||||
|
def process_action(method_name, *args)
|
||||||
|
send_action(method_name, *args)
|
||||||
|
end
|
||||||
|
|
||||||
|
# Actually call the method associated with the action. Override
|
||||||
|
# this method if you wish to change how action methods are called,
|
||||||
|
# not to add additional behavior around it. For example, you would
|
||||||
|
# override #send_action if you want to inject arguments into the
|
||||||
|
# method.
|
||||||
|
alias send_action send
|
||||||
|
|
||||||
|
# If the action name was not found, but a method called "action_missing"
|
||||||
|
# was found, #method_for_action will return "_handle_action_missing".
|
||||||
|
# This method calls #action_missing with the current action name.
|
||||||
|
def _handle_action_missing(*args)
|
||||||
|
action_missing(@_action_name, *args)
|
||||||
|
end
|
||||||
|
|
||||||
|
# Takes an action name and returns the name of the method that will
|
||||||
|
# handle the action.
|
||||||
|
#
|
||||||
|
# It checks if the action name is valid and returns false otherwise.
|
||||||
|
#
|
||||||
|
# See method_for_action for more information.
|
||||||
|
#
|
||||||
|
# ==== Parameters
|
||||||
|
# * <tt>action_name</tt> - An action name to find a method name for
|
||||||
|
#
|
||||||
|
# ==== Returns
|
||||||
|
# * <tt>string</tt> - The name of the method that handles the action
|
||||||
|
# * false - No valid method name could be found.
|
||||||
|
# Raise +AbstractController::ActionNotFound+.
|
||||||
|
def _find_action_name(action_name)
|
||||||
|
_valid_action_name?(action_name) && method_for_action(action_name)
|
||||||
|
end
|
||||||
|
|
||||||
|
# Takes an action name and returns the name of the method that will
|
||||||
|
# handle the action. In normal cases, this method returns the same
|
||||||
|
# name as it receives. By default, if #method_for_action receives
|
||||||
|
# a name that is not an action, it will look for an #action_missing
|
||||||
|
# method and return "_handle_action_missing" if one is found.
|
||||||
|
#
|
||||||
|
# Subclasses may override this method to add additional conditions
|
||||||
|
# that should be considered an action. For instance, an HTTP controller
|
||||||
|
# with a template matching the action name is considered to exist.
|
||||||
|
#
|
||||||
|
# If you override this method to handle additional cases, you may
|
||||||
|
# also provide a method (like +_handle_method_missing+) to handle
|
||||||
|
# the case.
|
||||||
|
#
|
||||||
|
# If none of these conditions are true, and +method_for_action+
|
||||||
|
# returns +nil+, an +AbstractController::ActionNotFound+ exception will be raised.
|
||||||
|
#
|
||||||
|
# ==== Parameters
|
||||||
|
# * <tt>action_name</tt> - An action name to find a method name for
|
||||||
|
#
|
||||||
|
# ==== Returns
|
||||||
|
# * <tt>string</tt> - The name of the method that handles the action
|
||||||
|
# * <tt>nil</tt> - No method name could be found.
|
||||||
|
def method_for_action(action_name)
|
||||||
|
if action_method?(action_name)
|
||||||
|
action_name
|
||||||
|
elsif respond_to?(:action_missing, true)
|
||||||
|
"_handle_action_missing"
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
# Checks if the action name is valid and returns false otherwise.
|
||||||
|
def _valid_action_name?(action_name)
|
||||||
|
!action_name.to_s.include? File::SEPARATOR
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
65
debian/gems-compat/actionpack-5.1.6.1/lib/abstract_controller/caching.rb
vendored
Normal file
65
debian/gems-compat/actionpack-5.1.6.1/lib/abstract_controller/caching.rb
vendored
Normal file
|
@ -0,0 +1,65 @@
|
||||||
|
module AbstractController
|
||||||
|
module Caching
|
||||||
|
extend ActiveSupport::Concern
|
||||||
|
extend ActiveSupport::Autoload
|
||||||
|
|
||||||
|
eager_autoload do
|
||||||
|
autoload :Fragments
|
||||||
|
end
|
||||||
|
|
||||||
|
module ConfigMethods
|
||||||
|
def cache_store
|
||||||
|
config.cache_store
|
||||||
|
end
|
||||||
|
|
||||||
|
def cache_store=(store)
|
||||||
|
config.cache_store = ActiveSupport::Cache.lookup_store(store)
|
||||||
|
end
|
||||||
|
|
||||||
|
private
|
||||||
|
def cache_configured?
|
||||||
|
perform_caching && cache_store
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
include ConfigMethods
|
||||||
|
include AbstractController::Caching::Fragments
|
||||||
|
|
||||||
|
included do
|
||||||
|
extend ConfigMethods
|
||||||
|
|
||||||
|
config_accessor :default_static_extension
|
||||||
|
self.default_static_extension ||= ".html"
|
||||||
|
|
||||||
|
config_accessor :perform_caching
|
||||||
|
self.perform_caching = true if perform_caching.nil?
|
||||||
|
|
||||||
|
config_accessor :enable_fragment_cache_logging
|
||||||
|
self.enable_fragment_cache_logging = false
|
||||||
|
|
||||||
|
class_attribute :_view_cache_dependencies
|
||||||
|
self._view_cache_dependencies = []
|
||||||
|
helper_method :view_cache_dependencies if respond_to?(:helper_method)
|
||||||
|
end
|
||||||
|
|
||||||
|
module ClassMethods
|
||||||
|
def view_cache_dependency(&dependency)
|
||||||
|
self._view_cache_dependencies += [dependency]
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
def view_cache_dependencies
|
||||||
|
self.class._view_cache_dependencies.map { |dep| instance_exec(&dep) }.compact
|
||||||
|
end
|
||||||
|
|
||||||
|
private
|
||||||
|
# Convenience accessor.
|
||||||
|
def cache(key, options = {}, &block) # :doc:
|
||||||
|
if cache_configured?
|
||||||
|
cache_store.fetch(ActiveSupport::Cache.expand_cache_key(key, :controller), options, &block)
|
||||||
|
else
|
||||||
|
yield
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
143
debian/gems-compat/actionpack-5.1.6.1/lib/abstract_controller/caching/fragments.rb
vendored
Normal file
143
debian/gems-compat/actionpack-5.1.6.1/lib/abstract_controller/caching/fragments.rb
vendored
Normal file
|
@ -0,0 +1,143 @@
|
||||||
|
module AbstractController
|
||||||
|
module Caching
|
||||||
|
# Fragment caching is used for caching various blocks within
|
||||||
|
# views without caching the entire action as a whole. This is
|
||||||
|
# useful when certain elements of an action change frequently or
|
||||||
|
# depend on complicated state while other parts rarely change or
|
||||||
|
# can be shared amongst multiple parties. The caching is done using
|
||||||
|
# the +cache+ helper available in the Action View. See
|
||||||
|
# ActionView::Helpers::CacheHelper for more information.
|
||||||
|
#
|
||||||
|
# While it's strongly recommended that you use key-based cache
|
||||||
|
# expiration (see links in CacheHelper for more information),
|
||||||
|
# it is also possible to manually expire caches. For example:
|
||||||
|
#
|
||||||
|
# expire_fragment('name_of_cache')
|
||||||
|
module Fragments
|
||||||
|
extend ActiveSupport::Concern
|
||||||
|
|
||||||
|
included do
|
||||||
|
if respond_to?(:class_attribute)
|
||||||
|
class_attribute :fragment_cache_keys
|
||||||
|
else
|
||||||
|
mattr_writer :fragment_cache_keys
|
||||||
|
end
|
||||||
|
|
||||||
|
self.fragment_cache_keys = []
|
||||||
|
|
||||||
|
helper_method :fragment_cache_key if respond_to?(:helper_method)
|
||||||
|
end
|
||||||
|
|
||||||
|
module ClassMethods
|
||||||
|
# Allows you to specify controller-wide key prefixes for
|
||||||
|
# cache fragments. Pass either a constant +value+, or a block
|
||||||
|
# which computes a value each time a cache key is generated.
|
||||||
|
#
|
||||||
|
# For example, you may want to prefix all fragment cache keys
|
||||||
|
# with a global version identifier, so you can easily
|
||||||
|
# invalidate all caches.
|
||||||
|
#
|
||||||
|
# class ApplicationController
|
||||||
|
# fragment_cache_key "v1"
|
||||||
|
# end
|
||||||
|
#
|
||||||
|
# When it's time to invalidate all fragments, simply change
|
||||||
|
# the string constant. Or, progressively roll out the cache
|
||||||
|
# invalidation using a computed value:
|
||||||
|
#
|
||||||
|
# class ApplicationController
|
||||||
|
# fragment_cache_key do
|
||||||
|
# @account.id.odd? ? "v1" : "v2"
|
||||||
|
# end
|
||||||
|
# end
|
||||||
|
def fragment_cache_key(value = nil, &key)
|
||||||
|
self.fragment_cache_keys += [key || -> { value }]
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
# Given a key (as described in +expire_fragment+), returns
|
||||||
|
# a key suitable for use in reading, writing, or expiring a
|
||||||
|
# cached fragment. All keys begin with <tt>views/</tt>,
|
||||||
|
# followed by any controller-wide key prefix values, ending
|
||||||
|
# with the specified +key+ value. The key is expanded using
|
||||||
|
# ActiveSupport::Cache.expand_cache_key.
|
||||||
|
def fragment_cache_key(key)
|
||||||
|
head = self.class.fragment_cache_keys.map { |k| instance_exec(&k) }
|
||||||
|
tail = key.is_a?(Hash) ? url_for(key).split("://").last : key
|
||||||
|
ActiveSupport::Cache.expand_cache_key([*head, *tail], :views)
|
||||||
|
end
|
||||||
|
|
||||||
|
# Writes +content+ to the location signified by
|
||||||
|
# +key+ (see +expire_fragment+ for acceptable formats).
|
||||||
|
def write_fragment(key, content, options = nil)
|
||||||
|
return content unless cache_configured?
|
||||||
|
|
||||||
|
key = fragment_cache_key(key)
|
||||||
|
instrument_fragment_cache :write_fragment, key do
|
||||||
|
content = content.to_str
|
||||||
|
cache_store.write(key, content, options)
|
||||||
|
end
|
||||||
|
content
|
||||||
|
end
|
||||||
|
|
||||||
|
# Reads a cached fragment from the location signified by +key+
|
||||||
|
# (see +expire_fragment+ for acceptable formats).
|
||||||
|
def read_fragment(key, options = nil)
|
||||||
|
return unless cache_configured?
|
||||||
|
|
||||||
|
key = fragment_cache_key(key)
|
||||||
|
instrument_fragment_cache :read_fragment, key do
|
||||||
|
result = cache_store.read(key, options)
|
||||||
|
result.respond_to?(:html_safe) ? result.html_safe : result
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
# Check if a cached fragment from the location signified by
|
||||||
|
# +key+ exists (see +expire_fragment+ for acceptable formats).
|
||||||
|
def fragment_exist?(key, options = nil)
|
||||||
|
return unless cache_configured?
|
||||||
|
key = fragment_cache_key(key)
|
||||||
|
|
||||||
|
instrument_fragment_cache :exist_fragment?, key do
|
||||||
|
cache_store.exist?(key, options)
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
# Removes fragments from the cache.
|
||||||
|
#
|
||||||
|
# +key+ can take one of three forms:
|
||||||
|
#
|
||||||
|
# * String - This would normally take the form of a path, like
|
||||||
|
# <tt>pages/45/notes</tt>.
|
||||||
|
# * Hash - Treated as an implicit call to +url_for+, like
|
||||||
|
# <tt>{ controller: 'pages', action: 'notes', id: 45}</tt>
|
||||||
|
# * Regexp - Will remove any fragment that matches, so
|
||||||
|
# <tt>%r{pages/\d*/notes}</tt> might remove all notes. Make sure you
|
||||||
|
# don't use anchors in the regex (<tt>^</tt> or <tt>$</tt>) because
|
||||||
|
# the actual filename matched looks like
|
||||||
|
# <tt>./cache/filename/path.cache</tt>. Note: Regexp expiration is
|
||||||
|
# only supported on caches that can iterate over all keys (unlike
|
||||||
|
# memcached).
|
||||||
|
#
|
||||||
|
# +options+ is passed through to the cache store's +delete+
|
||||||
|
# method (or <tt>delete_matched</tt>, for Regexp keys).
|
||||||
|
def expire_fragment(key, options = nil)
|
||||||
|
return unless cache_configured?
|
||||||
|
key = fragment_cache_key(key) unless key.is_a?(Regexp)
|
||||||
|
|
||||||
|
instrument_fragment_cache :expire_fragment, key do
|
||||||
|
if key.is_a?(Regexp)
|
||||||
|
cache_store.delete_matched(key, options)
|
||||||
|
else
|
||||||
|
cache_store.delete(key, options)
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
def instrument_fragment_cache(name, key) # :nodoc:
|
||||||
|
payload = instrument_payload(key)
|
||||||
|
ActiveSupport::Notifications.instrument("#{name}.#{instrument_name}", payload) { yield }
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
190
debian/gems-compat/actionpack-5.1.6.1/lib/abstract_controller/callbacks.rb
vendored
Normal file
190
debian/gems-compat/actionpack-5.1.6.1/lib/abstract_controller/callbacks.rb
vendored
Normal file
|
@ -0,0 +1,190 @@
|
||||||
|
module AbstractController
|
||||||
|
module Callbacks
|
||||||
|
extend ActiveSupport::Concern
|
||||||
|
|
||||||
|
# Uses ActiveSupport::Callbacks as the base functionality. For
|
||||||
|
# more details on the whole callback system, read the documentation
|
||||||
|
# for ActiveSupport::Callbacks.
|
||||||
|
include ActiveSupport::Callbacks
|
||||||
|
|
||||||
|
included do
|
||||||
|
define_callbacks :process_action,
|
||||||
|
terminator: ->(controller, result_lambda) { result_lambda.call if result_lambda.is_a?(Proc); controller.performed? },
|
||||||
|
skip_after_callbacks_if_terminated: true
|
||||||
|
end
|
||||||
|
|
||||||
|
# Override AbstractController::Base's process_action to run the
|
||||||
|
# process_action callbacks around the normal behavior.
|
||||||
|
def process_action(*args)
|
||||||
|
run_callbacks(:process_action) do
|
||||||
|
super
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
module ClassMethods
|
||||||
|
# If +:only+ or +:except+ are used, convert the options into the
|
||||||
|
# +:if+ and +:unless+ options of ActiveSupport::Callbacks.
|
||||||
|
#
|
||||||
|
# The basic idea is that <tt>:only => :index</tt> gets converted to
|
||||||
|
# <tt>:if => proc {|c| c.action_name == "index" }</tt>.
|
||||||
|
#
|
||||||
|
# Note that <tt>:only</tt> has priority over <tt>:if</tt> in case they
|
||||||
|
# are used together.
|
||||||
|
#
|
||||||
|
# only: :index, if: -> { true } # the :if option will be ignored.
|
||||||
|
#
|
||||||
|
# Note that <tt>:if</tt> has priority over <tt>:except</tt> in case they
|
||||||
|
# are used together.
|
||||||
|
#
|
||||||
|
# except: :index, if: -> { true } # the :except option will be ignored.
|
||||||
|
#
|
||||||
|
# ==== Options
|
||||||
|
# * <tt>only</tt> - The callback should be run only for this action.
|
||||||
|
# * <tt>except</tt> - The callback should be run for all actions except this action.
|
||||||
|
def _normalize_callback_options(options)
|
||||||
|
_normalize_callback_option(options, :only, :if)
|
||||||
|
_normalize_callback_option(options, :except, :unless)
|
||||||
|
end
|
||||||
|
|
||||||
|
def _normalize_callback_option(options, from, to) # :nodoc:
|
||||||
|
if from = options[from]
|
||||||
|
_from = Array(from).map(&:to_s).to_set
|
||||||
|
from = proc { |c| _from.include? c.action_name }
|
||||||
|
options[to] = Array(options[to]).unshift(from)
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
# Take callback names and an optional callback proc, normalize them,
|
||||||
|
# then call the block with each callback. This allows us to abstract
|
||||||
|
# the normalization across several methods that use it.
|
||||||
|
#
|
||||||
|
# ==== Parameters
|
||||||
|
# * <tt>callbacks</tt> - An array of callbacks, with an optional
|
||||||
|
# options hash as the last parameter.
|
||||||
|
# * <tt>block</tt> - A proc that should be added to the callbacks.
|
||||||
|
#
|
||||||
|
# ==== Block Parameters
|
||||||
|
# * <tt>name</tt> - The callback to be added.
|
||||||
|
# * <tt>options</tt> - A hash of options to be used when adding the callback.
|
||||||
|
def _insert_callbacks(callbacks, block = nil)
|
||||||
|
options = callbacks.extract_options!
|
||||||
|
_normalize_callback_options(options)
|
||||||
|
callbacks.push(block) if block
|
||||||
|
callbacks.each do |callback|
|
||||||
|
yield callback, options
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
##
|
||||||
|
# :method: before_action
|
||||||
|
#
|
||||||
|
# :call-seq: before_action(names, block)
|
||||||
|
#
|
||||||
|
# Append a callback before actions. See _insert_callbacks for parameter details.
|
||||||
|
|
||||||
|
##
|
||||||
|
# :method: prepend_before_action
|
||||||
|
#
|
||||||
|
# :call-seq: prepend_before_action(names, block)
|
||||||
|
#
|
||||||
|
# Prepend a callback before actions. See _insert_callbacks for parameter details.
|
||||||
|
|
||||||
|
##
|
||||||
|
# :method: skip_before_action
|
||||||
|
#
|
||||||
|
# :call-seq: skip_before_action(names)
|
||||||
|
#
|
||||||
|
# Skip a callback before actions. See _insert_callbacks for parameter details.
|
||||||
|
|
||||||
|
##
|
||||||
|
# :method: append_before_action
|
||||||
|
#
|
||||||
|
# :call-seq: append_before_action(names, block)
|
||||||
|
#
|
||||||
|
# Append a callback before actions. See _insert_callbacks for parameter details.
|
||||||
|
|
||||||
|
##
|
||||||
|
# :method: after_action
|
||||||
|
#
|
||||||
|
# :call-seq: after_action(names, block)
|
||||||
|
#
|
||||||
|
# Append a callback after actions. See _insert_callbacks for parameter details.
|
||||||
|
|
||||||
|
##
|
||||||
|
# :method: prepend_after_action
|
||||||
|
#
|
||||||
|
# :call-seq: prepend_after_action(names, block)
|
||||||
|
#
|
||||||
|
# Prepend a callback after actions. See _insert_callbacks for parameter details.
|
||||||
|
|
||||||
|
##
|
||||||
|
# :method: skip_after_action
|
||||||
|
#
|
||||||
|
# :call-seq: skip_after_action(names)
|
||||||
|
#
|
||||||
|
# Skip a callback after actions. See _insert_callbacks for parameter details.
|
||||||
|
|
||||||
|
##
|
||||||
|
# :method: append_after_action
|
||||||
|
#
|
||||||
|
# :call-seq: append_after_action(names, block)
|
||||||
|
#
|
||||||
|
# Append a callback after actions. See _insert_callbacks for parameter details.
|
||||||
|
|
||||||
|
##
|
||||||
|
# :method: around_action
|
||||||
|
#
|
||||||
|
# :call-seq: around_action(names, block)
|
||||||
|
#
|
||||||
|
# Append a callback around actions. See _insert_callbacks for parameter details.
|
||||||
|
|
||||||
|
##
|
||||||
|
# :method: prepend_around_action
|
||||||
|
#
|
||||||
|
# :call-seq: prepend_around_action(names, block)
|
||||||
|
#
|
||||||
|
# Prepend a callback around actions. See _insert_callbacks for parameter details.
|
||||||
|
|
||||||
|
##
|
||||||
|
# :method: skip_around_action
|
||||||
|
#
|
||||||
|
# :call-seq: skip_around_action(names)
|
||||||
|
#
|
||||||
|
# Skip a callback around actions. See _insert_callbacks for parameter details.
|
||||||
|
|
||||||
|
##
|
||||||
|
# :method: append_around_action
|
||||||
|
#
|
||||||
|
# :call-seq: append_around_action(names, block)
|
||||||
|
#
|
||||||
|
# Append a callback around actions. See _insert_callbacks for parameter details.
|
||||||
|
|
||||||
|
# set up before_action, prepend_before_action, skip_before_action, etc.
|
||||||
|
# for each of before, after, and around.
|
||||||
|
[:before, :after, :around].each do |callback|
|
||||||
|
define_method "#{callback}_action" do |*names, &blk|
|
||||||
|
_insert_callbacks(names, blk) do |name, options|
|
||||||
|
set_callback(:process_action, callback, name, options)
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
define_method "prepend_#{callback}_action" do |*names, &blk|
|
||||||
|
_insert_callbacks(names, blk) do |name, options|
|
||||||
|
set_callback(:process_action, callback, name, options.merge(prepend: true))
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
# Skip a before, after or around callback. See _insert_callbacks
|
||||||
|
# for details on the allowed parameters.
|
||||||
|
define_method "skip_#{callback}_action" do |*names|
|
||||||
|
_insert_callbacks(names) do |name, options|
|
||||||
|
skip_callback(:process_action, callback, name, options)
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
# *_action is the same as append_*_action
|
||||||
|
alias_method :"append_#{callback}_action", :"#{callback}_action"
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
41
debian/gems-compat/actionpack-5.1.6.1/lib/abstract_controller/collector.rb
vendored
Normal file
41
debian/gems-compat/actionpack-5.1.6.1/lib/abstract_controller/collector.rb
vendored
Normal file
|
@ -0,0 +1,41 @@
|
||||||
|
require "action_dispatch/http/mime_type"
|
||||||
|
|
||||||
|
module AbstractController
|
||||||
|
module Collector
|
||||||
|
def self.generate_method_for_mime(mime)
|
||||||
|
sym = mime.is_a?(Symbol) ? mime : mime.to_sym
|
||||||
|
class_eval <<-RUBY, __FILE__, __LINE__ + 1
|
||||||
|
def #{sym}(*args, &block)
|
||||||
|
custom(Mime[:#{sym}], *args, &block)
|
||||||
|
end
|
||||||
|
RUBY
|
||||||
|
end
|
||||||
|
|
||||||
|
Mime::SET.each do |mime|
|
||||||
|
generate_method_for_mime(mime)
|
||||||
|
end
|
||||||
|
|
||||||
|
Mime::Type.register_callback do |mime|
|
||||||
|
generate_method_for_mime(mime) unless instance_methods.include?(mime.to_sym)
|
||||||
|
end
|
||||||
|
|
||||||
|
private
|
||||||
|
|
||||||
|
def method_missing(symbol, &block)
|
||||||
|
unless mime_constant = Mime[symbol]
|
||||||
|
raise NoMethodError, "To respond to a custom format, register it as a MIME type first: " \
|
||||||
|
"http://guides.rubyonrails.org/action_controller_overview.html#restful-downloads. " \
|
||||||
|
"If you meant to respond to a variant like :tablet or :phone, not a custom format, " \
|
||||||
|
"be sure to nest your variant response within a format response: " \
|
||||||
|
"format.html { |html| html.tablet { ... } }"
|
||||||
|
end
|
||||||
|
|
||||||
|
if Mime::SET.include?(mime_constant)
|
||||||
|
AbstractController::Collector.generate_method_for_mime(mime_constant)
|
||||||
|
send(symbol, &block)
|
||||||
|
else
|
||||||
|
super
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
4
debian/gems-compat/actionpack-5.1.6.1/lib/abstract_controller/error.rb
vendored
Normal file
4
debian/gems-compat/actionpack-5.1.6.1/lib/abstract_controller/error.rb
vendored
Normal file
|
@ -0,0 +1,4 @@
|
||||||
|
module AbstractController
|
||||||
|
class Error < StandardError #:nodoc:
|
||||||
|
end
|
||||||
|
end
|
195
debian/gems-compat/actionpack-5.1.6.1/lib/abstract_controller/helpers.rb
vendored
Normal file
195
debian/gems-compat/actionpack-5.1.6.1/lib/abstract_controller/helpers.rb
vendored
Normal file
|
@ -0,0 +1,195 @@
|
||||||
|
require "active_support/dependencies"
|
||||||
|
|
||||||
|
module AbstractController
|
||||||
|
module Helpers
|
||||||
|
extend ActiveSupport::Concern
|
||||||
|
|
||||||
|
included do
|
||||||
|
class_attribute :_helpers
|
||||||
|
self._helpers = Module.new
|
||||||
|
|
||||||
|
class_attribute :_helper_methods
|
||||||
|
self._helper_methods = Array.new
|
||||||
|
end
|
||||||
|
|
||||||
|
class MissingHelperError < LoadError
|
||||||
|
def initialize(error, path)
|
||||||
|
@error = error
|
||||||
|
@path = "helpers/#{path}.rb"
|
||||||
|
set_backtrace error.backtrace
|
||||||
|
|
||||||
|
if error.path =~ /^#{path}(\.rb)?$/
|
||||||
|
super("Missing helper file helpers/%s.rb" % path)
|
||||||
|
else
|
||||||
|
raise error
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
module ClassMethods
|
||||||
|
# When a class is inherited, wrap its helper module in a new module.
|
||||||
|
# This ensures that the parent class's module can be changed
|
||||||
|
# independently of the child class's.
|
||||||
|
def inherited(klass)
|
||||||
|
helpers = _helpers
|
||||||
|
klass._helpers = Module.new { include helpers }
|
||||||
|
klass.class_eval { default_helper_module! } unless klass.anonymous?
|
||||||
|
super
|
||||||
|
end
|
||||||
|
|
||||||
|
# Declare a controller method as a helper. For example, the following
|
||||||
|
# makes the +current_user+ and +logged_in?+ controller methods available
|
||||||
|
# to the view:
|
||||||
|
# class ApplicationController < ActionController::Base
|
||||||
|
# helper_method :current_user, :logged_in?
|
||||||
|
#
|
||||||
|
# def current_user
|
||||||
|
# @current_user ||= User.find_by(id: session[:user])
|
||||||
|
# end
|
||||||
|
#
|
||||||
|
# def logged_in?
|
||||||
|
# current_user != nil
|
||||||
|
# end
|
||||||
|
# end
|
||||||
|
#
|
||||||
|
# In a view:
|
||||||
|
# <% if logged_in? -%>Welcome, <%= current_user.name %><% end -%>
|
||||||
|
#
|
||||||
|
# ==== Parameters
|
||||||
|
# * <tt>method[, method]</tt> - A name or names of a method on the controller
|
||||||
|
# to be made available on the view.
|
||||||
|
def helper_method(*meths)
|
||||||
|
meths.flatten!
|
||||||
|
self._helper_methods += meths
|
||||||
|
|
||||||
|
meths.each do |meth|
|
||||||
|
_helpers.class_eval <<-ruby_eval, __FILE__, __LINE__ + 1
|
||||||
|
def #{meth}(*args, &blk) # def current_user(*args, &blk)
|
||||||
|
controller.send(%(#{meth}), *args, &blk) # controller.send(:current_user, *args, &blk)
|
||||||
|
end # end
|
||||||
|
ruby_eval
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
# The +helper+ class method can take a series of helper module names, a block, or both.
|
||||||
|
#
|
||||||
|
# ==== Options
|
||||||
|
# * <tt>*args</tt> - Module, Symbol, String
|
||||||
|
# * <tt>block</tt> - A block defining helper methods
|
||||||
|
#
|
||||||
|
# When the argument is a module it will be included directly in the template class.
|
||||||
|
# helper FooHelper # => includes FooHelper
|
||||||
|
#
|
||||||
|
# When the argument is a string or symbol, the method will provide the "_helper" suffix, require the file
|
||||||
|
# and include the module in the template class. The second form illustrates how to include custom helpers
|
||||||
|
# when working with namespaced controllers, or other cases where the file containing the helper definition is not
|
||||||
|
# in one of Rails' standard load paths:
|
||||||
|
# helper :foo # => requires 'foo_helper' and includes FooHelper
|
||||||
|
# helper 'resources/foo' # => requires 'resources/foo_helper' and includes Resources::FooHelper
|
||||||
|
#
|
||||||
|
# Additionally, the +helper+ class method can receive and evaluate a block, making the methods defined available
|
||||||
|
# to the template.
|
||||||
|
#
|
||||||
|
# # One line
|
||||||
|
# helper { def hello() "Hello, world!" end }
|
||||||
|
#
|
||||||
|
# # Multi-line
|
||||||
|
# helper do
|
||||||
|
# def foo(bar)
|
||||||
|
# "#{bar} is the very best"
|
||||||
|
# end
|
||||||
|
# end
|
||||||
|
#
|
||||||
|
# Finally, all the above styles can be mixed together, and the +helper+ method can be invoked with a mix of
|
||||||
|
# +symbols+, +strings+, +modules+ and blocks.
|
||||||
|
#
|
||||||
|
# helper(:three, BlindHelper) { def mice() 'mice' end }
|
||||||
|
#
|
||||||
|
def helper(*args, &block)
|
||||||
|
modules_for_helpers(args).each do |mod|
|
||||||
|
add_template_helper(mod)
|
||||||
|
end
|
||||||
|
|
||||||
|
_helpers.module_eval(&block) if block_given?
|
||||||
|
end
|
||||||
|
|
||||||
|
# Clears up all existing helpers in this class, only keeping the helper
|
||||||
|
# with the same name as this class.
|
||||||
|
def clear_helpers
|
||||||
|
inherited_helper_methods = _helper_methods
|
||||||
|
self._helpers = Module.new
|
||||||
|
self._helper_methods = Array.new
|
||||||
|
|
||||||
|
inherited_helper_methods.each { |meth| helper_method meth }
|
||||||
|
default_helper_module! unless anonymous?
|
||||||
|
end
|
||||||
|
|
||||||
|
# Returns a list of modules, normalized from the acceptable kinds of
|
||||||
|
# helpers with the following behavior:
|
||||||
|
#
|
||||||
|
# String or Symbol:: :FooBar or "FooBar" becomes "foo_bar_helper",
|
||||||
|
# and "foo_bar_helper.rb" is loaded using require_dependency.
|
||||||
|
#
|
||||||
|
# Module:: No further processing
|
||||||
|
#
|
||||||
|
# After loading the appropriate files, the corresponding modules
|
||||||
|
# are returned.
|
||||||
|
#
|
||||||
|
# ==== Parameters
|
||||||
|
# * <tt>args</tt> - An array of helpers
|
||||||
|
#
|
||||||
|
# ==== Returns
|
||||||
|
# * <tt>Array</tt> - A normalized list of modules for the list of
|
||||||
|
# helpers provided.
|
||||||
|
def modules_for_helpers(args)
|
||||||
|
args.flatten.map! do |arg|
|
||||||
|
case arg
|
||||||
|
when String, Symbol
|
||||||
|
file_name = "#{arg.to_s.underscore}_helper"
|
||||||
|
begin
|
||||||
|
require_dependency(file_name)
|
||||||
|
rescue LoadError => e
|
||||||
|
raise AbstractController::Helpers::MissingHelperError.new(e, file_name)
|
||||||
|
end
|
||||||
|
|
||||||
|
mod_name = file_name.camelize
|
||||||
|
begin
|
||||||
|
mod_name.constantize
|
||||||
|
rescue LoadError
|
||||||
|
# dependencies.rb gives a similar error message but its wording is
|
||||||
|
# not as clear because it mentions autoloading. To the user all it
|
||||||
|
# matters is that a helper module couldn't be loaded, autoloading
|
||||||
|
# is an internal mechanism that should not leak.
|
||||||
|
raise NameError, "Couldn't find #{mod_name}, expected it to be defined in helpers/#{file_name}.rb"
|
||||||
|
end
|
||||||
|
when Module
|
||||||
|
arg
|
||||||
|
else
|
||||||
|
raise ArgumentError, "helper must be a String, Symbol, or Module"
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
private
|
||||||
|
# Makes all the (instance) methods in the helper module available to templates
|
||||||
|
# rendered through this controller.
|
||||||
|
#
|
||||||
|
# ==== Parameters
|
||||||
|
# * <tt>module</tt> - The module to include into the current helper module
|
||||||
|
# for the class
|
||||||
|
def add_template_helper(mod)
|
||||||
|
_helpers.module_eval { include mod }
|
||||||
|
end
|
||||||
|
|
||||||
|
def default_helper_module!
|
||||||
|
module_name = name.sub(/Controller$/, "".freeze)
|
||||||
|
module_path = module_name.underscore
|
||||||
|
helper module_path
|
||||||
|
rescue LoadError => e
|
||||||
|
raise e unless e.is_missing? "helpers/#{module_path}_helper"
|
||||||
|
rescue NameError => e
|
||||||
|
raise e unless e.missing_name? "#{module_name}Helper"
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
12
debian/gems-compat/actionpack-5.1.6.1/lib/abstract_controller/logger.rb
vendored
Normal file
12
debian/gems-compat/actionpack-5.1.6.1/lib/abstract_controller/logger.rb
vendored
Normal file
|
@ -0,0 +1,12 @@
|
||||||
|
require "active_support/benchmarkable"
|
||||||
|
|
||||||
|
module AbstractController
|
||||||
|
module Logger #:nodoc:
|
||||||
|
extend ActiveSupport::Concern
|
||||||
|
|
||||||
|
included do
|
||||||
|
config_accessor :logger
|
||||||
|
include ActiveSupport::Benchmarkable
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
18
debian/gems-compat/actionpack-5.1.6.1/lib/abstract_controller/railties/routes_helpers.rb
vendored
Normal file
18
debian/gems-compat/actionpack-5.1.6.1/lib/abstract_controller/railties/routes_helpers.rb
vendored
Normal file
|
@ -0,0 +1,18 @@
|
||||||
|
module AbstractController
|
||||||
|
module Railties
|
||||||
|
module RoutesHelpers
|
||||||
|
def self.with(routes, include_path_helpers = true)
|
||||||
|
Module.new do
|
||||||
|
define_method(:inherited) do |klass|
|
||||||
|
super(klass)
|
||||||
|
if namespace = klass.parents.detect { |m| m.respond_to?(:railtie_routes_url_helpers) }
|
||||||
|
klass.include(namespace.railtie_routes_url_helpers(include_path_helpers))
|
||||||
|
else
|
||||||
|
klass.include(routes.url_helpers(include_path_helpers))
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
134
debian/gems-compat/actionpack-5.1.6.1/lib/abstract_controller/rendering.rb
vendored
Normal file
134
debian/gems-compat/actionpack-5.1.6.1/lib/abstract_controller/rendering.rb
vendored
Normal file
|
@ -0,0 +1,134 @@
|
||||||
|
require "abstract_controller/error"
|
||||||
|
require "action_view"
|
||||||
|
require "action_view/view_paths"
|
||||||
|
require "set"
|
||||||
|
|
||||||
|
module AbstractController
|
||||||
|
class DoubleRenderError < Error
|
||||||
|
DEFAULT_MESSAGE = "Render and/or redirect were called multiple times in this action. Please note that you may only call render OR redirect, and at most once per action. Also note that neither redirect nor render terminate execution of the action, so if you want to exit an action after redirecting, you need to do something like \"redirect_to(...) and return\"."
|
||||||
|
|
||||||
|
def initialize(message = nil)
|
||||||
|
super(message || DEFAULT_MESSAGE)
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
module Rendering
|
||||||
|
extend ActiveSupport::Concern
|
||||||
|
include ActionView::ViewPaths
|
||||||
|
|
||||||
|
# Normalizes arguments, options and then delegates render_to_body and
|
||||||
|
# sticks the result in <tt>self.response_body</tt>.
|
||||||
|
# :api: public
|
||||||
|
def render(*args, &block)
|
||||||
|
options = _normalize_render(*args, &block)
|
||||||
|
rendered_body = render_to_body(options)
|
||||||
|
if options[:html]
|
||||||
|
_set_html_content_type
|
||||||
|
else
|
||||||
|
_set_rendered_content_type rendered_format
|
||||||
|
end
|
||||||
|
self.response_body = rendered_body
|
||||||
|
end
|
||||||
|
|
||||||
|
# Raw rendering of a template to a string.
|
||||||
|
#
|
||||||
|
# It is similar to render, except that it does not
|
||||||
|
# set the +response_body+ and it should be guaranteed
|
||||||
|
# to always return a string.
|
||||||
|
#
|
||||||
|
# If a component extends the semantics of +response_body+
|
||||||
|
# (as ActionController extends it to be anything that
|
||||||
|
# responds to the method each), this method needs to be
|
||||||
|
# overridden in order to still return a string.
|
||||||
|
# :api: plugin
|
||||||
|
def render_to_string(*args, &block)
|
||||||
|
options = _normalize_render(*args, &block)
|
||||||
|
render_to_body(options)
|
||||||
|
end
|
||||||
|
|
||||||
|
# Performs the actual template rendering.
|
||||||
|
# :api: public
|
||||||
|
def render_to_body(options = {})
|
||||||
|
end
|
||||||
|
|
||||||
|
# Returns Content-Type of rendered content
|
||||||
|
# :api: public
|
||||||
|
def rendered_format
|
||||||
|
Mime[:text]
|
||||||
|
end
|
||||||
|
|
||||||
|
DEFAULT_PROTECTED_INSTANCE_VARIABLES = Set.new %i(
|
||||||
|
@_action_name @_response_body @_formats @_prefixes
|
||||||
|
)
|
||||||
|
|
||||||
|
# This method should return a hash with assigns.
|
||||||
|
# You can overwrite this configuration per controller.
|
||||||
|
# :api: public
|
||||||
|
def view_assigns
|
||||||
|
protected_vars = _protected_ivars
|
||||||
|
variables = instance_variables
|
||||||
|
|
||||||
|
variables.reject! { |s| protected_vars.include? s }
|
||||||
|
variables.each_with_object({}) { |name, hash|
|
||||||
|
hash[name.slice(1, name.length)] = instance_variable_get(name)
|
||||||
|
}
|
||||||
|
end
|
||||||
|
|
||||||
|
# Normalize args by converting <tt>render "foo"</tt> to
|
||||||
|
# <tt>render :action => "foo"</tt> and <tt>render "foo/bar"</tt> to
|
||||||
|
# <tt>render :file => "foo/bar"</tt>.
|
||||||
|
# :api: plugin
|
||||||
|
def _normalize_args(action = nil, options = {})
|
||||||
|
if action.respond_to?(:permitted?)
|
||||||
|
if action.permitted?
|
||||||
|
action
|
||||||
|
else
|
||||||
|
raise ArgumentError, "render parameters are not permitted"
|
||||||
|
end
|
||||||
|
elsif action.is_a?(Hash)
|
||||||
|
action
|
||||||
|
else
|
||||||
|
options
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
# Normalize options.
|
||||||
|
# :api: plugin
|
||||||
|
def _normalize_options(options)
|
||||||
|
options
|
||||||
|
end
|
||||||
|
|
||||||
|
# Process extra options.
|
||||||
|
# :api: plugin
|
||||||
|
def _process_options(options)
|
||||||
|
options
|
||||||
|
end
|
||||||
|
|
||||||
|
# Process the rendered format.
|
||||||
|
# :api: private
|
||||||
|
def _process_format(format)
|
||||||
|
end
|
||||||
|
|
||||||
|
def _process_variant(options)
|
||||||
|
end
|
||||||
|
|
||||||
|
def _set_html_content_type # :nodoc:
|
||||||
|
end
|
||||||
|
|
||||||
|
def _set_rendered_content_type(format) # :nodoc:
|
||||||
|
end
|
||||||
|
|
||||||
|
# Normalize args and options.
|
||||||
|
# :api: private
|
||||||
|
def _normalize_render(*args, &block)
|
||||||
|
options = _normalize_args(*args, &block)
|
||||||
|
_process_variant(options)
|
||||||
|
_normalize_options(options)
|
||||||
|
options
|
||||||
|
end
|
||||||
|
|
||||||
|
def _protected_ivars # :nodoc:
|
||||||
|
DEFAULT_PROTECTED_INSTANCE_VARIABLES
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
29
debian/gems-compat/actionpack-5.1.6.1/lib/abstract_controller/translation.rb
vendored
Normal file
29
debian/gems-compat/actionpack-5.1.6.1/lib/abstract_controller/translation.rb
vendored
Normal file
|
@ -0,0 +1,29 @@
|
||||||
|
module AbstractController
|
||||||
|
module Translation
|
||||||
|
# Delegates to <tt>I18n.translate</tt>. Also aliased as <tt>t</tt>.
|
||||||
|
#
|
||||||
|
# When the given key starts with a period, it will be scoped by the current
|
||||||
|
# controller and action. So if you call <tt>translate(".foo")</tt> from
|
||||||
|
# <tt>PeopleController#index</tt>, it will convert the call to
|
||||||
|
# <tt>I18n.translate("people.index.foo")</tt>. This makes it less repetitive
|
||||||
|
# to translate many keys within the same controller / action and gives you a
|
||||||
|
# simple framework for scoping them consistently.
|
||||||
|
def translate(key, options = {})
|
||||||
|
if key.to_s.first == "."
|
||||||
|
path = controller_path.tr("/", ".")
|
||||||
|
defaults = [:"#{path}#{key}"]
|
||||||
|
defaults << options[:default] if options[:default]
|
||||||
|
options[:default] = defaults.flatten
|
||||||
|
key = "#{path}.#{action_name}#{key}"
|
||||||
|
end
|
||||||
|
I18n.translate(key, options)
|
||||||
|
end
|
||||||
|
alias :t :translate
|
||||||
|
|
||||||
|
# Delegates to <tt>I18n.localize</tt>. Also aliased as <tt>l</tt>.
|
||||||
|
def localize(*args)
|
||||||
|
I18n.localize(*args)
|
||||||
|
end
|
||||||
|
alias :l :localize
|
||||||
|
end
|
||||||
|
end
|
33
debian/gems-compat/actionpack-5.1.6.1/lib/abstract_controller/url_for.rb
vendored
Normal file
33
debian/gems-compat/actionpack-5.1.6.1/lib/abstract_controller/url_for.rb
vendored
Normal file
|
@ -0,0 +1,33 @@
|
||||||
|
module AbstractController
|
||||||
|
# Includes +url_for+ into the host class (e.g. an abstract controller or mailer). The class
|
||||||
|
# has to provide a +RouteSet+ by implementing the <tt>_routes</tt> methods. Otherwise, an
|
||||||
|
# exception will be raised.
|
||||||
|
#
|
||||||
|
# Note that this module is completely decoupled from HTTP - the only requirement is a valid
|
||||||
|
# <tt>_routes</tt> implementation.
|
||||||
|
module UrlFor
|
||||||
|
extend ActiveSupport::Concern
|
||||||
|
include ActionDispatch::Routing::UrlFor
|
||||||
|
|
||||||
|
def _routes
|
||||||
|
raise "In order to use #url_for, you must include routing helpers explicitly. " \
|
||||||
|
"For instance, `include Rails.application.routes.url_helpers`."
|
||||||
|
end
|
||||||
|
|
||||||
|
module ClassMethods
|
||||||
|
def _routes
|
||||||
|
nil
|
||||||
|
end
|
||||||
|
|
||||||
|
def action_methods
|
||||||
|
@action_methods ||= begin
|
||||||
|
if _routes
|
||||||
|
super - _routes.named_routes.helper_names
|
||||||
|
else
|
||||||
|
super
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
63
debian/gems-compat/actionpack-5.1.6.1/lib/action_controller.rb
vendored
Normal file
63
debian/gems-compat/actionpack-5.1.6.1/lib/action_controller.rb
vendored
Normal file
|
@ -0,0 +1,63 @@
|
||||||
|
require "active_support/rails"
|
||||||
|
require "abstract_controller"
|
||||||
|
require "action_dispatch"
|
||||||
|
require "action_controller/metal/live"
|
||||||
|
require "action_controller/metal/strong_parameters"
|
||||||
|
|
||||||
|
module ActionController
|
||||||
|
extend ActiveSupport::Autoload
|
||||||
|
|
||||||
|
autoload :API
|
||||||
|
autoload :Base
|
||||||
|
autoload :Metal
|
||||||
|
autoload :Middleware
|
||||||
|
autoload :Renderer
|
||||||
|
autoload :FormBuilder
|
||||||
|
|
||||||
|
eager_autoload do
|
||||||
|
autoload :Caching
|
||||||
|
end
|
||||||
|
|
||||||
|
autoload_under "metal" do
|
||||||
|
autoload :ConditionalGet
|
||||||
|
autoload :Cookies
|
||||||
|
autoload :DataStreaming
|
||||||
|
autoload :EtagWithTemplateDigest
|
||||||
|
autoload :EtagWithFlash
|
||||||
|
autoload :Flash
|
||||||
|
autoload :ForceSSL
|
||||||
|
autoload :Head
|
||||||
|
autoload :Helpers
|
||||||
|
autoload :HttpAuthentication
|
||||||
|
autoload :BasicImplicitRender
|
||||||
|
autoload :ImplicitRender
|
||||||
|
autoload :Instrumentation
|
||||||
|
autoload :MimeResponds
|
||||||
|
autoload :ParamsWrapper
|
||||||
|
autoload :Redirecting
|
||||||
|
autoload :Renderers
|
||||||
|
autoload :Rendering
|
||||||
|
autoload :RequestForgeryProtection
|
||||||
|
autoload :Rescue
|
||||||
|
autoload :Streaming
|
||||||
|
autoload :StrongParameters
|
||||||
|
autoload :ParameterEncoding
|
||||||
|
autoload :Testing
|
||||||
|
autoload :UrlFor
|
||||||
|
end
|
||||||
|
|
||||||
|
autoload_under "api" do
|
||||||
|
autoload :ApiRendering
|
||||||
|
end
|
||||||
|
|
||||||
|
autoload :TestCase, "action_controller/test_case"
|
||||||
|
autoload :TemplateAssertions, "action_controller/test_case"
|
||||||
|
end
|
||||||
|
|
||||||
|
# Common Active Support usage in Action Controller
|
||||||
|
require "active_support/core_ext/module/attribute_accessors"
|
||||||
|
require "active_support/core_ext/load_error"
|
||||||
|
require "active_support/core_ext/module/attr_internal"
|
||||||
|
require "active_support/core_ext/name_error"
|
||||||
|
require "active_support/core_ext/uri"
|
||||||
|
require "active_support/inflector"
|
147
debian/gems-compat/actionpack-5.1.6.1/lib/action_controller/api.rb
vendored
Normal file
147
debian/gems-compat/actionpack-5.1.6.1/lib/action_controller/api.rb
vendored
Normal file
|
@ -0,0 +1,147 @@
|
||||||
|
require "action_view"
|
||||||
|
require "action_controller"
|
||||||
|
require "action_controller/log_subscriber"
|
||||||
|
|
||||||
|
module ActionController
|
||||||
|
# API Controller is a lightweight version of <tt>ActionController::Base</tt>,
|
||||||
|
# created for applications that don't require all functionalities that a complete
|
||||||
|
# \Rails controller provides, allowing you to create controllers with just the
|
||||||
|
# features that you need for API only applications.
|
||||||
|
#
|
||||||
|
# An API Controller is different from a normal controller in the sense that
|
||||||
|
# by default it doesn't include a number of features that are usually required
|
||||||
|
# by browser access only: layouts and templates rendering, cookies, sessions,
|
||||||
|
# flash, assets, and so on. This makes the entire controller stack thinner,
|
||||||
|
# suitable for API applications. It doesn't mean you won't have such
|
||||||
|
# features if you need them: they're all available for you to include in
|
||||||
|
# your application, they're just not part of the default API controller stack.
|
||||||
|
#
|
||||||
|
# Normally, +ApplicationController+ is the only controller that inherits from
|
||||||
|
# <tt>ActionController::API</tt>. All other controllers in turn inherit from
|
||||||
|
# +ApplicationController+.
|
||||||
|
#
|
||||||
|
# A sample controller could look like this:
|
||||||
|
#
|
||||||
|
# class PostsController < ApplicationController
|
||||||
|
# def index
|
||||||
|
# posts = Post.all
|
||||||
|
# render json: posts
|
||||||
|
# end
|
||||||
|
# end
|
||||||
|
#
|
||||||
|
# Request, response, and parameters objects all work the exact same way as
|
||||||
|
# <tt>ActionController::Base</tt>.
|
||||||
|
#
|
||||||
|
# == Renders
|
||||||
|
#
|
||||||
|
# The default API Controller stack includes all renderers, which means you
|
||||||
|
# can use <tt>render :json</tt> and brothers freely in your controllers. Keep
|
||||||
|
# in mind that templates are not going to be rendered, so you need to ensure
|
||||||
|
# your controller is calling either <tt>render</tt> or <tt>redirect_to</tt> in
|
||||||
|
# all actions, otherwise it will return 204 No Content.
|
||||||
|
#
|
||||||
|
# def show
|
||||||
|
# post = Post.find(params[:id])
|
||||||
|
# render json: post
|
||||||
|
# end
|
||||||
|
#
|
||||||
|
# == Redirects
|
||||||
|
#
|
||||||
|
# Redirects are used to move from one action to another. You can use the
|
||||||
|
# <tt>redirect_to</tt> method in your controllers in the same way as in
|
||||||
|
# <tt>ActionController::Base</tt>. For example:
|
||||||
|
#
|
||||||
|
# def create
|
||||||
|
# redirect_to root_url and return if not_authorized?
|
||||||
|
# # do stuff here
|
||||||
|
# end
|
||||||
|
#
|
||||||
|
# == Adding New Behavior
|
||||||
|
#
|
||||||
|
# In some scenarios you may want to add back some functionality provided by
|
||||||
|
# <tt>ActionController::Base</tt> that is not present by default in
|
||||||
|
# <tt>ActionController::API</tt>, for instance <tt>MimeResponds</tt>. This
|
||||||
|
# module gives you the <tt>respond_to</tt> method. Adding it is quite simple,
|
||||||
|
# you just need to include the module in a specific controller or in
|
||||||
|
# +ApplicationController+ in case you want it available in your entire
|
||||||
|
# application:
|
||||||
|
#
|
||||||
|
# class ApplicationController < ActionController::API
|
||||||
|
# include ActionController::MimeResponds
|
||||||
|
# end
|
||||||
|
#
|
||||||
|
# class PostsController < ApplicationController
|
||||||
|
# def index
|
||||||
|
# posts = Post.all
|
||||||
|
#
|
||||||
|
# respond_to do |format|
|
||||||
|
# format.json { render json: posts }
|
||||||
|
# format.xml { render xml: posts }
|
||||||
|
# end
|
||||||
|
# end
|
||||||
|
# end
|
||||||
|
#
|
||||||
|
# Make sure to check the modules included in <tt>ActionController::Base</tt>
|
||||||
|
# if you want to use any other functionality that is not provided
|
||||||
|
# by <tt>ActionController::API</tt> out of the box.
|
||||||
|
class API < Metal
|
||||||
|
abstract!
|
||||||
|
|
||||||
|
# Shortcut helper that returns all the ActionController::API modules except
|
||||||
|
# the ones passed as arguments:
|
||||||
|
#
|
||||||
|
# class MyAPIBaseController < ActionController::Metal
|
||||||
|
# ActionController::API.without_modules(:ForceSSL, :UrlFor).each do |left|
|
||||||
|
# include left
|
||||||
|
# end
|
||||||
|
# end
|
||||||
|
#
|
||||||
|
# This gives better control over what you want to exclude and makes it easier
|
||||||
|
# to create an API controller class, instead of listing the modules required
|
||||||
|
# manually.
|
||||||
|
def self.without_modules(*modules)
|
||||||
|
modules = modules.map do |m|
|
||||||
|
m.is_a?(Symbol) ? ActionController.const_get(m) : m
|
||||||
|
end
|
||||||
|
|
||||||
|
MODULES - modules
|
||||||
|
end
|
||||||
|
|
||||||
|
MODULES = [
|
||||||
|
AbstractController::Rendering,
|
||||||
|
|
||||||
|
UrlFor,
|
||||||
|
Redirecting,
|
||||||
|
ApiRendering,
|
||||||
|
Renderers::All,
|
||||||
|
ConditionalGet,
|
||||||
|
BasicImplicitRender,
|
||||||
|
StrongParameters,
|
||||||
|
|
||||||
|
ForceSSL,
|
||||||
|
DataStreaming,
|
||||||
|
|
||||||
|
# Before callbacks should also be executed as early as possible, so
|
||||||
|
# also include them at the bottom.
|
||||||
|
AbstractController::Callbacks,
|
||||||
|
|
||||||
|
# Append rescue at the bottom to wrap as much as possible.
|
||||||
|
Rescue,
|
||||||
|
|
||||||
|
# Add instrumentations hooks at the bottom, to ensure they instrument
|
||||||
|
# all the methods properly.
|
||||||
|
Instrumentation,
|
||||||
|
|
||||||
|
# Params wrapper should come before instrumentation so they are
|
||||||
|
# properly showed in logs
|
||||||
|
ParamsWrapper
|
||||||
|
]
|
||||||
|
|
||||||
|
MODULES.each do |mod|
|
||||||
|
include mod
|
||||||
|
end
|
||||||
|
|
||||||
|
ActiveSupport.run_load_hooks(:action_controller_api, self)
|
||||||
|
ActiveSupport.run_load_hooks(:action_controller, self)
|
||||||
|
end
|
||||||
|
end
|
14
debian/gems-compat/actionpack-5.1.6.1/lib/action_controller/api/api_rendering.rb
vendored
Normal file
14
debian/gems-compat/actionpack-5.1.6.1/lib/action_controller/api/api_rendering.rb
vendored
Normal file
|
@ -0,0 +1,14 @@
|
||||||
|
module ActionController
|
||||||
|
module ApiRendering
|
||||||
|
extend ActiveSupport::Concern
|
||||||
|
|
||||||
|
included do
|
||||||
|
include Rendering
|
||||||
|
end
|
||||||
|
|
||||||
|
def render_to_body(options = {})
|
||||||
|
_process_options(options)
|
||||||
|
super
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
Some files were not shown because too many files have changed in this diff Show more
Loading…
Reference in a new issue