urbit-api 0.3.0 → 0.4.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/fact.rb CHANGED
@@ -1,206 +1,50 @@
1
- require 'urbit/graph'
2
- require 'urbit/node'
3
- require 'urbit/parser'
1
+ # frozen_string_literal: true
4
2
 
5
- module Urbit
6
- class Fact
7
- attr_reader :ack
8
-
9
- def initialize(channel:, event:)
10
- @channel = channel
11
- @data = event.data
12
- @type = event.type
13
- puts "Received a Fact for [#{channel}] -- [#{@type}] -- [#{@data}]"
14
- end
15
-
16
- #
17
- # This is a Facotry method to make the proper Fact subclass from
18
- # a Channel Event.
19
- #
20
- def self.collect(channel:, event:)
21
- contents = JSON.parse(event.data)
22
- return Fact.new(channel: channel, event: event) if contents["json"].nil?
23
- return SettingsEventFact.new(channel: channel, event: event) if contents["json"]["settings-event"]
24
-
25
- return Fact.new(channel: channel, event: event) if contents["json"]["graph-update"].nil?
26
- return AddGraphFact.new(channel: channel, event: event) if contents["json"]["graph-update"]["add-graph"]
27
- return AddNodesFact.new(channel: channel, event: event) if contents["json"]["graph-update"]["add-nodes"]
28
- return RemoveGraphFact.new(channel: channel, event: event) if contents["json"]["graph-update"]["remove-graph"]
29
-
30
- return Fact.new(channel: channel, event: event)
31
- end
32
-
33
- def add_ack(ack:)
34
- @ack = :ack
35
- end
36
-
37
- def contents
38
- JSON.parse(@data)
39
- end
40
-
41
- def for_this_ship?
42
- self.ship == @channel.ship
43
- end
44
-
45
- def graph_update?
46
- false
47
- end
48
-
49
- def is_acknowledged?
50
- !@ack.nil?
51
- end
52
-
53
- def raw_json
54
- nil
55
- end
56
-
57
- def ship
58
- @channel.ship
59
- end
60
-
61
- def to_h
62
- {
63
- ship: self.ship.to_h,
64
- acknowleged: self.is_acknowledged?,
65
- is_graph_update: self.graph_update?
66
- }
67
- end
68
-
69
- def to_s
70
- "a #{self.class.name}(#{self.to_h})"
71
- end
72
- end
73
-
74
- class GraphUpdateFact < Fact
75
- def initialize(channel:, event:)
76
- super channel: channel, event: event
77
- end
78
-
79
- #
80
- # Attach this new fact as a node to its Graph.
81
- #
82
- def attach_parser
83
- # puts "Received a graph update for [#{self.ship.graph(resource: self.resource)}]"
84
- if self.incoming_graph
85
- # puts "Received an add_graph event: #{self.raw_json} on #{self.resource}"
86
- self.create_parser
87
- end
88
- end
89
-
90
- def create_parser
91
- nil
92
- end
93
-
94
- def graph_update?
95
- true
96
- end
97
-
98
- def incoming_graph
99
- self.ship.graph(resource: self.resource)
100
- end
101
-
102
- def resource
103
- return "~#{self.resource_h["ship"]}/#{self.resource_h["name"]}" unless self.resource_h.nil?
104
- end
105
-
106
- def resource_h
107
- self.raw_json["resource"]
108
- end
109
-
110
- def root_h
111
- self.contents["json"]["graph-update"]
112
- end
113
-
114
- def to_h
115
- super.merge!(resource: self.resource)
116
- end
117
- end
118
-
119
- class AddGraphFact < GraphUpdateFact
120
- def initialize(channel:, event:)
121
- super channel: channel, event: event
122
- end
123
-
124
- def create_parser
125
- Urbit::AddGraphParser.new(for_graph: incoming_graph, with_json: self.raw_json).add_nodes
126
- end
127
-
128
- def raw_json
129
- self.root_h["add-graph"]
130
- end
131
- end
132
-
133
- class AddNodesFact < GraphUpdateFact
134
- def initialize(channel:, event:)
135
- super channel: channel, event: event
136
- end
137
-
138
- def create_parser
139
- Urbit::AddNodesParser.new(for_graph: incoming_graph, with_json: self.raw_json).add_nodes
140
- end
141
-
142
- def raw_json
143
- self.root_h["add-nodes"]
144
- end
145
- end
146
-
147
- class RemoveGraphFact < GraphUpdateFact
148
- def initialize(channel:, event:)
149
- super channel: channel, event: event
150
- end
151
-
152
- def create_parser
153
- Urbit::RemoveGraphParser.new(for_graph: incoming_graph, with_json: self.raw_json)
154
- end
155
-
156
- def raw_json
157
- self.root_h["remove-graph"]
158
- end
159
-
160
- def resource_h
161
- self.raw_json
162
- end
163
- end
3
+ require_relative 'fact/base_fact'
4
+ require_relative 'fact/graph_fact'
5
+ require_relative 'fact/group_fact'
6
+ require_relative 'fact/settings_fact'
164
7
 
