at_protocol 0.0.4

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
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: []