urbit-api 0.2.1 → 0.5.0

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.
data/lib/urbit/graph.rb CHANGED
@@ -4,12 +4,13 @@ require 'urbit/parser'
4
4
 
5
5
  module Urbit
6
6
  class Graph
7
- attr_reader :host_ship_name, :name, :ship
7
+ attr_reader :host_ship_name, :name, :ship
8
8
 
9
9
  def initialize(ship:, graph_name:, host_ship_name:)
10
10
  @ship = ship
11
- @name = graph_name
11
+ @group = nil
12
12
  @host_ship_name = host_ship_name
13
+ @name = graph_name
13
14
  @nodes = SortedSet.new
14
15
  end
15
16
 
@@ -17,6 +18,28 @@ module Urbit
17
18
  @nodes << node unless node.deleted?
18
19
  end
19
20
 
21
+ def creator
22
+ self.fetch_link if @creator.nil?
23
+ @creator
24
+ end
25
+
26
+ def description
27
+ self.fetch_link if @description.nil?
28
+ @description
29
+ end
30
+
31
+ def group
32
+ if @group.nil?
33
+ @link = self.fetch_link
34
+ @group = @link.group unless @link.nil?
35
+ end
36
+ @group
37
+ end
38
+
39
+ def group=(a_group)
40
+ @group = a_group
41
+ end
42
+
20
43
  def host_ship
21
44
  "~#{@host_ship_name}"
22
45
  end
@@ -84,6 +107,16 @@ module Urbit
84
107
  self.fetch_sibling_nodes(node, :older, count)[0..(count - 1)]
85
108
  end
86
109
 
110
+ def title
111
+ self.fetch_link if @title.nil?
112
+ @title
113
+ end
114
+
115
+ def type
116
+ self.fetch_link if @type.nil?
117
+ @type
118
+ end
119
+
87
120
  #
88
121
  # the canonical printed representation of a Graph
89
122
  def to_s
@@ -98,6 +131,15 @@ module Urbit
98
131
  "add-graph")
99
132
  end
100
133
 
134
+ def fetch_link
135
+ @link = self.ship.links.find_graph(resource: self.resource)
136
+ @creator = @link.metadata['creator']
137
+ @description = @link.metadata['description']
138
+ @title = @link.metadata['title']
139
+ @type = @link.metadata['config']['graph']
140
+ @link
141
+ end
142
+
101
143
  def fetch_newest_nodes(count)
