Ruby gem for ingesting ATProto repo data from a Tap service (extension of Skyfall gem)

Compare changes

Choose any two refs to compare.

+47 -6
+6
CHANGELOG.md
··· 1 + ## [0.0.2] - 2025-12-23 2 + 3 + - added `#operation` method (aliased as `#op`) to `Operation` 4 + - added `#resolve_did` for calling the `/resolve/:did` API endpoint 5 + - workaround for `isActive` field sent as `is_active` in identity events 6 + 1 7 ## [0.0.1] - 2025-12-22 2 8 3 9 - first working version, with streaming from Tap, support for ack and admin password options, and calling two HTTP endpoints
+7 -2
README.md
··· 33 33 34 34 ## Installation 35 35 36 - Add this to your `Gemfile`: 36 + Tapfall should run on any somewhat recent version of Ruby (3.x/4.x), although it's recommended to use one that's still getting maintenance updates, ideally the latest one. In production, it's also recommended to install it with [YJIT support](https://shopify.engineering/ruby-yjit-is-production-ready) and with [jemalloc](https://scalingo.com/blog/improve-ruby-application-memory-jemalloc). A compatible version should be available on most Linux systems, otherwise you can install one using tools such as [RVM](https://rvm.io), [asdf](https://asdf-vm.com), [ruby-install](https://github.com/postmodern/ruby-install) or [ruby-build](https://github.com/rbenv/ruby-build), or `rpm` or `apt-get` on Linux (see more installation options on [ruby-lang.org](https://www.ruby-lang.org/en/downloads/)). 37 + 38 + To use it in your app, add this to your `Gemfile`: 37 39 38 40 gem 'tapfall' 39 41 ··· 116 118 - `type` (symbol) – the message type identifier, e.g. `:record` 117 119 - `id` (integer), aliased as `seq` – a sequential index of the message 118 120 119 - The `:record` messages have an `operations` method, which includes an array of add/remove/edit `Operation`s done on some records. Currently Tap event format only includes one single record operation in each event, but it's returned as an array here for symmetry with the `Skyfall::Firehose` stream version. 121 + The `:record` messages have an `operation` method (aliased as `op`), which returns an `Operation` object with details of an create/update/delete operation done a record. (For symmetry with the `Skyfall::Firehose` stream version, there's also an `operations` method which returns an array.) 120 122 121 123 An `Operation` has such fields (also matching the API of `Skyfall::Firehose::Operation` and `Skyfall::Jetstream::Operation`): 122 124 ··· 147 149 end 148 150 end 149 151 ``` 152 + 153 + > [!NOTE] 154 + > If you're doing a full network backfill of some app.bsky.* lexicons, that's going to be a *lot* of events that Tap will be sending to you, on localhost (so not limited by network bandwidth), likely in large bursts. In that case it's [recommended](https://bsky.app/profile/did:plc:ragtjsm2j2vknwkz3zp4oxrd/post/3mawmnwukws2w) to try to do as little processing as possible in the event handling loop, and especially avoid any sync network requests there. If you're working with a limited number of repos and/or with non-Bluesky lexicons only, this is probably much less of an issue. 150 155 151 156 152 157 ### Note on custom lexicons
+24
lib/tapfall/api.rb
··· 25 25 post_request('/repos/remove', { dids: dids }) 26 26 end 27 27 28 + def resolve_did(did) 29 + get_request("/resolve/#{did}") 30 + end 31 + 28 32 private 29 33 30 34 def build_root_url(server) ··· 49 53 end 50 54 end 51 55 56 + def get_request(path) 57 + uri = URI(@root_url + path) 58 + 59 + request = Net::HTTP::Get.new(uri) 60 + 61 + if @options[:admin_password] 62 + request.basic_auth('admin', @options[:admin_password]) 63 + end 64 + 65 + response = Net::HTTP.start(uri.hostname, uri.port, :use_ssl => (uri.scheme == 'https')) do |http| 66 + http.request(request) 67 + end 68 + 69 + handle_response(response) 70 + end 71 + 52 72 def post_request(path, json_data) 53 73 uri = URI(@root_url + path) 54 74 ··· 64 84 http.request(request) 65 85 end 66 86 87 + handle_response(response) 88 + end 89 + 90 + def handle_response(response) 67 91 status = response.code.to_i 68 92 message = response.message 69 93 response_body = (response.content_type == 'application/json') ? JSON.parse(response.body) : response.body
+1 -1
lib/tapfall/messages/identity_message.rb
··· 20 20 end 21 21 22 22 def active? 23 - @identity['isActive'] 23 + @identity['isActive'] || @identity['is_active'] 24 24 end 25 25 26 26 def status
+7 -1
lib/tapfall/messages/record_message.rb
··· 8 8 super 9 9 end 10 10 11 + def operation 12 + @operation ||= Operation.new(json['record']) 13 + end 14 + 11 15 def operations 12 - @operations ||= [Operation.new(json['record'])] 16 + [operation] 13 17 end 18 + 19 + alias op operation 14 20 end 15 21 end
+1 -1
lib/tapfall/stream.rb
··· 10 10 class Tapfall::Stream < Skyfall::Stream 11 11 extend Forwardable 12 12 13 - def_delegators :@api, :add_repo, :add_repos, :remove_repo, :remove_repos 13 + def_delegators :@api, :add_repo, :add_repos, :remove_repo, :remove_repos, :resolve_did 14 14 15 15 def initialize(server, options = {}) 16 16 super(server)
+1 -1
lib/tapfall/version.rb
··· 1 1 # frozen_string_literal: true 2 2 3 3 module Tapfall 4 - VERSION = '0.0.1' 4 + VERSION = '0.0.2' 5 5 end