solana_rpc_ruby 1.0.0.pre → 1.1.1

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 CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: 8bea15d348b3cc9652473f49747cff8a89992b43543bcf3d569c1aef933c03cf
4
- data.tar.gz: d9741e5591c96851ba5d71c70ac09687e7888748e5a82dcbde3e2eab3cebff7f
3
+ metadata.gz: 2b473e11604fa0d409efbe09260aa85bf467a1b76b6fa014e7a0716e019b3c5e
4
+ data.tar.gz: 7e116f4bdb58358bf85c3260eb526be306445d87ec87788648ee40ffbd9bd80d
5
5
  SHA512:
6
- metadata.gz: f28f02c36ed297a1fb527244e2e4e859a636920bd99c9a8cdc4fd20b2f345738d8c2fd011786ccbe4f2b69677d4d089cee77b85572dbb55b18cd6a94ad89e931
7
- data.tar.gz: c6ac7e9ee342c856553bcb3053c50f8dee9db48c667946a5d0ecb775b3b026c39eacfed59b254697d0aebe1b89c2a933cff7b4f65b72823855f9dc7d1a48b69a
6
+ metadata.gz: 5013b6799259a02d77cc232e3a1456a2de52b71c93c31e78a376c702656ed7c6444577e2f4467ceb8c5aa7df7b9b78d4bb05ee27711f003e08d6576c15b84fc4
7
+ data.tar.gz: ada8e45a050f6bc74e712f9a61a2e8b62032a6a9e60e8a21627c99dd5ce48673833b3cc119c6a893b117dcb81233e1aa8c196bb5cd27145bf684ab88b9c257cb
data/CHANGELOG.md CHANGED
@@ -1,3 +1,13 @@
1
1
  # Changelog
2
- ## 1.0.0 (TBA)
2
+ ## 1.0.0
3
3
  * Initial release