102
144
  self.fetch_nodes("#{self.graph_resource}/node/siblings/newest/kith/#{count}/",
103
145
  AddNodesParser,
@@ -0,0 +1,164 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Urbit
4
+ class Group
5
+ attr_accessor :graphs, :manager, :members, :tags, :title
6
+ attr_reader :hidden, :path, :policy
7
+
8
+ def initialize(path:, members:, policy:, tags:, hidden:)
9
+ @graphs = Set.new
10
+ @hidden = hidden
11
+ @manager = nil
12
+ @members = Set.new(members)
13
+ @path = path
14
+ @policy = policy
15
+ @tags = self.parse_tags(tags)
16
+ end
17
+
18
+ def ==(another_group)
19
+ another_group.path == self.path
20
+ end
21
+
22
+ def <=>(another_group)
23
+ self.path <=> another_group.path
24
+ end
25
+
26
+ def creator
27
+ self.fetch_link if @creator.nil?
28
+ @creator
29
+ end
30
+
31
+ def description
32
+ self.fetch_link if @description.nil?
33
+ @description
34
+ end
35
+
36
+ #
37
+ # This is the action labeled as "Archive" in the Landscape UI.
38
+ # As of now, you can only do this to groups on your own ship.
39
+ #
40
+ def delete
41
+ if (self.host == self.manager.ship.name)
42
+ spdr = self.manager.spider('group-delete', %Q({"remove": {"ship": "#{self.host}", "name": "#{self.key}"}}))
43
+ self.manager.remove(self) if 200 == spdr[:status]
44
+ return spdr
45
+ end
46
+ {status: 400, code: 'bad_request', body: 'Can only delete Groups on your own ship.'}
47
+ end
48
+
49
+ def eql?(another_group)
50
+ another_group.path == self.path
51
+ end
52
+
53
+ def graphs
54
+ if @graphs.empty?
55
+ self.graph_links.each {|gl| @graphs << gl.graph}
56
+ end
57
+ @graphs
58
+ end
59
+
60
+ def host
61
+ self.path_tokens[0]
62
+ end
63
+
64
+ def invite(ship_names:, message:)
65
+ data = %Q({
66
+ "invite": {
67
+ "resource": {
68
+ "ship": "#{self.host}",
69
+ "name": "#{self.key}"
70
+ },
71
+ "ships": [
72
+ "#{ship_names.join(',')}"
73
+ ],
74
+ "description": "#{message}"
75
+ }
76
+ })
77
+ self.manager.spider('group-invite', data)
78
+ end
79
+
80
+ def key
81
+ self.path_tokens[1]
82
+ end
83
+
84
+ def leave
85
+ spdr = self.manager.spider('group-leave', %Q({"leave": {"ship": "#{self.host}", "name": "#{self.key}"}}))
86
+ self.manager.remove(self) if 200 == spdr[:status]
87
+ spdr
88
+ end
89
+
90
+ def path_tokens
91
+ self.path.split('/')
92
+ end
93
+
94
+ def pending_invites
95
+ if (i = @policy["invite"])
96
+ if (p = i["pending"])
97
+ return p.count
98
+ end
99
+ end
100
+ '?'
101
+ end
102
+
103
+ def picture
104
+ self.fetch_link if @picture.nil?
105
+ @picture
106
+ end
107
+
108
+ def title
109
+ self.fetch_link if @title.nil?
110
+ @title
111
+ end
112
+
113
+ def to_h
114
+ {
115
+ title: self.title,
116
+ description: self.description,
117
+ host: self.host,
118
+ key: self.key,
119
+ member_count: self.members.count,
120
+ pending_invites: self.pending_invites,
121
+ hidden: self.hidden
122
+ }
123
+ end
124
+
125
+ def to_list
126
+ self.title || "Untitled - #{self.path}"
127
+ end
128
+
129
+ def to_s
130
+ "a Group(#{self.to_h})"
131
+ end
132
+
133
+ private
134
+
135
+ def fetch_link
136
+ @group_link = self.manager.ship.links.find_group(path: self.path)
137
+ unless @group_link.nil?
138
+ @creator = @group_link.metadata['creator']
139
+ @description = @group_link.metadata['description']
140
+ @picture = @group_link.metadata['picture']
141
+ @title = @group_link.metadata['title']
142
+ end
143
+ end
144
+
145
+ def graph_links
146
+ self.group_link.graph_links
147
+ end
148
+
149
+ def group_link
150
+ if @group_link.nil?
151
+ self.fetch_link
152
+ end
153
+ @group_link
154
+ end
155
+
156
+ def parse_tags(tags)
157
+ h = {}
158
+ return h if tags.empty?
159
+ tags.each {|k, v| h[k] = Set.new(v)}
160
+ tags.replace(h)
161
+ end
162
+
163
+ end
164
+ end
@@ -0,0 +1,71 @@
1
+ # frozen_string_literal: true
2
+
3
+ require 'urbit/parser'
4
+
5
+ module Urbit
6
+
7
+ class GroupParser < Parser
8
+ def resource
9
+ "~#{self.resource_hash["ship"]}/#{self.resource_hash["name"]}"
10
+ end
11
+
12
+ def resource_hash
13
+ @j["resource"]
14
+ end
15
+ end
16
+
17
+ class AddGroupParser < GroupParser
18
+ def group
19
+ Urbit::Group.new(path: self.resource,
20
+ members: [],
21
+ policy: @j["policy"],
22
+ tags: [],
23
+ hidden: @j["hidden"])
24
+ end
25
+ end
26
+
27
+ class ChangeTagParser < GroupParser
28
+ def ships
29
+ @j["ships"]
30
+ end
31
+
32
+ def tag
33
+ @j["tag"]["tag"]
34
+ end
35
+ end
36
+
37
+ class ChangeMemberParser < GroupParser
38
+ def ships
39
+ @j["ships"]
40
+ end
41
+ end
42
+
43
+ class InitialGroupParser < Parser
44
+ def groups
45
+ self.group_hashes.collect {|k, v| Group.new(path: k.sub('/ship/', ''),
46
+ members: v["members"],
47
+ policy: v["policy"],
48
+ tags: v["tags"],
49
+ hidden: v["hidden"])}
50
+ end
51
+
52
+ def group_hashes
53
+ @j
54
+ end
55
+ end
56
+
57
+ class InitialGroupGroupParser < GroupParser
58
+ def group
59
+ Urbit::Group.new(path: self.resource,
60
+ members: self.group_hash["members"],
61
+ policy: self.group_hash["policy"],
62
+ tags: self.group_hash["tags"],
63
+ hidden: self.group_hash["hidden"])
64
+ end
65
+
66
+ def group_hash
67
+ @j["group"]
68
+ end
69
+ end
70
+
71
+ end
@@ -0,0 +1,100 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Urbit
4
+ class Groups < Set
5
+ attr_accessor :ship
6
+
7
+ def initialize(ship:)
8
+ @ship = ship
9
+ @hash = {}
10
+ end
11
+
12
+ def [](path: nil, title: nil)
13
+ if title.nil?
14
+ if (g = self.select {|g| g.path == path}.first)
15
+ g.manager = self
16
+ return g
17
+ end
18
+ else
19
+ if (g = self.select {|g| g.title == title}.first)
20
+ g.manager = self
21
+ return g
22
+ end
23
+ end
24
+ nil
25
+ end
26
+
27
+ #
28
+ # Adds a Group to this Manager's groups collection
29
+ #
30
+ def add(a_group)
31
+ a_group.manager = self
32
+ self << a_group
33
+ end
34
+
35
+ def add_members(group_path:, ships:)
36
+ if (group = self[path: group_path])
37
+ group.members += ships
38
+ end
39
+ end
40
+
41
+ def add_tag(group_path:, ships:, tag:)
42
+ if (group = self[path: group_path])
43
+ if (group.tags.include? tag)
44
+ group.tags[tag] += ships
45
+ end
46
+ end
47
+ end
48
+
49
+ def create(name:, title:, description:)
50
+ self.spider('group-create', %Q({"create": {"name": "#{name}", "title": "#{title}", "description": "#{description}", "policy": {"open": {"banRanks": [], "banned": []}}}}))
51
+ end
52
+
53
+ def empty?
54
+ self.empty?
55
+ end
56
+
57
+ def join(host:, name:, share_contact: false, auto_join: false)
58
+ data = {join: {resource: {ship: "#{host}", name: "#{name}"}, ship: "#{host}", shareContact: share_contact, app: "groups", autojoin: auto_join}}
59
+ self.ship.poke(app: 'group-view', mark: 'group-view-action', message: data)
60
+ nil
61
+ end
62
+
63
+ def list
64
+ self.map {|g| g.to_list}.join("\n")
65
+ end
66
+
67
+ def remove(group)
68
+ self.delete(group)
69
+ end
70
+
71
+ def remove_members(group_path:, ships:)
72
+ if (group = self[path: group_path])
73
+ group.members -= ships
74
+ end
75
+ end
76
+
77
+ def remove_tag(group_path:, ships:, tag:)
78
+ if (group = self[path: group_path])
79
+ if (group.tags.include? tag)
80
+ group.tags[tag] -= ships
81
+ end
82
+ end
83
+ end
84
+
85
+ def load
86
+ if self.ship.logged_in?
87
+ self.ship.subscribe(app: 'group-store', path: '/groups')
88
+ end
89
+ nil
90
+ end
91
+
92
+ def spider(thread, data)
93
+ self.ship.spider(mark_in: 'group-view-action', mark_out: 'json', thread: thread, data: data)
94
+ end
95
+
96
+ def to_s
97
+ self.sort.each {|g| puts g}
98
+ end
99
+ end
100
+ end
data/lib/urbit/link.rb ADDED
@@ -0,0 +1,69 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Urbit
4
+ class Link
5
+ attr_reader :path, :data
6
+
7
+ def initialize(chain:, path:, data:)
8
+ @chain = chain
9
+ @graph = nil
10
+ @group = nil
11
+ @path = path
12
+ @data = data
13
+ end
14
+
15
+ def ==(another)
16
+ another.path == self.path
17
+ end
18
+
19
+ def <=>(another)
20
+ self.path <=> another.path
21
+ end
22
+
23
+ def eql?(another)
24
+ another.path == self.path
25
+ end
26
+
27
+ def graph
28
+ if @graph.nil?
29
+ @graph = self.ship.graph(resource: self.graph_resource)
30
+ end
31
+ @graph
32
+ end
33
+
34
+ def graph_links
35
+ @chain.find_graph_links_for_group(path: self.group_path)
36
+ end
37
+
38
+ def graph_resource
39
+ @data['resource'].sub('/ship/', '')
40
+ end
41
+
42
+ def group
43
+ if @group.nil?
44
+ @group = self.ship.groups[path: self.group_path]
45
+ end
46
+ @group
47
+ end
48
+
49
+ def group_path
50
+ @data['group'].sub('/ship/', '')
51
+ end
52
+
53
+ def metadata
54
+ @data['metadata']
55
+ end
56
+
57
+ def ship
58
+ @chain.ship
59
+ end
60
+
61
+ def to_list
62
+ @path
63
+ end
64
+
65
+ def type
66
+ @data['app-name']
67
+ end
68
+ end
69
+ end
@@ -0,0 +1,39 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Urbit
4
+ class Links < Set
5
+ attr_reader :ship
6
+
7
+ def initialize
8
+ @hash = {}
9
+ @ship = nil
10
+ end
11
+
12
+ def [](path:)
13
+ self.select {|l| path == l.path}.first
14
+ end
15
+
16
+ def find_graph(resource:)
17
+ self.select{|l| l.type == 'graph' && resource == l.graph_resource}.first
18
+ end
19
+
20
+ def find_graph_links_for_group(path:)
21
+ self.select{|l| l.type == 'graph' && path == l.group_path}
22
+ end
23
+
24
+ def find_group(path:)
25
+ self.select{|l| l.type == 'groups' && path == l.group_path}.first
26
+ end
27
+
28
+ def list
29
+ self.sort.each {|l| puts l.to_list}
30
+ nil
31
+ end
32
+
33
+ def load(ship:)
34
+ ship.subscribe(app: 'metadata-store', path: '/all')
35
+ @ship = ship
36
+ nil
37
+ end
38
+ end
39
+ end
data/lib/urbit/message.rb CHANGED
@@ -22,11 +22,11 @@ module Urbit
22
22
  end
23
23
 
24
24
  def channel_url
25
- "#{self.ship.config.api_base_url}/~/channel/#{self.channel.key}"
25
+ "#{self.ship.url}/~/channel/#{self.channel.key}"
26
26
  end
27
27
 
28
28
  def request_body
29
- self.to_a.to_json
29
+ JSON.generate(self.to_a)
30
30
  end
31
31
 
32
32
  def ship
data/lib/urbit/parser.rb CHANGED
@@ -3,9 +3,15 @@ require 'urbit/node'
3
3
 
4
4
  module Urbit
5
5
  class Parser
6
+ def initialize(with_json:)
7
+ @j = with_json
8
+ end
9
+ end
10
+
11
+ class GraphParser < Parser
6
12
  def initialize(for_graph:, with_json:)
13
+ super(with_json: with_json)
7
14
  @g = for_graph
8
- @j = with_json
9
15
  end
10
16
 
11
17
  #
@@ -33,16 +39,30 @@ module Urbit
33
39
  end
34
40
  end
35
41
 
36
- class AddGraphParser < Parser
42
+ class AddGraphParser < GraphParser
37
43
  def nodes_hash
38
44
  @j["graph"]
39
45
  end
40
46
 
47
+ def resource_hash
48
+ @j["resource"]
49
+ end
41
50
  end
42
51
 
43
- class AddNodesParser < Parser
52
+ class AddNodesParser < GraphParser
44
53
  def nodes_hash
45
54
  @j["nodes"]
46
55
  end
47
56
  end
57
+
58
+ class RemoveGraphParser < GraphParser
59
+ def nodes_hash
60
+ nil
61
+ end
62
+
63
+ def resource_hash
64
+ @j["resource"]
65
+ end
66
+ end
67
+
48
68
  end
@@ -1,7 +1,7 @@
1
1
  module Urbit
2
2
  class PokeMessage < Message
3
- def initialize(channel:, app:, mark:, a_string:)
4
- super(channel: channel, app: app, mark: mark, contents: a_string)
3
+ def initialize(channel:, app:, mark:, a_message_hash:)
4
+ super(channel: channel, app: app, mark: mark, contents: a_message_hash)
5
5
  end
6
6
  end
7
7
  end
@@ -1,19 +1,19 @@
1
1
  require 'ld-eventsource'
2
+ require "logger"
2
3
 
3
4
  require 'urbit/ack_message'
4
5
  require 'urbit/fact'
5
6
 
6
7
  module Urbit
7
8
  class Receiver < SSE::Client
8
- attr_accessor :facts
9
+ attr_accessor :errors, :facts
9
10
 
10
11
  def initialize(channel:)
11
- @facts = []
12
- super(channel.url, {headers: self.headers(channel)}) do |rec|
12
+ super(channel.url, headers: self.headers(channel), logger: self.default_logger) do |rec|
13
13
  # We are now listening on a socket for SSE::Events. This block will be called for each one.
14
14
  rec.on_event do |event|
15
15
  # Wrap the returned event in a Fact.
16
- @facts << (f = Fact.new(channel: channel, event: event))
16
+ @facts << (f = Fact.collect(channel: channel, event: event))
17
17
 
18
18
  # We need to acknowlege each message or urbit will eventually disconnect us.
19
19
  # We record the ack with the Fact itself.
@@ -22,12 +22,22 @@ module Urbit
22
22
  end
23
23
 
24
24
  rec.on_error do |error|
25
- self.facts += ["I received an error fact: #{error.class}"]
25
+ self.errors << ["I received an error fact: #{error.class}"]
26
26
  end
27
27
  end
28
+
29
+ @errors = []
30
+ @facts = []
28
31
  @is_open = true
29
32
  end
30
33
 
34
+ def default_logger
35
+ log = ::Logger.new($stdout)
36
+ log.level = ::Logger::WARN
37
+ log.progname = 'ld-eventsource'
38
+ log
39
+ end
40
+
31
41
  def open?
32
42
  @is_open
33
43
  end
@@ -0,0 +1,71 @@
1
+ # frozen_string_literal: true
2
+
3
+ require 'urbit/bucket'
4
+
5
+ module Urbit
6
+ class Setting
7
+ attr_accessor :buckets
8
+ attr_reader :desk, :ship
9
+
10
+ def initialize(ship:, desk:, buckets:)
11
+ @ship = ship
12
+ @desk = desk
13
+ @buckets = Set.new
14
+ buckets.each {|k, v| @buckets << Bucket.new(setting: self, name: k, entries: v)}
15
+ end
16
+
17
+ def ==(another_group)
18
+ another_setting.desk == self.desk
19
+ end
20
+
21
+ def <=>(another_group)
22
+ self.desk <=> another_group.desk
23
+ end
24
+
25
+ def [](bucket:)
26
+ self.buckets.select {|b| bucket == b.name}.first
27
+ end
28
+
29
+ def add_bucket(name:, entries:)
30
+ msg = {
31
+ "put-bucket": {
32
+ "bucket-key": "#{name}",
33
+ "desk": "#{self.desk}",
34
+ "bucket": entries
35
+ }
36
+ }
37
+ self.ship.poke(app: 'settings-store', mark: 'settings-event', message: msg)
38
+ nil
39
+ end
40
+
41
+ def entries(bucket:)
42
+ self[bucket: bucket].entries
43
+ end
44
+
45
+ def remove_bucket(name:)
46
+ msg = {
47
+ "del-bucket": {
48
+ "bucket-key": "#{name}",
49
+ "desk": "#{self.desk}"
50
+ }
51
+ }
52
+ self.ship.poke(app: 'settings-store', mark: 'settings-event', message: msg)
53
+ nil
54
+ end
55
+
56
+ def to_h
57
+ {
58
+ desk: @desk,
59
+ buckets: self.buckets,
60
+ }
61
+ end
62
+
63
+ def to_s
64
+ "a Setting(#{self.to_h})"
65
+ end
66
+
67
+ def to_string
68
+ "desk: #{self.desk}\n buckets: #{self.buckets.collect {|b| b.to_string}}"
69
+ end
70
+ end
71
+ end