165
- class SettingsEventFact < Fact
166
- def initialize(channel:, event:)
167
- super channel: channel, event: event
168
-
169
- if self.for_this_ship?
170
- # See if we already have this setting, if no add it, if yes update it.
171
- if (entries = channel.ship.setting(bucket: self.bucket))
172
- entries[self.entry] = self.value
8
+ module Urbit
9
+ module Fact
10
+ class << self
11
+ #
12
+ # This is a Factory method to make the proper Fact subclass from a Channel Event.
13
+ #
14
+ def collect(channel:, event:)
15
+ contents = JSON.parse(event.data)
16
+
17
+ if contents["json"].nil?
18
+ return SuccessFact.new(channel: channel, event: event) if contents["ok"]
19
+ return ErrorFact.new(channel: channel, event: event) if contents["err"]
20
+ return EmptyFact.new(channel: channel, event: event)
173
21
  end
174
- end
175
- end
176
22
 
177
- def bucket
178
- self.contents["bucket-key"]
179
- end
180
-
181
- def contents
182
- JSON.parse(@data)["json"]["settings-event"]["put-entry"]
183
- end
184
-
185
- def desk
186
- self.contents["desk"]
187
- end
23
+ if contents["json"]["graph-update"]
24
+ return AddGraphFact.new(channel: channel, event: event) if contents["json"]["graph-update"]["add-graph"]
25
+ return AddNodesFact.new(channel: channel, event: event) if contents["json"]["graph-update"]["add-nodes"]
26
+ return RemoveGraphFact.new(channel: channel, event: event) if contents["json"]["graph-update"]["remove-graph"]
27
+ end
188
28
 
189
- def entry
190
- self.contents["entry-key"]
191
- end
29
+ if (c = contents["json"]["groupUpdate"])
30
+ return AddGroupFact.new(channel: channel, event: event) if c["addGroup"]
31
+ return AddGroupMemberFact.new(channel: channel, event: event) if c["addMembers"]
32
+ return AddTagFact.new(channel: channel, event: event) if c["addTag"]
33
+ return InitialGroupFact.new(channel: channel, event: event) if c["initial"]
34
+ return InitialGroupGroupFact.new(channel: channel, event: event) if c["initialGroup"]
35
+ return RemoveGroupMemberFact.new(channel: channel, event: event) if c["removeMembers"]
36
+ return RemoveTagFact.new(channel: channel, event: event) if c["removeTag"]
37
+ end
192
38
 
193
- def to_h
194
- super.merge!({
195
- bucket: self.bucket,
196
- desk: self.desk,
197
- entry: self.entry,
198
- value: self.value
199
- })
200
- end
39
+ if (c = contents["json"]["settings-event"])
40
+ return SettingsEventDelBucketFact.new(channel: channel, event: event) if c["del-bucket"]
41
+ return SettingsEventDelEntryFact.new(channel: channel, event: event) if c["del-entry"]
42
+ return SettingsEventPutBucketFact.new(channel: channel, event: event) if c["put-bucket"]
43
+ return SettingsEventPutEntryFact.new(channel: channel, event: event) if c["put-entry"]
44
+ end
201
45
 
202
- def value
203
- self.contents["value"]
46
+ return BaseFact.new(channel: channel, event: event)
47
+ end
204
48
  end
205
49
  end
206
50
  end