4
+
5
+ ## 1.0.1
6
+ * Add optional id argument that can be passed to MethodsWrapper class.
7
+
8
+ ## 1.1.0
9
+ * Add websockets connection to gem.
10
+
11
+ ## 1.1.1
12
+ * Fix SolanaRpcRuby::ApiError occurring when websocket program runs for too long
13
+ (#<SolanaRpcRuby::ApiError: NoMethodError undefined method ping)
data/README.md CHANGED
@@ -1,4 +1,4 @@
1
- ![specs](https://github.com/Block-Logic/solana-rpc-ruby/actions/workflows/specs.yml/badge.svg?branch=177580443_create_wrapper_for_solana_rpc)
1
+ ![specs](https://github.com/Block-Logic/solana-rpc-ruby/actions/workflows/specs.yml/badge.svg)
2
2
  # solana_rpc_ruby
3
3
  A Solana RPC Client for Ruby. This gem provides a wrapper methods for Solana RPC JSON API https://docs.solana.com/developing/clients/jsonrpc-api.
4
4
 
@@ -21,7 +21,7 @@ Next, you need to run the generator:
21
21
  rails g solana_rpc_ruby:install
22
22
  ```
23
23
 
24
- The latter command will generate a new config file `config/solana_rpc_ruby_config.rb` looking like this:
24
+ The latter command will generate a new config file `config/initializers/solana_rpc_ruby_config.rb` looking like this:
25
25
 
26
26
  ```ruby
27
27
  require 'solana_rpc_ruby'
@@ -29,21 +29,171 @@ require 'solana_rpc_ruby'
29
29
  SolanaRpcRuby.config do |c|
30
30
  c.cluster = 'https://api.testnet.solana.com'
31
31
  c.json_rpc_version = '2.0'
32
- c.encoding = 'base58'
33
32
  # ...other options
34
33
  end
35
34
  ```
36
35
  You can customize it to your needs.
37
36
 
38
37
  ### Usage examples
38
+
39
+ #### JSON RPC API
39
40
  ```ruby
40
41
  # If you set default cluster you don't need to pass it every time.
41
- method_wrapper = SolanaRpcRuby::MethodsWrapper.new(cluster: 'https://api.testnet.solana.com')
42
+ method_wrapper = SolanaRpcRuby::MethodsWrapper.new(
43
+ # optional, if not passed, default cluster from config will be used
44
+ cluster: 'https://api.testnet.solana.com',
45
+
46
+ # optional, if not passed, default random number
47
+ # from range 1 to 99_999 will be used
48
+ id: 123
49
+ )
50
+
42
51
  response = method_wrapper.get_account_info(account_pubkey)
43
52
  puts response
53
+
54
+ # You can check cluster and id that are used.
55
+ method_wrapper.cluster
56
+ method_wrapper.id
57
+ ```
58
+ #### Subscription Websocket (BETA)
59
+ ```ruby
60
+ ws_method_wrapper = SolanaRpcRuby::WebsocketsMethodsWrapper.new(
61
+ # optional, if not passed, default ws_cluster from config will be used
62
+ cluster: 'ws://api.testnet.solana.com',
63
+
64
+ # optional, if not passed, default random number
65
+ # from range 1 to 99_999 will be used
66
+ id: 123
67
+ )
68
+
69
+ # You should see stream of messages in your console.
70
+ ws_method_wrapper.root_subscribe
71
+
72
+ # You can pass a block to do something with websocket's messages, ie:
73
+ block = Proc.new do |message|
74
+ json = JSON.parse(message)
75
+ puts json['params']
76
+ end
77
+
78
+ ws_method_wrapper.root_subscribe(&block)
79
+
80
+ # You can check cluster and id that are used.
81
+ ws_method_wrapper.cluster
82
+ ws_method_wrapper.id
83
+ ```
84
+
85
+ #### Websockets usage in Rails
86
+ You can easily plug-in websockets connection to your rails app by using ActionCable.
87
+ Here is an example for development environment.
88
+ More explanation on Action Cable here: https://www.pluralsight.com/guides/updating-a-rails-app's-wall-feed-in-real-time-with-actioncable
89
+
90
+ 0. Make sure that you have action_cable and solana_rpc_ruby gems installed properly. Also install redis unless you have it.
91
+
92
+ 1. Mount action_cable in `routes.rb`.
93
+ ```
94
+ Rails.application.routes.draw do
95
+ mount ActionCable.server => '/cable'
96
+ ...
97
+ end
98
+ ```
99
+
100
+ 2. Update `config/environments/development.rb`.
101
+ ```
102
+ config.action_cable.url = "ws://localhost:3000/cable"
103
+ config.action_cable.allowed_request_origins = [/http:\/\/*/, /https:\/\/*/]
104
+ ```
105
+
106
+ 3. Update adapter in `cable.yml`.
107
+ ```
108
+ development:
109
+ adapter: redis
110
+ url: <%= ENV.fetch("REDIS_URL") { "redis://localhost:6379/1" } %>
111
+ ```
112
+
113
+ 4. Create a channel.
114
+ ```
115
+ rails g channel wall
44
116
  ```
45
117
 
46
- All info about methods you can find in the docs on: FILL THE ADDRESS!!!
118
+ 5. Your `wall_channel.rb` should look like this:
119
+ ```
120
+ class WallChannel < ApplicationCable::Channel
121
+ def subscribed
122
+ stream_from "wall_channel"
123
+ end
124
+
125
+ def unsubscribed
126
+ # Any cleanup needed when channel is unsubscribed
127
+ end
128
+ end
129
+ ```
130
+
131
+ 6. Your `wall_channel.js` should look like this (json keys are configured for `root_subscription` method response):
132
+ ```
133
+ import consumer from "./consumer"
134
+
135
+ consumer.subscriptions.create("WallChannel", {
136
+ connected() {
137
+ console.log("Connected to WallChannel");
138
+ // Called when the subscription is ready for use on the server
139
+ },
140
+
141
+ disconnected() {
142
+ // Called when the subscription has been terminated by the server
143
+ },
144
+
145
+ received(data) {
146
+ let wall = document.getElementById('wall');
147
+
148
+ wall.innerHTML += "<p>Result: "+ data['message']['result'] + "</p>";
149
+ // Called when there's incoming data on the websocket for this channel
150
+ }
151
+ });
152
+
153
+
154
+ ```
155
+
156
+ 7. Create placeholder somewhere in your view for messages.
157
+ ```
158
+ <div id='wall' style='overflow-y: scroll; height:400px;''>
159
+ <h1>Solana subscription messages</h1>
160
+ </div>
161
+ ```
162
+
163
+ 8. Create a script with a block to run websockets (`script/websockets_solana.rb`).
164
+ ```
165
+ require_relative '../config/environment'
166
+
167
+ ws_method_wrapper = SolanaRpcRuby::WebsocketsMethodsWrapper.new
168
+
169
+ # Example of block that can be passed to the method to manipulate the data.
170
+ block = Proc.new do |message|
171
+ json = JSON.parse(message)
172
+
173
+ ActionCable.server.broadcast(
174
+ "wall_channel",
175
+ {
176
+ message: json['params']
177
+ }
178
+ )
179
+ end
180
+
181
+ ws_method_wrapper.root_subscribe(&block)
182
+ ```
183
+ 9. Run `rails s`, open webpage where you put your placeholder.
184
+ 10. Open `http://localhost:3000/address_with_websockets_view`.
185
+ 11. Run `rails r script/websockets_solana.rb` in another terminal window.
186
+ 12. You should see incoming websocket messages on your webpage.
187
+ ### Demo scripts
188
+ Gem is coming with demo scripts that you can run and test API and Websockets.
189
+
190
+ 1. Clone the repo
191
+ 2. Set the gemset
192
+ 3. Run `ruby demo.rb` or `ruby demo_ws_METHOD.rb` to see example output.
193
+ 4. Check the gem or Solana JSON RPC API docs to get more information about method usage and modify demo scripts loosely.
194
+
195
+ All info about methods you can find in the docs on: https://www.rubydoc.info/github/Block-Logic/solana-rpc-ruby/main/SolanaRpcRuby
196
+
47
197
  Also, as a reference you can use docs from solana: https://docs.solana.com/developing/clients/jsonrpc-api
48
198
  ## License
49
199
 
@@ -6,7 +6,7 @@ module SolanaRpcRuby
6
6
 
7
7
  desc 'Creates a SolanaRpcRuby config file.'
8
8
  def copy_config
9
- template 'solana_rpc_ruby_config.rb', "#{Rails.root}/config/solana_rpc_ruby.rb"
9
+ template 'solana_rpc_ruby_config.rb', "#{Rails.root}/config/initializers/solana_rpc_ruby.rb"
10
10
  end
11
11
  end
12
12
  end
@@ -1,14 +1,13 @@
1
1
  require_relative 'solana_rpc_ruby'
2
2
 
3
- # DEVNET_CLUSTER = 'https://api.devnet.solana.com'
4
- # MAINNET_CLUSTER = 'https://api.mainnet-beta.solana.com'
5
- # TESTNET_CLUSTER = 'https://api.testnet.solana.com'
6
-
7
3
  SolanaRpcRuby.config do |c|
8
- # These are mandatory options that you must set before using gem:
4
+ # These are options that you can set before using gem:
9
5
  #
6
+ # You can use this setting or pass cluster directly, check the docs.
10
7
  # c.cluster = 'https://api.testnet.solana.com'
11
- # c.json_rpc = '2.0
12
- # c.encoding = 'base58'
13
- # c.id = 1
8
+ # c.ws_cluster = 'ws://api.testnet.solana.com'
9
+
10
+
11
+ # This one is mandatory.
12
+ c.json_rpc_version = '2.0'
14
13
  end
@@ -1,5 +1,4 @@
1
1
  require 'net/http'
2
-
3
2
  module SolanaRpcRuby
4
3
  ##
5
4
  # ApiClient class serves as a client for solana JSON RPC API.
@@ -14,29 +13,29 @@ module SolanaRpcRuby
14
13
  attr_accessor :default_headers
15
14
 
16
15
  # Initialize object with cluster address where requests will be sent.
17
- #
18
- # @param cluster [String]
16
+ #
17
+ # @param cluster [String]
19
18
  def initialize(cluster = nil)
20
19
  @cluster = cluster || SolanaRpcRuby.cluster
21
20
 
22
21
  message = 'Cluster is missing. Please provide default cluster in config or pass it to the client directly.'
23
22
  raise ArgumentError, message unless @cluster
24
23
  end
25
-
24
+
26
25
  # Sends request to the api.
27
26
  #
28
- # @param body [Hash]
27
+ # @param body [Hash]
29
28
  # @param http_method [Symbol]
30
29
  # @param params [Hash]
31
- #
30
+ #
32
31
  # @return [Object] Net::HTTPOK
33
32
  def call_api(body:, http_method:, params: {})
34
33
  uri = URI(@cluster)
35
34
  rpc_response = Net::HTTP.public_send(
36
- http_method,
37
- uri,
38
- body,
39
- default_headers,
35
+ http_method,
36
+ uri,
37
+ body,
38
+ default_headers,
40
39
  )
41
40
 
42
41
  rpc_response
@@ -44,12 +43,12 @@ module SolanaRpcRuby
44
43
  rescue Timeout::Error,
45
44
  Net::HTTPError,
46
45
  Net::HTTPNotFound,
47
- Net::HTTPServerException,
46
+ Net::HTTPClientException,
48
47
  Net::HTTPFatalError,
49
48
  Net::ReadTimeout => e
50
-
51
49
  fail ApiError.new(message: e.message)
52
50
  rescue StandardError => e
51
+
53
52
  message = "#{e.class} #{e.message}\n Backtrace: \n #{e.backtrace}"
54
53
  fail ApiError.new(message: message)
55
54
  end
@@ -6,20 +6,20 @@ module SolanaRpcRuby
6
6
  # Error code.
7
7
  # @return [Integer]
8
8
  attr_reader :code
9
-
9
+
10
10
  # Error message.
11
11
  # @return [String]
12
12
  attr_reader :message
13
-
13
+
14
14
  # Initialize object with json response from the API with error.
15
- #
15
+ #
16
16
  # @param code [Integer]
17
17
  # @param message [String]
18
- #
18
+ #
19
19
  # @return [SolanaRpcRuby::ApiError]
20
20
  def initialize(code: nil, message:)
21
21
  @code = code
22
- @message = message
22
+ @message = message.to_s
23
23
 
24
24
  super message
25
25
  end
@@ -12,5 +12,18 @@ module SolanaRpcRuby
12
12
 
13
13
  object.nil? || object.empty?
14
14
  end
15
+
16
+ # Creates method name to match names required by Solana RPC JSON.
17
+ #
18
+ # @param method [String]
19
+ #
20
+ # @return [String]
21
+ def create_method_name(method)
22
+ return '' unless method && (method.is_a?(String) || method.is_a?(Symbol))
23
+
24
+ method.to_s.split('_').map.with_index do |string, i|
25
+ i == 0 ? string : string.capitalize
26
+ end.join
27
+ end
15
28
  end
16
29
  end