at_protocol 0.0.4

Sign up to get free protection for your applications and to get access to all the features.
checksums.yaml ADDED
@@ -0,0 +1,7 @@
1
+ ---
2
+ SHA256:
3
+ metadata.gz: 88028815424d12616955d54125fb23ad627812290c2c52a8d491bb108ebd54fa
4
+ data.tar.gz: d68b5af3850c75174ef573809f2d87a4012c707d82079601cdd3351da5c317ed
5
+ SHA512:
6
+ metadata.gz: 37cdd260697f6461ed9215f4d06b932988a292a5fc53afde8d555a1dc04c5355819026742528997d5c2d476a9618ddf396a972f794c73cab11784e6fff39c520
7
+ data.tar.gz: 5cd9f10e044b3edfdc37ce71abd01ae53fd66bfe4fa9669157d0a2f507e5d6190f3ed4b89490fb012dc9868ac2c7e2722fa35967428934cf1efec697c6def618
@@ -0,0 +1,99 @@
1
+ # typed: false
2
+
3
+ module ATProto
4
+ module RequestUtils
5
+ extend T::Sig
6
+ include Kernel
7
+
8
+ sig { params(url: String, atp_host: String).returns(T.nilable(AtUri)) }
9
+
10
+ def at_uri(url, atp_host = "https://bsky.social")
11
+ AtUriParser.parse(url, AtUriParser::RuleSets, pds: atp_host)
12
+ end
13
+
14
+ module_function :at_uri
15
+ end
16
+ end
17
+
18
+ module ATProto
19
+ CID = Skyfall::CID
20
+
21
+ module AtUriParser
22
+ extend T::Sig
23
+
24
+ class << self
25
+ include RequestUtils
26
+ end
27
+
28
+ Rule = Struct.new(:pattern, :transform)
29
+
30
+ sig { params(url: T.any(String, AtUri), rulesets: T::Array[Rule], pds: String).returns(T.nilable(AtUri)) }
31
+ def self.parse(url, rulesets, pds: "https://bsky.social")
32
+ return url if url.is_a?(AtUri)
33
+ rulesets.each do |ruleset|
34
+ match_data = url.match(ruleset.pattern)
35
+ next unless match_data
36
+
37
+ at_uri = ruleset.transform.call(match_data, pds)
38
+ return at_uri if at_uri.is_a?(AtUri)
39
+ end
40
+
41
+ nil
42
+ end
43
+
44
+ def self.create_rule(pattern, &block)
45
+ transform = Proc.new do |match_data, pds|
46
+ block.call(*match_data.captures, pds)
47
+ end
48
+ Rule.new(pattern, transform)
49
+ end
50
+ RuleSets = [
51
+ AtUriParser.create_rule(%r{^#{Regexp.escape("https://")}(bsky\.app)/profile/(.+)/post/([\w]+)$}) do |_, handle, rkey, pds|
52
+ handle.start_with?("did:") ? did = handle : did = resolve_handle(handle, pds)
53
+ AtUri.new(repo: did, collection: "app.bsky.feed.post", rkey: rkey)
54
+ end,
55
+
56
+ AtUriParser.create_rule(%r{^#{Regexp.escape("https://")}(bsky\.app)/profile/(.+)$}) do |_, handle, pds|
57
+ handle.start_with?("did:") ? did = handle : did = resolve_handle(handle, pds)
58
+ AtUri.new(repo: did)
59
+ end,
60
+
61
+ AtUriParser.create_rule(%r{^at://(.+)/(.+)/(\w+)$}) do |handle, collection, rkey, pds|
62
+ handle.start_with?("did:") ? did = handle : did = resolve_handle(handle, pds)
63
+ AtUri.new(repo: did, collection: collection, rkey: rkey)
64
+ end,
65
+ AtUriParser.create_rule(%r{^at://(.+)/(.+)$}) do |handle, collection, pds|
66
+ handle.start_with?("did:") ? did = handle : did = resolve_handle(handle, pds)
67
+ AtUri.new(repo: did, collection: collection)
68
+ end,
69
+ AtUriParser.create_rule(%r{^at://(.+)$}) do |handle, pds|
70
+ handle.start_with?("did:") ? did = handle : did = resolve_handle(handle, pds)
71
+ AtUri.new(repo: did)
72
+ end,
73
+
74
+ ]
75
+ end
76
+
77
+ class AtUri < T::Struct
78
+ extend T::Sig
79
+ const :repo, T.any(ATProto::Repo, String)
80
+ const :collection, T.nilable(T.any(ATProto::Repo::Collection, String))
81
+ const :rkey, T.nilable(String)
82
+
83
+ def resolve(pds: "https://bsky.social")
84
+ if @collection.nil?
85
+ Repo.new(@repo.to_s, pds)
86
+ elsif @rkey.nil?
87
+ Repo::Collection.new(Repo.new(@repo.to_s, pds), @collection.to_s, pds)
88
+ else
89
+ Record.from_uri(self, pds)
90
+ end
91
+ end
92
+
93
+ sig { returns(String) }
94
+
95
+ def to_s
96
+ "at://#{@repo}/#{@collection.nil? ? "" : "#{@collection}/"}#{@rkey}"
97
+ end
98
+ end
99
+ end
@@ -0,0 +1,62 @@
1
+ # typed: true
2
+ module ATProto
3
+ class Repo
4
+ class Collection < T::Struct
5
+ include RequestUtils
6
+ include Enumerable
7
+ extend T::Sig
8
+
9
+ const(:repo, ATProto::Repo)
10
+ const(:collection, String)
11
+
12
+ sig { params(limit: Integer).returns(T::Array[ATProto::Record]) }
13
+
14
+ def list(limit = 10)
15
+ self.repo.xrpc
16
+ .get.com_atproto_repo_listRecords(
17
+ repo: self.repo.did,
18
+ collection: self.collection,
19
+ limit: limit,
20
+ )["records"]
21
+ .map { |record|
22
+ ATProto::Record.from_hash(record)
23
+ }
24
+ end
25
+
26
+ def list_all()
27
+ T.must(get_paginated_data(self.repo, :com_atproto_repo_listRecords.to_s, key: "records", params: { repo: self.repo.to_s, collection: self.to_s }, cursor: nil) do |record|
28
+ ATProto::Record.from_hash(record)
29
+ end)
30
+ end
31
+
32
+ sig { returns(String) }
33
+
34
+ def to_uri
35
+ "at://#{self.repo.did}/#{self.collection}/"
36
+ end
37
+
38
+ sig { returns(String) }
39
+
40
+ def to_s
41
+ @collection
42
+ end
43
+
44
+ sig { params(rkey: String).returns(T.nilable(ATProto::Record)) }
45
+
46
+ def [](rkey)
47
+ ATProto::Record.from_uri(
48
+ T.must(
49
+ at_uri(
50
+ "at://#{self.repo.did}/#{@collection}/#{rkey}"
51
+ )
52
+ ),
53
+ self.repo.pds
54
+ )
55
+ end
56
+
57
+ def each(&block)
58
+ list_all.each(&block)
59
+ end
60
+ end
61
+ end
62
+ end
@@ -0,0 +1,90 @@
1
+ # typed: false
2
+ module ATProto
3
+ class Record < T::Struct
4
+ const(:uri, ATProto::AtUri)
5
+ const(:cid, Skyfall::CID)
6
+ const(:timestamp, T.untyped)
7
+ prop(:content, Hash)
8
+ extend T::Sig
9
+ class << self
10
+ extend T::Sig
11
+ include RequestUtils
12
+
13
+ sig { params(json_hash: Hash).returns(T.nilable(ATProto::Record)) }
14
+
15
+ def from_hash(json_hash)
16
+ return nil if json_hash["value"].nil?
17
+ timestamp = nil
18
+ timestamp = Time.parse json_hash["value"]["createdAt"] if json_hash["value"] && json_hash["value"]["createdAt"]
19
+ raw_content = json_hash["value"]
20
+ new(
21
+ uri: at_uri(
22
+ T.must(json_hash["uri"])
23
+ ),
24
+ cid: CID.from_json(json_hash["cid"]),
25
+ timestamp: timestamp, content: raw_content,
26
+ )
27
+ end
28
+
29
+ sig { params(uri: ATProto::AtUri, pds: String).returns(T.nilable(ATProto::Record)) }
30
+
31
+ def from_uri(uri, pds = "https://bsky.social")
32
+ from_hash(XRPC::Client.new(pds).get.com_atproto_repo_getRecord(
33
+ repo: uri.repo.to_s,
34
+ collection: "#{uri.collection}",
35
+ rkey: uri.rkey,
36
+ ))
37
+ end
38
+
39
+ sig { params(content_hash: Hash, session: ATProto::Session, rkey: T.nilable(String)).returns(T.nilable(ATProto::Record)) }
40
+
41
+ def create(content_hash, session, rkey = nil)
42
+ return nil if content_hash["$type"].nil?
43
+ params = {
44
+ repo: session.did,
45
+ collection: content_hash["$type"],
46
+ record: content_hash,
47
+ }
48
+ params[:rkey] = rkey unless rkey.nil?
49
+ from_uri(at_uri(session.xrpc.post.com_atproto_repo_createRecord(params)["uri"]))
50
+ end
51
+ end
52
+ dynamic_attr_reader(:to_uri) { "at://#{self.uri.repo}/#{self.uri.collection}/#{self.uri.rkey}" }
53
+ dynamic_attr_reader(:strongref) { StrongRef.new(uri: self.uri, cid: self.cid) }
54
+
55
+ def refresh(pds = "https://bsky.social")
56
+ self.class.from_uri(self.uri, pds)
57
+ end
58
+
59
+ sig { params(session: ATProto::Session).returns(T.nilable(ATProto::Record)) }
60
+
61
+ def put(session)
62
+ session.then(&to_write(:update)).uri.resolve(pds: session.pds)
63
+ end
64
+
65
+ sig { params(session: ATProto::Session).returns(T.nilable(Integer)) }
66
+
67
+ def delete(session)
68
+ session.then(&to_write)
69
+ end
70
+
71
+ sig { params(type: Symbol).returns(ATProto::Writes::Write) }
72
+
73
+ def to_write(type = :delete)
74
+ ATProto::Writes::Write.new(
75
+ {
76
+ action: Writes::Write::Action.deserialize(type),
77
+ value: (self.content if type == :update),
78
+ collection: self.uri.collection,
79
+ rkey: self.uri.rkey,
80
+ }.compact
81
+ )
82
+ end
83
+
84
+ sig { params(other: ATProto::Record).returns(T::Boolean) }
85
+
86
+ def ==(other)
87
+ self.cid.to_s == other.cid.to_s
88
+ end
89
+ end
90
+ end
@@ -0,0 +1,67 @@
1
+ # typed: true
2
+
3
+ class ATProto::Repo
4
+ include ATProto::RequestUtils
5
+ extend T::Sig
6
+
7
+ sig { params(username: String, pds: String, open: T::Boolean, authenticate: T.nilable(ATProto::Session)).void }
8
+
9
+ # @param username [String] The username or DID (Decentralized Identifier) to use.
10
+ # @param pds [String] The URL of the personal data server (default: "https://bsky.social").
11
+ # @param open [Boolean] Whether to open the repository or not (default: true).
12
+ # @param authenticate [NilClass, Object] Additional authentication data (default: nil).
13
+
14
+ def initialize(username, pds = "https://bsky.social", open: true, authenticate: nil)
15
+ @pds = T.let pds, String
16
+ @xrpc = T.let(XRPC::Client.new(pds), XRPC::Client)
17
+ if username.start_with?("did:")
18
+ @did = T.let(username, String)
19
+ else
20
+ @did = T.let(resolve_handle(username, pds), String)
21
+ end
22
+ @record_list = []
23
+ if open == true
24
+ open!
25
+ end
26
+ end
27
+
28
+ def open!
29
+ @collections = describe_repo["collections"]
30
+ end
31
+
32
+ sig { returns(String) }
33
+
34
+ def to_uri
35
+ "at://#{@did}/"
36
+ end
37
+
38
+ sig { returns(String) }
39
+
40
+ def to_s
41
+ @did
42
+ end
43
+
44
+ sig { returns(Hash) }
45
+
46
+ def describe_repo
47
+ @xrpc.get.com_atproto_repo_describeRepo(repo: @did)
48
+ end
49
+
50
+ sig { returns(Hash) }
51
+
52
+ def did_document
53
+ describe_repo()["didDoc"]
54
+ end
55
+
56
+ sig { params(collection: String).returns(ATProto::Repo::Collection) }
57
+
58
+ def [](collection)
59
+ Collection.new(repo: self, collection: collection)
60
+ end
61
+
62
+ def inspect
63
+ "Repo(#{@did})"
64
+ end
65
+
66
+ attr_reader :did, :record_list, :pds, :xrpc
67
+ end
@@ -0,0 +1,125 @@
1
+ # typed: true
2
+ module ATProto
3
+ class Error < StandardError; end
4
+
5
+ class HTTPError < Error; end
6
+
7
+ class UnauthorizedError < HTTPError; end
8
+
9
+ module RequestUtils # Goal is to replace with pure XRPC eventually
10
+ extend T::Sig
11
+
12
+ def resolve_handle(username, pds = "https://bsky.social")
13
+ (XRPC::Client.new(pds).get.com_atproto_identity_resolveHandle(handle: username))["did"]
14
+ end
15
+
16
+ def query_obj_to_query_params(q)
17
+ out = "?"
18
+ q.to_h.each do |key, value|
19
+ out += "#{key}=#{value}&" unless value.nil? || (value.class.method_defined?(:empty?) && value.empty?)
20
+ end
21
+ out.slice(0...-1)
22
+ end
23
+
24
+ def default_headers
25
+ { "Content-Type" => "application/json" }
26
+ end
27
+
28
+ def create_session_uri(pds)
29
+ "#{pds}/xrpc/com.atproto.server.createSession"
30
+ end
31
+
32
+ def delete_session_uri(pds)
33
+ "#{pds}/xrpc/com.atproto.server.deleteSession"
34
+ end
35
+
36
+ def refresh_session_uri(pds)
37
+ "#{pds}/xrpc/com.atproto.server.refreshSession"
38
+ end
39
+
40
+ def get_session_uri(pds)
41
+ "#{pds}/xrpc/com.atproto.server.getSession"
42
+ end
43
+
44
+ def delete_record_uri(pds)
45
+ "#{pds}/xrpc/com.atproto.repo.deleteRecord"
46
+ end
47
+
48
+ def mute_actor_uri(pds)
49
+ "#{pds}/xrpc/app.bsky.graph.muteActor"
50
+ end
51
+
52
+ def upload_blob_uri(pds)
53
+ "#{pds}/xrpc/com.atproto.repo.uploadBlob"
54
+ end
55
+
56
+ def get_post_thread_uri(pds, query)
57
+ "#{pds}/xrpc/app.bsky.feed.getPostThread#{query_obj_to_query_params(query)}"
58
+ end
59
+
60
+ def default_authenticated_headers(session)
61
+ default_headers.merge({
62
+ Authorization: "Bearer #{session.access_token}",
63
+ })
64
+ end
65
+
66
+ def refresh_token_headers(session)
67
+ default_headers.merge({
68
+ Authorization: "Bearer #{session.refresh_token}",
69
+ })
70
+ end
71
+
72
+ sig {
73
+ params(
74
+ session: T.any(ATProto::Session, ATProto::Repo),
75
+ method: String,
76
+ key: String,
77
+ params: Hash,
78
+ cursor: T.nilable(
79
+ String
80
+ ),
81
+ map_block: T.nilable(Proc),
82
+ )
83
+ .returns(T
84
+ .nilable(
85
+ Array
86
+ ))
87
+ }
88
+
89
+ def get_paginated_data(session, method, key:, params:, cursor: nil, &map_block)
90
+ params.merge({ limit: 100, cursor: cursor }).then do |send_data|
91
+ session.xrpc.get.public_send(method, **send_data).then do |response|
92
+ response.dig(key).then do |data|
93
+ if data.nil? || data.empty?
94
+ return []
95
+ end
96
+
97
+ if block_given?
98
+ data.map(&map_block).then do |results|
99
+ response.dig("cursor").then do |next_cursor|
100
+ if next_cursor.nil?
101
+ return results
102
+ else
103
+ get_paginated_data(session, method, key: key, params: params, cursor: next_cursor, &map_block).then do |next_results|
104
+ return results + next_results
105
+ end
106
+ end
107
+ end
108
+ end
109
+ else
110
+ response.dig("cursor").then do |next_cursor|
111
+ if next_cursor.nil?
112
+ return data
113
+ else
114
+ get_paginated_data(session, method, key: key, params: params, cursor: next_cursor, &map_block).then do |next_results|
115
+ return data + next_results
116
+ end
117
+ end
118
+ end
119
+ end
120
+ end
121
+ end
122
+ end
123
+ end
124
+ end
125
+ end
@@ -0,0 +1,88 @@
1
+ # typed: true
2
+
3
+ module ATProto
4
+ Credentials = Struct.new :username, :pw, :pds do
5
+ extend T::Sig
6
+
7
+ sig { params(username: String, pw: String, pds: String).void }
8
+
9
+ def initialize(username, pw, pds = "https://bsky.social")
10
+ super
11
+ self.pds ||= "https://bsky.social"
12
+ end
13
+ end
14
+
15
+ class Session
16
+ include RequestUtils
17
+ extend T::Sig
18
+
19
+ attr_reader :pds, :access_token, :refresh_token, :did, :xrpc
20
+
21
+ sig { params(credentials: ATProto::Credentials, should_open: T::Boolean).void }
22
+
23
+ def initialize(credentials, should_open = true)
24
+ @credentials = credentials
25
+ @pds = credentials.pds
26
+ open! if should_open
27
+ end
28
+
29
+ def open!
30
+ @xrpc = XRPC::Client.new(@pds)
31
+ response = @xrpc.post.com_atproto_server_createSession(identifier: @credentials.username, password: @credentials.pw)
32
+
33
+ raise UnauthorizedError if response["accessJwt"].nil?
34
+
35
+ @access_token = response["accessJwt"]
36
+ @refresh_token = response["refreshJwt"]
37
+ @did = response["did"]
38
+
39
+ @xrpc = XRPC::Client.new(@pds, @access_token)
40
+ @refresher = XRPC::Client.new(@pds, @refresh_token)
41
+ end
42
+
43
+ def refresh!
44
+ response = @refresher.post.com_atproto_server_refreshSession
45
+ raise UnauthorizedError if response["accessJwt"].nil?
46
+ @access_token = response["accessJwt"]
47
+ @refresh_token = response["refreshJwt"]
48
+ @xrpc = XRPC::Client.new(@pds, @access_token)
49
+ @refresher = XRPC::Client.new(@pds, @refresh_token)
50
+ end
51
+
52
+ sig { returns(T.nilable(Hash)) }
53
+
54
+ def get_session
55
+ @xrpc.get.com_atproto_server_getSession
56
+ end
57
+
58
+ def delete!
59
+ response = HTTParty.post(
60
+ URI(delete_session_uri(pds)),
61
+ headers: refresh_token_headers(self),
62
+ )
63
+ if response.code == 200
64
+ { success: true }
65
+ else
66
+ raise UnauthorizedError
67
+ end
68
+ end
69
+ end
70
+ end
71
+
72
+ module ATProto
73
+ class TokenSession < Session
74
+ extend T::Sig
75
+
76
+ sig { params(token: String, pds: String).void }
77
+
78
+ def initialize(token, pds = "https://bsky.social")
79
+ @token = token
80
+ @pds = pds
81
+ open!
82
+ end
83
+
84
+ def open!
85
+ @xrpc = XRPC::Client.new(@pds, @token)
86
+ end
87
+ end
88
+ end
@@ -0,0 +1,4 @@
1
+ # typed: strict
2
+ module ATProto
3
+ VERSION = "0.0.4"
4
+ end
@@ -0,0 +1,155 @@
1
+ # typed: true
2
+ module ATProto
3
+ class Writes < T::Struct
4
+ class Write < T::Struct
5
+ class Action < T::Enum
6
+ enums do
7
+ Create = new(:create)
8
+ Update = new(:update)
9
+ Delete = new(:delete)
10
+ end
11
+ end
12
+
13
+ extend T::Sig
14
+ prop(:action, Action)
15
+ prop(:value, T.nilable(Hash))
16
+ prop(:collection, String)
17
+ prop(:rkey, T.nilable(String))
18
+
19
+ sig { returns(T::Hash[Symbol, T.any(String, Symbol, Hash)]) }
20
+
21
+ def to_h
22
+ {
23
+ :"$type" => "com.atproto.repo.applyWrites##{self.action.serialize}",
24
+ action: self.action.serialize,
25
+ value: self.value || nil,
26
+ collection: self.collection,
27
+ rkey: self.rkey || nil,
28
+ }.compact
29
+ end
30
+
31
+ ## If you want to use with individual actions instead of applyWrites:
32
+ def endpoint_name
33
+ case self.action
34
+ when Action::Create
35
+ "com_atproto_repo_createRecord"
36
+ when Action::Update
37
+ "com_atproto_repo_putRecord"
38
+ when Action::Delete
39
+ "com_atproto_repo_deleteRecord"
40
+ end
41
+ end
42
+
43
+ def to_individual_hash(session)
44
+ case self.action
45
+ when Action::Create
46
+ {
47
+ repo: session.did,
48
+ collection: self.collection.to_s,
49
+ record: self.value,
50
+ rkey: self.rkey || nil,
51
+ }.compact
52
+ when Action::Update
53
+ {
54
+ repo: session.did,
55
+ collection: self.collection.to_s,
56
+ rkey: self.rkey,
57
+ record: self.value,
58
+ }.compact
59
+ when Action::Delete
60
+ {
61
+ repo: session.did,
62
+ collection: self.collection.to_s,
63
+ rkey: self.rkey,
64
+ }.compact
65
+ end
66
+ end
67
+
68
+ def to_proc
69
+ ->session {
70
+ session.xrpc.post.send(self.endpoint_name, **self.to_individual_hash(session)).then do |response|
71
+ if response.is_a?(Numeric)
72
+ response
73
+ elsif response.is_a?(Hash)
74
+ ATProto::Record::StrongRef.new(
75
+ uri: RequestUtils.at_uri(response["uri"]),
76
+ cid: Skyfall::CID.from_json(response["cid"]),
77
+ )
78
+ end
79
+ end
80
+ }
81
+ end
82
+ end
83
+
84
+ extend T::Sig
85
+ prop(:writes, T::Array[Write])
86
+ prop(:repo, ATProto::Repo)
87
+ prop(:session, ATProto::Session)
88
+
89
+ sig { returns(Hash) }
90
+
91
+ def to_h
92
+ {
93
+ writes: self.writes.map(&:to_h).compact,
94
+ repo: self.repo.to_s,
95
+ }.compact
96
+ end
97
+
98
+ def apply
99
+ self.session.xrpc.post.com_atproto_repo_applyWrites(**to_h)
100
+ end
101
+
102
+ class << self
103
+ extend T::Sig
104
+ sig { params(block: Proc).returns(T::Array[Write]) }
105
+
106
+ def generate(&block)
107
+ Collector.new.instance_eval(&block)
108
+ end
109
+ end
110
+ end
111
+
112
+ class Writes
113
+ class Collector
114
+ include RequestUtils
115
+ extend T::Sig
116
+
117
+ def initialize
118
+ @writes = []
119
+ end
120
+
121
+ sig { params(hash: Hash).returns(T::Array[Write]) }
122
+
123
+ def create(hash)
124
+ @writes << Write.new({
125
+ action: Write::Action::Create,
126
+ value: hash,
127
+ collection: hash["$type"] || hash[:"$type"],
128
+ })
129
+ end
130
+
131
+ sig { params(uri: T.any(String, ATProto::AtUri), hash: Hash).returns(T::Array[Write]) }
132
+
133
+ def update(uri, hash)
134
+ aturi = at_uri(uri)
135
+ @writes << Write.new({
136
+ action: Write::Action::Update,
137
+ value: hash,
138
+ collection: T.must(aturi).collection.to_s,
139
+ rkey: T.must(aturi).rkey,
140
+ })
141
+ end
142
+
143
+ sig { params(uri: T.any(String, ATProto::AtUri)).returns(T::Array[Write]) }
144
+
145
+ def delete(uri)
146
+ aturi = at_uri(uri)
147
+ @writes << Write.new({
148
+ action: Write::Action::Delete,
149
+ collection: T.must(aturi).collection.to_s,
150
+ rkey: T.must(aturi).rkey,
151
+ })
152
+ end
153
+ end
154
+ end
155
+ end
@@ -0,0 +1,27 @@
1
+ # typed: true
2
+ # frozen_string_literal: true
3
+ require "sorbet-runtime"
4
+
5
+ class Class
6
+ def dynamic_attr_reader(attr_name, &block)
7
+ define_method(attr_name) do
8
+ instance_variable = "@#{attr_name}"
9
+ if instance_variable_defined?(instance_variable)
10
+ instance_variable_get(instance_variable)
11
+ else
12
+ instance_variable_set(instance_variable, instance_eval(&block))
13
+ end
14
+ end
15
+ end
16
+ end
17
+
18
+ require :"skyfall/cid".to_s
19
+ require "xrpc"
20
+ require "at_protocol/requests"
21
+ require "at_protocol/session"
22
+ require "at_protocol/repo"
23
+ require "at_protocol/collection"
24
+ require "at_protocol/at_uri"
25
+ require "at_protocol/writes"
26
+ require "at_protocol/record"
27
+ require "at_protocol/helpers/strongref"
metadata ADDED
@@ -0,0 +1,67 @@
1
+ --- !ruby/object:Gem::Specification
2
+ name: at_protocol
3
+ version: !ruby/object:Gem::Version
4
+ version: 0.0.4
5
+ platform: ruby
6
+ authors:
7
+ - Shreyan Jain
8
+ - Tynan Burke
9
+ autorequire:
10
+ bindir: bin
11
+ cert_chain: []
12
+ date: 2023-08-16 00:00:00.000000000 Z
13
+ dependencies:
14
+ - !ruby/object:Gem::Dependency
15
+ name: xrpc
16
+ requirement: !ruby/object:Gem::Requirement
17
+ requirements:
18
+ - - ">="
19
+ - !ruby/object:Gem::Version
20
+ version: 0.1.5
21
+ type: :runtime
22
+ prerelease: false
23
+ version_requirements: !ruby/object:Gem::Requirement
24
+ requirements:
25
+ - - ">="
26
+ - !ruby/object:Gem::Version
27
+ version: 0.1.5
28
+ description: A Ruby gem for interacting with AT Protocol
29
+ email:
30
+ - shreyan.jain.9@outlook.com
31
+ executables: []
32
+ extensions: []
33
+ extra_rdoc_files: []
34
+ files:
35
+ - "./lib/at_protocol.rb"
36
+ - "./lib/at_protocol/at_uri.rb"
37
+ - "./lib/at_protocol/collection.rb"
38
+ - "./lib/at_protocol/record.rb"
39
+ - "./lib/at_protocol/repo.rb"
40
+ - "./lib/at_protocol/requests.rb"
41
+ - "./lib/at_protocol/session.rb"
42
+ - "./lib/at_protocol/version.rb"
43
+ - "./lib/at_protocol/writes.rb"
44
+ homepage: https://github.com/ShreyanJain9/at_protocol
45
+ licenses:
46
+ - MIT
47
+ metadata: {}
48
+ post_install_message:
49
+ rdoc_options: []
50
+ require_paths:
51
+ - lib
52
+ required_ruby_version: !ruby/object:Gem::Requirement
53
+ requirements:
54
+ - - ">="
55
+ - !ruby/object:Gem::Version
56
+ version: '0'
57
+ required_rubygems_version: !ruby/object:Gem::Requirement
58
+ requirements:
59
+ - - ">="
60
+ - !ruby/object:Gem::Version
61
+ version: '0'
62
+ requirements: []
63
+ rubygems_version: 3.4.15
64
+ signing_key:
65
+ specification_version: 4
66
+ summary: Interact with the AT Protocol using Ruby
67
+ test_files: []