urbit-api 0.1.0 → 0.3.0

Sign up to get free protection for your applications and to get access to all the features.
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: 626e3718491acdc2cd451fc847e489bac5bb4eb1490797bf6417fb9352c8b17b
4
- data.tar.gz: a744f2e30e52b901d1e681bdb8a2109f48facd9bcdc45cfc9148a11a0f32d059
3
+ metadata.gz: 2cef1b4e2c8a8a7ef8ba203f66e92e516e563c7b32119e98ee55366983e72dd3
4
+ data.tar.gz: dc6267d7c21e034c6725309a15678b09b4e0d8713990b5e975c94ef25475caed
5
5
  SHA512:
6
- metadata.gz: 31738b651665457fb50113b26e6f3fe319473bc8112c01ba9fedc58f27c474a62a40a1619ee0c1c765dbf70582f61c54885521a3b53c91bcd30b963ff6ca0d5b
7
- data.tar.gz: 36f2bcab1490c8dfe2b83cd875dba0d9571f53f65e7760d0cdfd38ccd8b5eb21753cf039cda46d11cff9f493d9cf4d818397ec36a8b66ea334e79459f8cf3d1c
6
+ metadata.gz: 9b19087253b1b6864e0941b94513f530aee01856357d590468e670273412a273a8ad4e514bc21c21ea86bc24877e4cc72b9ce475cbf218c2f53efafd462e2cb7
7
+ data.tar.gz: 1443c08a0370fd78bf33dca3c701aba09669421d434b10a475544a0828fe1eaae3ac1a222aa963654e29e800b3f7ab019efd73bbd2d9dbb6b00f4c775f52e63c
data/.ruby-version CHANGED
@@ -1 +1,2 @@
1
- 2.7.2
1
+ 2.7.4
2
+
data/README.md CHANGED
@@ -1,8 +1,13 @@
1
1
  # Urbit::Api
2
+
2
3
  ## The Ruby interface to the Urbit HTTP API
3
4
 
4
5
  This library wraps the Urbit ship http interface exposing it as a Ruby gem.
5
6
 