@@ -0,0 +1,109 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Urbit
4
+ class Group
5
+ attr_accessor :manager, :members, :tags
6
+ attr_reader :hidden, :path, :policy
7
+
8
+ def initialize(path:, members:, policy:, tags:, hidden:)
9
+ @hidden = hidden
10
+ @manager = nil
11
+ @members = Set.new(members)
12
+ @path = path
13
+ @policy = policy
14
+ @tags = self.parse_tags(tags)
15
+ end
16
+
17
+ def ==(another_group)
18
+ another_group.path == self.path
19
+ end
20
+
21
+ def <=>(another_group)
22
+ self.path <=> another_group.path
23
+ end
24
+
25
+ #
26
+ # This is the action labeled as "Archive" in the Landscape UI.
27
+ # As of now, you can only do this to groups on your own ship.
28
+ #
29
+ def delete
30
+ if (self.host == self.manager.ship.name)
31
+ spdr = self.manager.spider('group-delete', %Q({"remove": {"ship": "#{self.host}", "name": "#{self.key}"}}))
32
+ self.manager.remove(self) if 200 == spdr[:status]
33
+ return spdr
34
+ end
35
+ {status: 400, code: 'bad_request', body: 'Can only delete Groups on your own ship.'}
36
+ end
37
+
38
+ def eql?(another_group)
39
+ another_group.path == self.path
40
+ end
41
+
42
+ def host
43
+ self.path_tokens[0]
44
+ end
45
+
46
+ def invite(ship_names:, message:)
47
+ data = %Q({
48
+ "invite": {
49
+ "resource": {
50
+ "ship": "#{self.host}",
51
+ "name": "#{self.key}"
52
+ },
53
+ "ships": [
54
+ "#{ship_names.join(',')}"
55
+ ],
56
+ "description": "#{message}"
57
+ }
58
+ })
59
+ self.manager.spider('group-invite', data)
60
+ end
61
+
62
+ def key
63
+ self.path_tokens[1]
64
+ end
65
+
66
+ def leave
67
+ spdr = self.manager.spider('group-leave', %Q({"leave": {"ship": "#{self.host}", "name": "#{self.key}"}}))
68
+ self.manager.remove(self) if 200 == spdr[:status]
69
+ spdr
70
+ end
71
+
72
+ def path_tokens
73
+ self.path.split('/')
74
+ end
75
+
76
+ def pending_invites
77
+ if (i = @policy["invite"])
78
+ if (p = i["pending"])
79
+ return p.count
80
+ end
81
+ end
82
+ '?'
83
+ end
84
+
85
+ def to_h
86
+ {
87
+ host: self.host,
88
+ key: self.key,
89
+ member_count: self.members.count,
90
+ pending_invites: self.pending_invites,
91
+ hidden: self.hidden
92
+ }
93
+ end
94
+
95
+ def to_s
96
+ "a Group(#{self.to_h})"
97
+ end
98
+
99
+ private
100
+
101
+ def parse_tags(tags)
102
+ h = {}
103
+ return h if tags.empty?
104
+ tags.each {|k, v| h[k] = Set.new(v)}
105
+ tags.replace(h)
106
+ end
107
+
108
+ end
109
+ end
@@ -0,0 +1,100 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Urbit
4
+ class GroupManager
5
+ attr_accessor :groups, :ship
6
+
7
+ def initialize(ship:)
8
+ @ship = ship
9
+ @groups = []
10
+ end
11
+
12
+ #
13
+ # Adds a Group to this Manager's groups collection
14
+ #
15
+ def add(a_group)
16
+ @groups << a_group
17
+ end
18
+
19
+ def add_members(group_path:, ships:)
20
+ if (group = self.find(path: group_path))
21
+ group.members += ships
22
+ end
23
+ end
24
+
25
+ def add_tag(group_path:, ships:, tag:)
26
+ if (group = self.find(path: group_path))
27
+ if (group.tags.include? tag)
28
+ group.tags[tag] += ships
29
+ end
30
+ end
31
+ end
32
+
33
+ def create(name:, title:, description:)
34
+ self.spider('group-create', %Q({"create": {"name": "#{name}", "title": "#{title}", "description": "#{description}", "policy": {"open": {"banRanks": [], "banned": []}}}}))
35
+ end
36
+
37
+ def empty?
38
+ self.groups.empty?
39
+ end
40
+
41
+ #
42
+ # Answers the Group uniquely keyed by path:, if it exists
43
+ #
44
+ def find(path:)
45
+ if (g = self.groups.select {|g| g.path == path}.first)
46
+ g.manager = self
47
+ g
48
+ end
49
+ end
50
+
51
+ def first
52
+ g = self.groups.first
53
+ g.manager = self
54
+ g
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.groups.map {|g| g.path}.join("\n")
65
+ end
66
+
67
+ def remove(group)
68
+ @groups = self.groups.filter {|g| g != group}
69
+ end
70
+
71
+ def remove_members(group_path:, ships:)
72
+ if (group = self.find(path: group_path))
73
+ group.members -= ships
74
+ end
75
+ end
76
+
77
+ def remove_tag(group_path:, ships:, tag:)
78
+ if (group = self.find(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.groups.sort.each {|g| puts g}
98
+ end
99
+ end
100
+ 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
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,7 +39,7 @@ 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
@@ -43,13 +49,13 @@ module Urbit
43
49
  end
44
50
  end
45
51
 
46
- class AddNodesParser < Parser
52
+ class AddNodesParser < GraphParser
47
53
  def nodes_hash
48
54
  @j["nodes"]
49
55
  end
50
56
  end
51
57
 
52
- class RemoveGraphParser < Parser
58
+ class RemoveGraphParser < GraphParser
53
59
  def nodes_hash
54
60
  nil
55
61
  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,15 +1,15 @@
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.
@@ -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