Skip to content
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion .gitmodules
Original file line number Diff line number Diff line change
@@ -1,3 +1,3 @@
[submodule "lib/submodules/ably-common"]
path = lib/submodules/ably-common
path = core/lib/submodules/ably-common
url = https://github.com/ably/ably-common.git
2 changes: 2 additions & 0 deletions .yardopts
Original file line number Diff line number Diff line change
@@ -0,0 +1,2 @@
core/lib/**/*.rb
server/lib/**/*.rb
7 changes: 5 additions & 2 deletions Gemfile
Original file line number Diff line number Diff line change
@@ -1,4 +1,7 @@
source 'https://rubygems.org'

# Specify your gem's dependencies in ably.gemspec
gemspec
# This repo hosts two gems, released in lockstep at the same version (PDR-091b):
# - core/ ably-pubsub-core: the shared implementation (internal package)
# - server/ ably-pubsub-server: the public server-side package (factory entry points)
gemspec path: 'core'
gemspec path: 'server'
6 changes: 3 additions & 3 deletions Rakefile
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
require 'rubygems'
require 'bundler/setup'
require 'bundler/gem_tasks'

require 'json'

require 'yard'
Expand Down Expand Up @@ -32,8 +32,8 @@ begin

desc 'Generate error code constants from ably-common: https://github.com/ably/ably-common/issues/32'
task :generate_error_codes do
errors_json_path = File.join(File.dirname(__FILE__), 'lib/submodules/ably-common/protocol/errors.json')
module_path = File.join(File.dirname(__FILE__), 'lib/ably/modules/exception_codes.rb')
errors_json_path = File.join(File.dirname(__FILE__), 'core/lib/submodules/ably-common/protocol/errors.json')
module_path = File.join(File.dirname(__FILE__), 'core/lib/ably/modules/exception_codes.rb')
max_length = 0

errors = JSON.parse(File.read(errors_json_path)).each_with_object({}) do |(key, val), hash|
Expand Down
14 changes: 14 additions & 0 deletions core/README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,14 @@
# ably-pubsub-core

Internal implementation package for Ably's own Pub/Sub Ruby packages.

**This gem is not intended for direct external use.** It is published only so that
Ably's public packages can depend on it. Use [`ably-pubsub-server`](../server) instead,
which exposes the supported entry points:

```ruby
client = Ably::PubSub::Server.create_http_client(key) # stateless HTTP client
client = Ably::PubSub::Server.create_realtime_client(key) # stateful realtime client
```

`ably-pubsub-core` and `ably-pubsub-server` are released in lockstep at the same version.
16 changes: 8 additions & 8 deletions ably.gemspec → core/ably-pubsub-core.gemspec
Original file line number Diff line number Diff line change
Expand Up @@ -4,18 +4,18 @@ $LOAD_PATH.unshift(lib) unless $LOAD_PATH.include?(lib)
require 'ably/version'