7
+ [![awesome urbit badge](https://img.shields.io/badge/~-awesome%20urbit-lightgrey)](https://github.com/urbit/awesome-urbit)
8
+ [![Gem Version](https://badge.fury.io/rb/urbit-api.svg)](https://badge.fury.io/rb/urbit-api)
9
+ [![License](https://img.shields.io/github/license/Zaxonomy/urbit-ruby)](https://github.com/Zaxonomy/urbit-ruby/blob/master/LICENSE.txt)
10
+
6
11
  ## Installation
7
12
 
8
13
  Add this line to your application's Gemfile:
@@ -21,39 +26,194 @@ Or install it yourself as:
21
26
 
22
27
  ## Usage
23
28
 
24
- ```rb
25
- # TODO: fix namespacing :)
26
- require 'urbit/urbit'
29
+ ```sh
30
+ > bin/console
27
31
 
28
32
  # This will instantiate a ship that connects to the fake `~zod` dev server by default
29
33
  # See Urbit docs for more info: https://urbit.org/using/develop/
30
- ship = Urbit.new
31
- # => #<Urbit::Ship:0x00007fa74b87f920 ...
32
-
33
- ship.logged_in?
34
- # => false
35
-
36
- ship.login
37
- # => #<Urbit::Ship:0x00007fa74b87f920 ...
38
-
39
- ship.logged_in?
40
- # => true
34
+ [1] pry(main)> ship = Urbit.new
35
+ => #<Urbit::Ship:0x00007fa74b87f920 ...
36
+
37
+ OR... with config file...
38
+ > ship = Urbit.connect(config_file: '_config-barsyr-latreb.yml')
39
+
40
+ > ship.logged_in?
41
+ => false
42
+
43
+ > ship.login
44
+ => #<Urbit::Ship:0x00007fa74b87f920 ...
45
+
46
+ > ship.logged_in?
47
+ => true
48
+
49
+ > ship.to_s
50
+ => "a Ship(name: '~barsyr-latreb', host: 'http://localhost', port: '8080')"
51
+
52
+ > channel = ship.subscribe(app: 'graph-store', path: '/updates')
53
+ => a Channel (Open) on ~barsyr-latreb(name: 'Channel-0', key: '1622836437b540b4')
54
+
55
+ # Subscribing works by opening a Channel. Your ships has a collection of all it's open Channels.
56
+ > channel = ship.open_channels.first
57
+ => a Channel (Open) on ~barsyr-latreb(name: 'Channel-0', key: '1622836437b540b4')
58
+
59
+ # Notice that it's the same one.
60
+
61
+ # Every Channel has a unique key to identify it.
62
+ > channel.key
63
+ => "16142890875c348d"
64
+
65
+ # The Channel has a Receiver that will now be listening on the app and path you specified. Each time an event is sent in it will be stored in the receiver's facts collection.
66
+ > channel.receiver.facts.count
67
+ => 12
68
+
69
+ # Perform any action through landscape that would initiate an update into %graph-store...
70
+ # In this case I have added a comment to a local notebook.
71
+ > channel.receiver.facts.last
72
+ => a Fact({:ship=>{:name=>"~barsyr-latreb", :host=>"http://localhost", :port=>"8080"}, :resource=>"~barsyr-latreb/test0-996", :acknowleged=>true, :is_graph_update=>true})
73
+
74
+ # Your ship keeps a collection of all the messages sent to urbit:
75
+ > channel.sent_messages.collect {|m| m.to_s}
76
+ => [
77
+ "a Message({:action=>"poke", :app=>"hood", :id=>1, :json=>"Opening Airlock", :mark=>"helm-hi", :ship=>"barsyr-latreb"})",
78
+ "a Message({:action=>"subscribe", :app=>"graph-store", :id=>2, :path=>"/updates", :ship=>"barsyr-latreb"})",
79
+ "a Message({"id"=>3, "action"=>"ack", "event-id"=>"0"})",
80
+ "a Message({"id"=>4, "action"=>"ack", "event-id"=>"1"})",
81
+ "a Message({"id"=>5, "action"=>"ack", "event-id"=>"2"})"
82
+ ]
83
+
84
+ #
85
+ # --------------------------------------------------------------------
86
+ # Poke
87
+ # --------------------------------------------------------------------
88
+ #
89
+ > ship.poke(app: 'hood', mark: 'helm-hi', message: 'Opening Airlock')
90
+ => a Channel (Open) on ~barsyr-latreb(name: 'Channel-0', key: '1630355920a717e1')
91
+
92
+ #
93
+ # --------------------------------------------------------------------
94
+ # Scry
95
+ # --------------------------------------------------------------------
96
+ #
97
+ # Retrieving your ship's base hash using scry....
98
+ > ship.scry(app: 'file-server', path: '/clay/base/hash')
99
+ # => {:status=>200, :code=>"ok", :body=>"\"e75k5\""}
100
+
101
+ #
102
+ # --------------------------------------------------------------------
103
+ # Spider
104
+ # --------------------------------------------------------------------
105
+ #
106
+ # Creating a new Notebook in "My Channels" using %spider....
107
+ > create_json = %Q(
108
+ {"create": {"resource": { "ship": "~zod", "name": "random_name"},
109
+ "title": "Testing",
110
+ "description": "Testing Un-Managed Graph Creation",
111
+ "associated" : {"policy": {"invite": {"pending": []}}},
112
+ "module": "publish", "mark": "graph-validator-publish"}}
113
+ )
114
+ > ship.spider(mark_in: 'graph-view-action', mark_out: 'json', thread: 'graph-create', data: create_json)
115
+ # => {:status=>200, :code=>"ok", :body=>"\"e75k5\""}
116
+
117
+ #
118
+ # --------------------------------------------------------------------
119
+ # %graph-store
120
+ # --------------------------------------------------------------------
121
+ #
122
+ > puts ship.graph_names
123
+ ~barsyr-latreb/dm-inbox
124
+ ~darlur/announce
125
+ ~bitbet-bolbel/urbit-community-5.963
126
+ ~winter-paches/top-shelf-6391
127
+ ~winter-paches/the-great-north-7.579
128
+ ~barsyr-latreb/test0-996
129
+ ~fabled-faster/test-chat-a-5919
130
+ ~barsyr-latreb/test1-4287
131
+ ~darrux-landes/welcome-to-urbit-community
132
+ ~millyt-dorsen/finance-2.962
133
+ ~fabled-faster/interface-testing-facility-683
134
+ ~darlur/help-desk-4556
135
+ =>
136
+
137
+ # Reference a graph by name and return a single node.
138
+ > puts ship.graph(resource: '~winter-paches/top-shelf-6391').node(index: "170.141.184.505.207.751.870.046.689.877.378.990.080")
139
+ a Node({:index=>"170.141.184.505.207.751.870.046.689.877.378.990.080", :author=>"witfyl-ravped", :contents=>[{"text"=>"the patches don't really bother me though tbh"}], :time_sent=>1629316468195, :is_parent=>false, :child_count=>0})
140
+ =>
141
+
142
+ # You can also reference a graph by its index in the graphs collection.
143
+ > puts ship.graphs[3].node(index: "170.141.184.505.207.751.870.046.689.877.378.990.080")
144
+ a Node({:index=>"170.141.184.505.207.751.870.046.689.877.378.990.080", :author=>"witfyl-ravped", :contents=>[{"text"=>"the patches don't really bother me though tbh"}], :time_sent=>1629316468195, :is_parent=>false, :child_count=>0})
145
+ =>
146
+
147
+ # Return the contents of the 5 oldest nodes of a graph
148
+ > graph = ship.graph(resource: '~winter-paches/top-shelf-6391')
149
+ > graph.oldest_nodes(count: 5).sort.each {|n| p n.contents};nil
150
+ [{"text"=>"watching the 2020 stanley cup finals (tampa (sigh) just went up 2-0 in game 3) and i thought: \"the great north has to have a hockey chat, eh?\""}]
151
+ [{"text"=>"we'll see if this has legs. ;)"}]
152
+ [{"text"=>"shortie! now 2-1 tampa."}]
153
+ [{"text"=>"looks like tampa's going to go up 2-1. as a canadian this geographically depresses me. :/"}]
154
+ [{"text"=>"anyone in the stands?"}]
155
+ =>
156
+
157
+ # A single Node. In this case, the 3rd oldest node in the graph.
158
+ > puts graph.nodes[2].contents
159
+ {"text"=>"shortie! now 2-1 tampa."}
160
+ =>
161
+
162
+ # Getting the next newer Node. Remember that it always returns an Array, hence the '#first'.
163
+ > puts graph.nodes[2].next.first.contents
164
+ {"text"=>"looks like tampa's going to go up 2-1. as a canadian this geographically depresses me. :/"}
165
+ =>
166
+
167
+ # Return the indexes of the newest 5 nodes of a graph
168
+ > ship.graph(resource: '~winter-paches/top-shelf-6391').newest_nodes(count: 5).sort.each {|n| p n.index};nil
169
+ "170.141.184.505.209.257.330.601.508.862.548.770.816"
170
+ "170.141.184.505.209.375.247.350.471.711.992.578.048"
171
+ "170.141.184.505.209.545.972.004.310.065.795.301.376"
172
+ "170.141.184.505.209.627.337.970.761.265.544.429.568"
173
+ "170.141.184.505.209.644.102.846.398.558.514.446.336"
174
+ =>
175
+
176
+ # Fetching nodes older relative to another node. (See indexes above)
177
+ > puts (node = ship.graph(resource: '~winter-paches/top-shelf-6391').node(index: "170.141.184.505.209.644.102.846.398.558.514.446.336"))
178
+ a Node({:index=>"170.141.184.505.209.644.102.846.398.558.514.446.336", :author=>"winter-paches", :contents=>[{"text"=>"yep. that's how i did it as a kid. harry caray was the white sox announcer before he turned traitor and went to the cubs."}], :time_sent=>1629419046028, :is_parent=>false, :child_count=>0})
179
+ =>
180
+
181
+ > puts node.previous
182
+ a Node({:index=>"170.141.184.505.209.627.337.970.761.265.544.429.568", :author=>"pathus-hiddyn", :contents=>[{"text"=>"Lol oh man I haven’t listened to a baseball game on the radio in forever. It is great isn’t it. "}], :time_sent=>1629418137668, :is_parent=>false, :child_count=>0})
183
+ =>
184
+
185
+ > node.previous(count: 4).each {|n| p n.index};nil
186
+ "170.141.184.505.209.257.330.601.508.862.548.770.816"
187
+ "170.141.184.505.209.375.247.350.471.711.992.578.048"
188
+ "170.141.184.505.209.545.972.004.310.065.795.301.376"
189
+ "170.141.184.505.209.627.337.970.761.265.544.429.568"
190
+
191
+ #
192
+ # --------------------------------------------------------------------
193
+ # %settings-store
194
+ # --------------------------------------------------------------------
195
+ #
196
+ > ship.setting(desk: 'landscape', bucket: 'calm').each {|e| p e};nil
197
+ ["hideUtilities", false]
198
+ ["hideGroups", false]
199
+ ["hideAvatars", true]
200
+ ["hideUnreads", true]
201
+ ["hideNicknames", true]
202
+
203
+ > ship.subscribe(app: 'settings-store', path: '/all')
204
+ => a Channel (Open) on ~barsyr-latreb(name: 'Channel-0', key: '164375139513eacb')
205
+
206
+ # We are now listening for changes in any settings on the ship. Go to Landscape and toggle the "Hide Groups" button...
207
+ Received a Fact for [a Channel (Open) on ~barsyr-latreb(name: 'Channel-0', key: '164375139513eacb')] -- [message] -- [{"json":{"settings-event":{"put-entry":{"bucket-key":"calm","desk":"landscape","entry-key":"hideGroups","value":true}}},"id":1,"response":"diff"}]
208
+
209
+ > ship.setting(desk: 'landscape', bucket: 'calm').each {|e| p e};nil
210
+ ["hideUtilities", false]
211
+ **["hideGroups", true]**
212
+ ["hideAvatars", true]
213
+ ["hideUnreads", true]
214
+ ["hideNicknames", true]
41
215
 
42
- channel = ship.open_channel('my-channel')
43
- # => #<Urbit::Channel:0x00007fa74b291e50 ...
44
-
45
- channel.key
46
- # => "16142890875c348d"
47
-
48
- ship.channels.first.key
49
- # => "16142890875c348d"
50
-
51
- receiver = channel.subscribe(app: 'graph-store', path: '/updates')
52
- # => #<Urbit::Receiver:0x00007fd3928eba58
53
-
54
- # This receiver will now be listening on the app and path you specified. Each time an event is sent in it will be stored in the receiver's events collection.
55
216
  ```
56
-
57
217
  ### Configuration
58
218
 
59
219
  Configure your ship using a config file or constructor keyword arguments. Either or both can be used; the keyword args will override any values set via config file.
@@ -83,11 +243,10 @@ ship = Urbit.new(host: '127.0.0.1', port: '8080')
83
243
  ```sh
84
244
  bin/test
85
245
  ```
86
- ### ~zod
87
-
88
246
  Tests assume that an instance of a ["fake" development Urbit ship](https://urbit.org/using/develop/) (one not connected to the live network) will be running, available at `http://localhost:8080`.
247
+ ### "fake" ~zod
89
248
 
90
- To create a development ship:
249
+ To create this development ship:
91
250
  ```sh
92
251
  ./urbit -F zod
93
252
  ```
@@ -95,7 +254,7 @@ To create a development ship:
95
254
 
96
255
  After checking out the repo, run `bin/setup` to install dependencies. Then, run `rake test` to run the tests. You can also run `bin/console` for an interactive prompt that will allow you to experiment.
97
256
 
98
- To install this gem onto your local machine, run `bundle exec rake install`. To release a new version, update the version number in `version.rb`, and then run `bundle exec rake release`, which will create a git tag for the version, push git commits and tags, and push the `.gem` file to [rubygems.org](https://rubygems.org).
257
+ To install this gem onto your local machine, run `bundle exec rake install`.
99
258
 
100
259
  ## Contributing
101
260
 
data/bin/console CHANGED
@@ -4,8 +4,11 @@ require "bundler/setup"
4
4
  require "pry"
5
5
  require "urbit/urbit"
6
6
 
7
- # You can add fixtures and/or initialization code here to make experimenting
8
- # with your gem easier. You can also use a different console, if you like.
9
-
7
+ puts "Welcome! This is an interactive environment to explore your Urbit ship."
8
+ puts "You create a config file with connection details for your ship and off you go."
9
+ puts ""
10
10
  puts "e.g., ship = Urbit.new(config_file: 'my_config.yml')"
11
+ puts "=> a Ship(name: '~barsyr-latreb', host: 'http://localhost', port: '8080')"
12
+
13
+ Pry.config.print = proc { |output, value| output.puts "=> #{value}" }
11
14
  Pry.start
@@ -1,18 +1,23 @@
1
- require 'json'
2
-
3
- require 'urbit/message'
4
-
5
1
  module Urbit
6
2
  class AckMessage < Message
7
- def initialize(channel, sse_message_id)
8
- @action = 'ack'
9
- @channel = channel
10
- @id = 0
3
+ attr_reader :ack_id
4
+
5
+ def initialize(channel:, sse_message_id:)
6
+ super(channel: channel)
11
7
  @ack_id = sse_message_id
12
8
  end
13
9
 
10
+ def action
11
+ "ack"
12
+ end
13
+
14
14
  def to_h
15
- {'id' => id, 'action' => action, 'event-id' => @ack_id}
15
+ # Need to use the older hash style due to the key having a dash.
16
+ {'id' => self.id, 'action' => self.action, 'event-id' => self.ack_id}
17
+ end
18
+
19
+ def to_s
20
+ "an AckMessage(#{self.to_h})"
16
21
  end
17
22
  end
18
23
  end
@@ -1,5 +1,5 @@
1
1
  module Urbit
2
2
  module Api
3
- VERSION = "0.1.0"
3
+ VERSION = "0.3.0"
4
4
  end
5
5
  end
data/lib/urbit/channel.rb CHANGED
@@ -1,52 +1,58 @@
1
- require 'faraday'
2
1
  require 'securerandom'
3
2
 
4
3
  require 'urbit/message'
5
4
  require 'urbit/receiver'
5
+ require 'urbit/close_message'
6
+ require 'urbit/poke_message'
6
7
  require 'urbit/subscribe_message'
7
8
 
8
9
  module Urbit
9
10
  class Channel
10
11
  attr_accessor :messages
11
- attr_reader :key, :name, :ship
12
+ attr_reader :key, :name, :receiver, :ship
12
13
 
13
- def initialize(ship, name)
14
+ def initialize(ship:, name:)
14
15
  @ship = ship
15
16
  @key = "#{Time.now.to_i}#{SecureRandom.hex(3)}"
16
17
  @messages = []
17
18
  @name = name
19
+ @receiver = nil
18
20
  @is_open = false
19
- @is_subscribed = false
20
21
  end
21
22
 
22
23
  def close
23
24
  # puts "closing #{name}"
24
- m = Urbit::CloseMessage.new(self)
25
- @is_open = !self.send_message(m)
25
+ m = Urbit::CloseMessage.new(channel: self)
26
+ @is_open = !self.send(message: m)
26
27
  end
27
28
 
28
29
  def closed?
29
30
  !@is_open
30
31
  end
31
32
 
32
- def open(a_message_string)
33
- m = Urbit::Message.new(self, "poke", "hood", "helm-hi", a_message_string)
34
- @is_open = self.send_message(m)
35
- end
36
-
37
33
  def open?
38
34
  @is_open
39
35
  end
40
36
 
41
- def queue_message(a_message)
42
- a_message.id = self.sent_messages.size + 1
43
- @messages << a_message
37
+ #
38
+ # One way to open a channel by "poking" an urbit app with a mark and a (json) message.
39
+ # A typical example of this is poking the 'hood' app using the mark 'helm-hi' to start a DM chat.
40
+ #
41
+ def poke(app:, mark:, message:)
42
+ @is_open = self.send(message: (Urbit::PokeMessage.new(channel: self, app: app, mark: mark, a_string: message)))
43
+ @receiver = Urbit::Receiver.new(channel: self)
44
+ self
45
+ end
46
+
47
+ def queue(message:)
48
+ message.id = self.sent_messages.size + 1
49
+ @messages << message
44
50
  end
45
51
 
46
52
  # Answers true if message was successfully sent.
47
- def send_message(a_message)
48
- self.queue_message(a_message)
49
- resp = a_message.transmit
53
+ def send(message:)
54
+ self.queue(message: message)
55
+ resp = message.transmit
50
56
  resp.reason_phrase == "ok"
51
57
  end
52
58
 
@@ -58,14 +64,19 @@ module Urbit
58
64
  self.open? ? "Open" : "Closed"
59
65
  end
60
66
 
61
- def subscribe(app, path)
62
- m = Urbit::SubscribeMessage.new(self, app, path)
63
- @is_subscribed = self.send_message(m)
64
- receiver = Urbit::Receiver.new(self)
67
+ #
68
+ # Subscribe to an app at a path.
69
+ # Returns a Receiver which will begin to get back a stream of facts... which is a... Dictionary? Encyclopedia?
70
+ #
71
+ def subscribe(app:, path:)
72
+ m = Urbit::SubscribeMessage.new(channel: self, app: app, path: path)
73
+ @is_open = self.send(message: m)
74
+ @receiver = Urbit::Receiver.new(channel: self)
75
+ self
65
76
  end
66
77
 
67
78
  def subscribed?
68
- @is_subscribed
79
+ @is_open
69
80
  end
70
81
 
71
82
  def to_s
@@ -0,0 +1,22 @@
1
+ require 'securerandom'
2
+
3
+ require 'urbit/message'
4
+ require 'urbit/receiver'
5
+ require 'urbit/close_message'
6
+ require 'urbit/poke_message'
7
+ require 'urbit/subscribe_message'
8
+
9
+ module Urbit
10
+ class ChatChannel < Channel
11
+ # def messages
12
+ # self.fetch_all_nodes if @nodes.empty?
13
+ # @nodes
14
+ # end
15
+
16
+ # def newest_messages(count = 100)
17
+ # self.fetch_newest_nodes(count) if @nodes.empty?
18
+ # self.nodes
19
+ # end
20
+
21
+ end
22
+ end
@@ -0,0 +1,15 @@
1
+ module Urbit
2
+ class CloseMessage < Message
3
+ def initialize(channel:)
4
+ super(channel: channel)
5
+ end
6
+
7
+ def action
8
+ "delete"
9
+ end
10
+
11
+ def to_h
12
+ {id: self.id, action: self.action}
13
+ end
14
+ end
15
+ end
data/lib/urbit/fact.rb ADDED
@@ -0,0 +1,206 @@
1
+ require 'urbit/graph'
2
+ require 'urbit/node'
3
+ require 'urbit/parser'
4
+
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
164
+
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
173
+ end
174
+ end
175
+ end
176
+
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
188
+
189
+ def entry
190
+ self.contents["entry-key"]
191
+ end
192
+
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
201
+
202
+ def value
203
+ self.contents["value"]
204
+ end
205
+ end
206
+ end