urbit-api 0.3.0 → 0.4.0

Sign up to get free protection for your applications and to get access to all the features.
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