Gem::Specification.new do |spec|
spec.name = 'ably'
spec.name = 'ably-pubsub-core'
spec.version = Ably::VERSION
spec.authors = ['Lewis Marshall', "Matthew O'Riordan"]
spec.email = ['lewis@lmars.net', 'matt@ably.io']
spec.description = %q{A Ruby client library for ably.io realtime messaging}
spec.summary = %q{A Ruby client library for ably.io realtime messaging implemented using EventMachine}
spec.authors = ['Ably']
spec.email = ['support@ably.com']
spec.description = %q{Internal implementation package for Ably's own Pub/Sub packages. Not intended for direct external use: depend on ably-pubsub-server instead.}
spec.summary = %q{Shared core implementation for Ably Pub/Sub Ruby SDKs (internal)}
spec.homepage = 'https://github.com/ably/ably-pubsub-ruby'
spec.license = 'Apache-2.0'

spec.files = `git ls-files`.split($/)
spec.executables = spec.files.grep(%r{^bin/}) { |f| File.basename(f) }
spec.test_files = spec.files.grep(%r{^(test|spec|features)/})
spec.files = Dir.chdir(File.expand_path(__dir__)) do
`git ls-files -z lib`.split("\x0").reject { |f| f.start_with?('lib/submodules') }
end
spec.require_paths = ['lib']

spec.add_runtime_dependency 'eventmachine', '~> 1.2.6'
Expand Down
File renamed without changes.
8 changes: 8 additions & 0 deletions core/lib/ably/agent.rb
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
module Ably
# The SDK family identifier renamed from `ably-ruby` with the per-side package split, so the
# identifier alone partitions the fleet: `ably-ruby/*` is legacy-gem traffic,
# `ably-pubsub-ruby/*` is new-package traffic. It names the family rather than any one
# published gem; the side a client declares travels as a separate versionless agent entry
# (see Ably::PubSub::Server and the agents registry in ably-common).
AGENT = "ably-pubsub-ruby/#{Ably::VERSION} ruby/#{RUBY_VERSION}"
end
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
9 changes: 8 additions & 1 deletion lib/ably/rest/client.rb → core/lib/ably/rest/client.rb
Original file line number Diff line number Diff line change
Expand Up @@ -51,7 +51,7 @@ class Client
# @return [Symbol]
attr_reader :protocol

# Client agent i.e. `example-gem/1.2.0 ably-ruby/1.1.5 ruby/3.1.1`
# Client agent i.e. `example-gem/1.2.0 ably-pubsub-ruby/1.1.5 ruby/3.1.1`
# @return [String]
attr_reader :agent

Expand Down Expand Up @@ -186,6 +186,13 @@ def initialize(options)
end

@agent = options.delete(:agent) || Ably::AGENT
# Additive agent entries (`identifier => version`), appended to the base agent
# string. This is how a package layered on this one (such as ably-pubsub-server)
# declares itself — including the side-declaring entry that MAU classification
# reads — without replacing the base identifiers the way :agent does.
options.delete(:agents).to_h.each do |identifier, version|
@agent = "#{@agent} #{version ? "#{identifier}/#{version}" : identifier}"
end
@realtime_client = options.delete(:realtime_client)
@tls = options.delete_with_default(:tls, true)
@environment = options.delete(:environment) # nil is production
Expand Down
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
2 changes: 1 addition & 1 deletion lib/ably/version.rb → core/lib/ably/version.rb
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
module Ably
VERSION = '1.2.8'
VERSION = '2.0.0'
# The level of compatibility with the Ably service that this SDK supports.
# Also referred to as the 'wire protocol version'.
# spec : CSV2
Expand Down
3 changes: 0 additions & 3 deletions lib/ably/agent.rb

This file was deleted.

33 changes: 33 additions & 0 deletions server/README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,33 @@
# ably-pubsub-server

Ably Pub/Sub client for servers: backend services and other trusted runtimes.

Installing this package declares that its traffic originates from the server side —
[MAU classification](https://ably.com/pricing) is a side effect of the install decision.

## Installation

```ruby
gem 'ably-pubsub-server'
```

## Usage

The factory functions are the only recommended entry points:

```ruby
require 'ably/pubsub/server'

# Stateless HTTP client: publish, history, presence reads, token issuance
http_client = Ably::PubSub::Server.create_http_client('your-api-key')
http_client.channels.get('example').publish('event', 'payload')

# Stateful realtime client: a persistent, live connection (EventMachine-based)
realtime_client = Ably::PubSub::Server.create_realtime_client('your-api-key')
```

Both factories accept everything the underlying constructors accept: an options `Hash`,
an API key `String`, or a token `String`.

This gem is built on `ably-pubsub-core`, an internal implementation package. Depend on
this gem, not on the core.
22 changes: 22 additions & 0 deletions server/ably-pubsub-server.gemspec
Original file line number Diff line number Diff line change
@@ -0,0 +1,22 @@
# coding: utf-8
lib = File.expand_path('../lib', __FILE__)
$LOAD_PATH.unshift(lib) unless $LOAD_PATH.include?(lib)
require 'ably/pubsub/server/version'

Gem::Specification.new do |spec|
spec.name = 'ably-pubsub-server'
spec.version = Ably::PubSub::Server::VERSION
spec.authors = ['Ably']
spec.email = ['support@ably.com']
spec.description = %q{Ably Pub/Sub client for servers: backend services and other trusted runtimes. Construct clients with Ably::PubSub::Server.create_http_client or Ably::PubSub::Server.create_realtime_client.}
spec.summary = %q{Ably Pub/Sub client for servers}
spec.homepage = 'https://github.com/ably/ably-pubsub-ruby'
spec.license = 'Apache-2.0'

spec.files = Dir.chdir(File.expand_path(__dir__)) { `git ls-files -z lib README.md`.split("\x0") }
spec.require_paths = ['lib']

# Exact pin: core and server are released in lockstep at the same version (PDR-091b),
# which also guarantees a consumer can never resolve two different core versions.
spec.add_runtime_dependency 'ably-pubsub-core', "= #{Ably::PubSub::Server::VERSION}"
end
76 changes: 76 additions & 0 deletions server/lib/ably/pubsub/server.rb
Original file line number Diff line number Diff line change
@@ -0,0 +1,76 @@
require 'ably'
require 'ably/pubsub/server/version'

module Ably
module PubSub
# The Ably Pub/Sub SDK for servers. The factory functions here are the only
# recommended entry points of the +ably-pubsub-server+ gem.
module Server
# The agent identifier declaring the server side.
#
# The `-server` suffix is load-bearing, not cosmetic. On API-key auth the realtime
# system grants the MAU server exemption by matching an agent entry ending in
# `-server`, and an identifier that is not yet in the ably-common registry is
# classified by that suffix alone. Renaming it without preserving the suffix
# silently reclassifies every client this package constructs.
#
# The entry is stamped WITHOUT a version, matching its registration in the
# ably-common agents registry (a pure flag, like `browser`): under lockstep
# versioning a version here always duplicates the ably-pubsub-ruby entry beside it,
# which keeps carrying identity, version and support status. Wire shape:
# ably-pubsub-ruby/2.0.0 ruby/3.3.0 ably-pubsub-server
SERVER_AGENT_IDENTIFIER = 'ably-pubsub-server'

class << self
# Creates a stateless HTTP (REST) client declaring the server side.
#
# Accepts everything {Ably::Rest::Client#initialize} accepts: an options Hash,
# an API key String, or a token String.
#
# @return [Ably::Rest::Client]
def create_http_client(options)
Ably::Rest::Client.new(options_with_side_agent(options))
end

# Creates a stateful realtime client declaring the server side.
#
# Accepts everything {Ably::Realtime::Client#initialize} accepts: an options Hash,
# an API key String, or a token String.
#
# @return [Ably::Realtime::Client]
def create_realtime_client(options)
Ably::Realtime::Client.new(options_with_side_agent(options))
end

private

# Returns a copy of the caller's options carrying the agent entry that declares
# this package's side.
#
# The caller's own +:agents+ entries are preserved, so an SDK layered on top of
# this package keeps its attribution. The side entry is merged last and so wins a
# collision on its own identifier: which side the package declares is the
# package's to state, not the caller's to redefine.
#
# A nil argument passes through unchanged so the caller gets the core
# constructor's own initialization error rather than a vaguer failure later.
def options_with_side_agent(options)
return options if options.nil?

options = if options.kind_of?(String)
if options.match(Ably::Auth::API_KEY_REGEX)
{ key: options }
else
{ token: options }
end
else
options.clone
end

agents = options[:agents].to_h.merge(SERVER_AGENT_IDENTIFIER => nil)
options.merge(agents: agents)
end
end
end
end
end
9 changes: 9 additions & 0 deletions server/lib/ably/pubsub/server/version.rb
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
module Ably
module PubSub
module Server
# Must match Ably::VERSION in ably-pubsub-core: core and server are released in
# lockstep at the same version (PDR-091b). The release workflow and a spec assert this.
VERSION = '2.0.0'
end
end
end
2 changes: 1 addition & 1 deletion spec/acceptance/realtime/connection_spec.rb
Original file line number Diff line number Diff line change
Expand Up @@ -2005,7 +2005,7 @@ def self.available_states
it 'sends the lib version param agent (#RCS7d)' do
expect(EventMachine).to receive(:connect) do |host, port, transport, object, url|
uri = URI.parse(url)
expect(CGI::parse(uri.query)['agent'][0]).to match(/^ably-ruby\/\d\.\d\.\d ruby\/\d\.\d\.\d$/)
expect(CGI::parse(uri.query)['agent'][0]).to match(/^ably-pubsub-ruby\/\d+\.\d+\.\d+ ruby\/\d+\.\d+\.\d+$/)
stop_reactor
end
client
Expand Down
4 changes: 2 additions & 2 deletions spec/acceptance/realtime/message_spec.rb
Original file line number Diff line number Diff line change
Expand Up @@ -485,7 +485,7 @@ def publish_and_check_extras(extras)
end
end

resources_root = File.expand_path('../../../../lib/submodules/ably-common/test-resources', __FILE__)
resources_root = File.expand_path('../../../../core/lib/submodules/ably-common/test-resources', __FILE__)

shared_examples 'add_tests_for_data' do |data|
data['items'].each_with_index do |item, index|
Expand Down Expand Up @@ -823,7 +823,7 @@ def publish_and_check_extras(extras)
let(:client_options) { { key: api_key, environment: environment, protocol: :json } }
let(:channel_name) { "subscribe_send_text-#{random_str}" }

fixtures_path = File.expand_path('../../../../lib/submodules/ably-common/test-resources/messages-encoding.json', __FILE__)
fixtures_path = File.expand_path('../../../../core/lib/submodules/ably-common/test-resources/messages-encoding.json', __FILE__)

context 'over a JSON transport' do
let(:realtime_client) do
Expand Down
2 changes: 1 addition & 1 deletion spec/acceptance/rest/message_spec.rb
Original file line number Diff line number Diff line change
Expand Up @@ -395,7 +395,7 @@ def mock_for_two_publish_failures
end
end

resources_root = File.expand_path('../../../../lib/submodules/ably-common/test-resources', __FILE__)
resources_root = File.expand_path('../../../../core/lib/submodules/ably-common/test-resources', __FILE__)

def self.add_tests_for_data(data)
data['items'].each_with_index do |item, index|
Expand Down
2 changes: 1 addition & 1 deletion spec/support/test_app.rb
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
require 'singleton'

class TestApp
TEST_RESOURCES_PATH = File.expand_path('../../../lib/submodules/ably-common/test-resources', __FILE__)
TEST_RESOURCES_PATH = File.expand_path('../../../core/lib/submodules/ably-common/test-resources', __FILE__)

# App configuration for test app
# See https://github.com/ably/ably-common/blob/main/test-resources/test-app-setup.json
Expand Down
Loading
Loading