Skip to content

Commit

Permalink
Merge remote-tracking branch 'ph/master'
Browse files Browse the repository at this point in the history
* ph/master: (112 commits)
  Avoid .formatter.exs files with conflicting inputs (phoenixframework#3135)
  Phoenix.ConnTest.redirected_to/2 returns a string, not %Plug.Conn{} (phoenixframework#3138)
  Cowboy 2 does not accept error return in init, closes phoenixframework#3140
  Remove -rc from Ecto deps
  Maintain private socket state from transport connect
  Reduces ecto setup instructions (phoenixframework#3137)
  Allow custom keyword pairs to be passed to the socket macro. Closes phoenixframework#3136
  Improve error message for configured JSON library
  Fix missing json config for phx.new.web task. Closes phoenixframework#3121
  Release 1.4.0-rc.3
  Bump phoenix_ecto dependency
  Remove ecto overrides
  Log the configured URL instead of the raw ip (phoenixframework#3112)
  upgrade mocha due to security audit (phoenixframework#3127)
  Update list of mix tasks in tasks guide (phoenixframework#3095)
  Copy phoenix.js to installer templates
  Fix Phoenix.js ES5 output, upgrade webpack (phoenixframework#3126)
  Add info on the :reloadable_apps option to CHANGELOG.md (phoenixframework#3122)
  Import Ecto's .formatter.exs in templates (phoenixframework#3117)
  Remove duplicated assert (phoenixframework#3118)
  ...
  • Loading branch information
Xiaobin0860 committed Nov 6, 2018
2 parents 18f1bae + 937a061 commit c3a7207
Show file tree
Hide file tree
Showing 111 changed files with 5,649 additions and 7,425 deletions.
2 changes: 2 additions & 0 deletions .gitignore
Expand Up @@ -8,5 +8,7 @@

/installer/_build
/installer/tmp
/installer/doc
/installer/deps

erl_crash.dump
3 changes: 3 additions & 0 deletions .travis.yml
Expand Up @@ -30,3 +30,6 @@ script:
after_script:
- cd $TRAVIS_BUILD_DIR
- MIX_ENV=docs mix inch.report
cache:
directories:
- assets/node_modules
51 changes: 50 additions & 1 deletion CHANGELOG.md
@@ -1,5 +1,7 @@
# Changelog for v1.4

See the [upgrade guides](https://gist.github.com/chrismccord/bb1f8b136f5a9e4abc0bfc07b832257e) to bring your Phoenix 1.3.x apps up to speed.

## Cowboy 2 support

TODO: Write about how to use Cowboy 2
Expand All @@ -26,7 +28,51 @@ Note the websocket/longpoll configuration given to socket/3 will only apply afte

The old APIs for building transports are also deprecated. The good news is: adapting an existing transport to the new API is a less error prone process where you should mostly remove code.

## 1.4.0-dev

## 1.4.0-rc.4

### Enhancements
* [Endpoint] Allow custom keyword pairs to be passed to the socket `:connect_info` options.

## 1.4.0-rc.3 (2018-10-30)

### Enhancements
* [phx.new] Update Ecto deps with the release of Ecto 3.0 including `phoenix_ecto` 4.0
* [phx.new] Import Ecto's `.formatter.exs` in new projects
* [Endpoint] Log the configured url instead of raw IP when booting endpoint webserver

## 1.4.0-rc.2 (2018-10-20)

### Enhancements

* [phx.new] Use Ecto 3.0RC, with `ecto_sql` in new project deps
* [phx.new] Use Plug 1.7 with new `:plug_cowboy` dependency for cowboy adapter
* [phx.gen.html|json|schema|context] Support new Ecto 3.0 usec datetime types

### Bug Fixes

* [Routes] Fix regression in router compilation failing to escape plug options
* [phx.gen.json|html] Fix generator tests incorrectly encoding datetimes
* [phx.gen.cert] Fix generation of cert inside umbrella projects

## 1.4.0-rc.1 (2018-10-12)

### Enhancements

* [Socket] Improve error message when missing socket mount in endpoint

### Bug Fixes

* Add missing `.formatter.exs` to hex package for proper elixir formatter integration
* [phx.gen.cert] Fix usage inside umbrella applications
* [phx.new] Revert `Routes.static_url` in app layout in favor of original `Routes.static_path`
* [phx.new] Use phoenix_live_reload 1.2-rc to fix hex version errors

### JavaScript client

* Fix reconnect caused by pending heartbeat

## 1.4.0-rc.0 (2018-10-09)

### Enhancements

Expand All @@ -43,6 +89,9 @@ The old APIs for building transports are also deprecated. The good news is: adap
* [Router] Display list of available routes on debugger 404 error page
* [Router] Raise on duplicate plugs in `pipe_through` scopes
* [Presence] Add `Presence.get_by_key` to fetch presences for specific user
* [Socket] Add new `phoenix_socket_connect` instrumentation
* [Logger] Log calls to user socket connect
* [CodeReloader] Add `:reloadable_apps` endpoint configuration option to allow recompiling local dependencies

### Bug Fixes

Expand Down
3 changes: 2 additions & 1 deletion CONTRIBUTING.md
Expand Up @@ -13,7 +13,8 @@ Use the issues tracker for:

Please **do not** use the issue tracker for personal support requests nor feature requests. Support requests should be sent to:

* [the phoenix-talk mailing list](http://groups.google.com/group/phoenix-talk)
* [the phoenix-talk mailing list](http://groups.google.com/group/phoenix-talk) (closed in favor of forum, but archive is still online)
* [The Phoenix subforum on the Elixir forum](https://elixirforum.com/c/phoenix-forum)
* **[#elixir-lang](irc://chat.freenode.net/elixir-lang)** IRC channel on [chat.freenode.net](http://www.freenode.net/)

Development issues can be discussed on [the phoenix-core mailing list](http://groups.google.com/group/phoenix-core).
Expand Down
6 changes: 4 additions & 2 deletions README.md
Expand Up @@ -52,13 +52,15 @@ $ cd ..
$ MIX_ENV=docs mix docs
```

To build phoenix from source:
To build Phoenix from source:

```bash
$ mix deps.get
$ mix compile
```

To build the phoenix installer from source
To build the Phoenix installer from source:

```bash
$ mix deps.get
$ mix compile
Expand Down
5 changes: 5 additions & 0 deletions RELEASE.md
Expand Up @@ -13,10 +13,15 @@
9. Start -dev version in related files below
10. Update `phoenix_dep` in `installer/lib/phoenix_new.ex` back to git
11. Publish to `npm` with `npm publish`
12. Replace `master` for `source_url_pattern` in `installer/mix.exs`
13. Push installer (phx_new hex package) to hex

## Files with version

* `CHANGELOG`
* `mix.exs`
* `installer/mix.exs`
* `installer/README.md`
* `package.json`
* `assets/package.json`
* `guides/introduction/installation.md`
2 changes: 1 addition & 1 deletion assets/.babelrc
@@ -1,5 +1,5 @@
{
"presets": [
"env"
"@babel/preset-env"
]
}
91 changes: 55 additions & 36 deletions assets/js/phoenix.js
Expand Up @@ -185,7 +185,7 @@
* @module phoenix
*/

const global = typeof(self) !== "undefined" ? self : window
const global = typeof self !== "undefined" ? self : window
const VSN = "2.0.0"
const SOCKET_STATES = {connecting: 0, open: 1, closing: 2, closed: 3}
const DEFAULT_TIMEOUT = 10000
Expand Down Expand Up @@ -218,7 +218,7 @@ const TRANSPORTS = {

// wraps value in closure or returns closure
let closure = (value) => {
if(typeof(value) === "function"){
if(typeof value === "function"){
return value
} else {
let closure = function(){ return value }
Expand Down Expand Up @@ -382,17 +382,17 @@ export class Channel {
})
this.onClose( () => {
this.rejoinTimer.reset()
this.socket.log("channel", `close ${this.topic} ${this.joinRef()}`)
if (this.socket.hasLogger()) this.socket.log("channel", `close ${this.topic} ${this.joinRef()}`)
this.state = CHANNEL_STATES.closed
this.socket.remove(this)
})
this.onError( reason => { if(this.isLeaving() || this.isClosed()){ return }
this.socket.log("channel", `error ${this.topic}`, reason)
if (this.socket.hasLogger()) this.socket.log("channel", `error ${this.topic}`, reason)
this.state = CHANNEL_STATES.errored
this.rejoinTimer.scheduleTimeout()
})
this.joinPush.receive("timeout", () => { if(!this.isJoining()){ return }
this.socket.log("channel", `timeout ${this.topic} (${this.joinRef()})`, this.joinPush.timeout)
if (this.socket.hasLogger()) this.socket.log("channel", `timeout ${this.topic} (${this.joinRef()})`, this.joinPush.timeout)
let leavePush = new Push(this, CHANNEL_EVENTS.leave, closure({}), this.timeout)
leavePush.send()
this.state = CHANNEL_STATES.errored
Expand Down Expand Up @@ -523,7 +523,7 @@ export class Channel {
leave(timeout = this.timeout){
this.state = CHANNEL_STATES.leaving
let onClose = () => {
this.socket.log("channel", `leave ${this.topic}`)
if (this.socket.hasLogger()) this.socket.log("channel", `leave ${this.topic}`)
this.trigger(CHANNEL_EVENTS.close, "leave")
}
let leavePush = new Push(this, CHANNEL_EVENTS.leave, closure({}), timeout)
Expand All @@ -549,15 +549,19 @@ export class Channel {
*/
onMessage(event, payload, ref){ return payload }

/**
* @private
*/
isLifecycleEvent(event) { return CHANNEL_LIFECYCLE_EVENTS.indexOf(event) >= 0 }

/**
* @private
*/
isMember(topic, event, payload, joinRef){
if(this.topic !== topic){ return false }
let isLifecycleEvent = CHANNEL_LIFECYCLE_EVENTS.indexOf(event) >= 0

if(joinRef && isLifecycleEvent && joinRef !== this.joinRef()){
this.socket.log("channel", "dropping outdated message", {topic, event, payload, joinRef})
if(joinRef && joinRef !== this.joinRef() && this.isLifecycleEvent(event)){
if (this.socket.hasLogger()) this.socket.log("channel", "dropping outdated message", {topic, event, payload, joinRef})
return false
} else {
return true
Expand Down Expand Up @@ -591,8 +595,11 @@ export class Channel {
let handledPayload = this.onMessage(event, payload, ref, joinRef)
if(payload && !handledPayload){ throw("channel onMessage callbacks must return the payload, modified or unmodified") }

this.bindings.filter( bind => bind.event === event)
.map( bind => bind.callback(handledPayload, ref, joinRef || this.joinRef()))
for (let i = 0; i < this.bindings.length; i++) {
const bind = this.bindings[i]
if(bind.event !== event){ continue }
bind.callback(handledPayload, ref, joinRef || this.joinRef())
}
}

/**
Expand Down Expand Up @@ -719,7 +726,7 @@ export class Socket {
this.reconnectAfterMs = opts.reconnectAfterMs || function(tries){
return [1000, 2000, 5000, 10000][tries - 1] || 10000
}
this.logger = opts.logger || function(){} // noop
this.logger = opts.logger || null
this.longpollerTimeout = opts.longpollerTimeout || 20000
this.params = closure(opts.params || {})
this.endPoint = `${endPoint}/${TRANSPORTS.websocket}`
Expand Down Expand Up @@ -788,6 +795,11 @@ export class Socket {
*/
log(kind, msg, data){ this.logger(kind, msg, data) }

/**
* Returns true if a logger has been set on this socket.
*/
hasLogger(){ return this.logger !== null }

/**
* Registers callbacks for connection open events
*
Expand Down Expand Up @@ -822,20 +834,23 @@ export class Socket {
* @private
*/
onConnOpen(){
this.log("transport", `connected to ${this.endPointURL()}`)
if (this.hasLogger()) this.log("transport", `connected to ${this.endPointURL()}`)
this.flushSendBuffer()
this.reconnectTimer.reset()
if(!this.conn.skipHeartbeat){
clearInterval(this.heartbeatTimer)
this.heartbeatTimer = setInterval(() => this.sendHeartbeat(), this.heartbeatIntervalMs)
}
this.resetHeartbeat()
this.stateChangeCallbacks.open.forEach( callback => callback() )
}

/**
* @private
*/

resetHeartbeat(){ if(this.conn.skipHeartbeat){ return }
this.pendingHeartbeatRef = null
clearInterval(this.heartbeatTimer)
this.heartbeatTimer = setInterval(() => this.sendHeartbeat(), this.heartbeatIntervalMs)
}

teardown(callback, code, reason){
if(this.conn){
this.conn.onclose = function(){} // noop
Expand All @@ -846,7 +861,7 @@ export class Socket {
}

onConnClose(event){
this.log("transport", "close", event)
if (this.hasLogger()) this.log("transport", "close", event)
this.triggerChanError()
clearInterval(this.heartbeatTimer)
if(event && event.code !== WS_CLOSE_NORMAL) {
Expand All @@ -859,7 +874,7 @@ export class Socket {
* @private
*/
onConnError(error){
this.log("transport", error)
if (this.hasLogger()) this.log("transport", error)
this.triggerChanError()
this.stateChangeCallbacks.error.forEach( callback => callback(error) )
}
Expand Down Expand Up @@ -912,18 +927,15 @@ export class Socket {
* @param {Object} data
*/
push(data){
let {topic, event, payload, ref, join_ref} = data
let callback = () => {
this.encode(data, result => {
this.conn.send(result)
})
if (this.hasLogger()) {
let {topic, event, payload, ref, join_ref} = data
this.log("push", `${topic} ${event} (${join_ref}, ${ref})`, payload)
}
this.log("push", `${topic} ${event} (${join_ref}, ${ref})`, payload)

if(this.isConnected()){
callback()
}
else {
this.sendBuffer.push(callback)
this.encode(data, result => this.conn.send(result))
} else {
this.sendBuffer.push(() => this.encode(data, result => this.conn.send(result)))
}
}

Expand All @@ -941,7 +953,7 @@ export class Socket {
sendHeartbeat(){ if(!this.isConnected()){ return }
if(this.pendingHeartbeatRef){
this.pendingHeartbeatRef = null
this.log("transport", "heartbeat timeout. Attempting to re-establish connection")
if (this.hasLogger()) this.log("transport", "heartbeat timeout. Attempting to re-establish connection")
this.conn.close(WS_CLOSE_NORMAL, "hearbeat timeout")
return
}
Expand All @@ -961,10 +973,17 @@ export class Socket {
let {topic, event, payload, ref, join_ref} = msg
if(ref && ref === this.pendingHeartbeatRef){ this.pendingHeartbeatRef = null }

this.log("receive", `${payload.status || ""} ${topic} ${event} ${ref && "(" + ref + ")" || ""}`, payload)
this.channels.filter( channel => channel.isMember(topic, event, payload, join_ref) )
.forEach( channel => channel.trigger(event, payload, ref, join_ref) )
this.stateChangeCallbacks.message.forEach( callback => callback(msg) )
if (this.hasLogger()) this.log("receive", `${payload.status || ""} ${topic} ${event} ${ref && "(" + ref + ")" || ""}`, payload)

for (let i = 0; i < this.channels.length; i++) {
const channel = this.channels[i]
if(!channel.isMember(topic, event, payload, join_ref)){ continue }
channel.trigger(event, payload, ref, join_ref)
}

for (let i = 0; i < this.stateChangeCallbacks.message.length; i++) {
this.stateChangeCallbacks.message[i](msg)
}
})
}
}
Expand Down Expand Up @@ -1113,7 +1132,7 @@ export class Ajax {
}

static serialize(obj, parentKey){
let queryStr = [];
let queryStr = []
for(var key in obj){ if(!obj.hasOwnProperty(key)){ continue }
let paramKey = parentKey ? `${parentKey}[${key}]` : key
let paramVal = obj[key]
Expand Down Expand Up @@ -1255,7 +1274,7 @@ export class Presence {
if(currentPresence){
let joinedRefs = state[key].metas.map(m => m.phx_ref)
let curMetas = currentPresence.metas.filter(m => joinedRefs.indexOf(m.phx_ref) < 0)
state[key].metas.unshift(...curMetas);
state[key].metas.unshift(...curMetas)
}
onJoin(key, currentPresence, newPresence)
})
Expand Down

0 comments on commit c3a7207

Please sign in to comment.