near 0.1.0

Sign up to get free protection for your applications and to get access to all the features.
@@ -0,0 +1,158 @@
1
+ # This is free and unencumbered software released into the public domain.
2
+
3
+ ##
4
+ # @see https://github.com/near/near-cli-rs/blob/main/docs/GUIDE.en.md#contract---Manage-smart-contracts-deploy-code-call-functions
5
+ module NEAR::CLI::Contract
6
+ ##
7
+ # Calls a view method on a contract (read-only).
8
+ #
9
+ # @param [String] contract_id
10
+ # @param [String] method_name
11
+ # @param [Hash] args JSON arguments for the method
12
+ # @param [Block, Integer, String, Symbol] block
13
+ # @return [String] The method call result
14
+ def view_call(contract_id, method_name, args = {}, block: :now)
15
+ stdout, _ = execute(
16
+ 'contract',
17
+ 'call-function',
18
+ 'as-read-only', contract_id, method_name,
19
+ 'json-args', args.to_json,
20
+ 'network-config', @network,
21
+ *block_args(block)
22
+ )
23
+ stdout
24
+ end
25
+
26
+ ##
27
+ # Calls a state-changing method on a contract.
28
+ #
29
+ # @param [String] contract_id
30
+ # @param [String] method_name
31
+ # @param [Hash] args JSON arguments for the method
32
+ # @param [String] signer_id Account that signs the transaction
33
+ # @param [String] deposit Amount of NEAR to attach
34
+ # @param [String] gas Amount of gas to attach
35
+ # @return [String] Transaction result
36
+ def call_function(contract_id, method_name, args = {}, signer_id:, deposit: '0 NEAR', gas: '30 TGas')
37
+ stdout, stderr = execute(
38
+ 'contract',
39
+ 'call-function',
40
+ 'as-transaction', contract_id, method_name,
41
+ 'json-args', args.to_json,
42
+ 'prepaid-gas', gas,
43
+ 'attached-deposit', deposit,
44
+ 'sign-as', signer_id,
45
+ 'network-config', @network,
46
+ 'sign-with-keychain',
47
+ 'send'
48
+ )
49
+ stderr
50
+ end
51
+ alias_method :call_method, :call_function
52
+
53
+ ##
54
+ # Deploys a new contract.
55
+ #
56
+ # @param [String] contract_id Account to deploy the contract to
57
+ # @param [String] wasm_path Path to the .wasm file
58
+ # @param [String] signer_id Account that signs the transaction
59
+ # @param [String, nil] init_method Method to call after deployment
60
+ # @param [Hash] init_args Arguments for the init method
61
+ # @param [String] init_deposit Deposit for the init method
62
+ # @param [String] init_gas Gas for the init method
63
+ # @return [String] Transaction result
64
+ def deploy_contract(contract_id, wasm_path, signer_id:, init_method: nil, init_args: {},
65
+ init_deposit: '0 NEAR', init_gas: '30 TGas')
66
+ args = [
67
+ 'contract',
68
+ 'deploy',
69
+ contract_id,
70
+ 'use-file', wasm_path
71
+ ]
72
+
73
+ if init_method
74
+ args += [
75
+ 'with-init-call', init_method,
76
+ 'json-args', init_args.to_json,
77
+ 'prepaid-gas', init_gas,
78
+ 'attached-deposit', init_deposit
79
+ ]
80
+ end
81
+
82
+ args += [
83
+ 'sign-as', signer_id,
84
+ 'network-config', @network,
85
+ 'sign-with-keychain',
86
+ 'send'
87
+ ]
88
+
89
+ stdout, stderr = execute(*args)
90
+ stderr
91
+ end
92
+
93
+ ##
94
+ # Downloads a contract's WASM code.
95
+ #
96
+ # @param [String] contract_id
97
+ # @param [String] output_path Path to write the .wasm file to
98
+ # @param [Block, Integer, String, Symbol] block
99
+ # @return [String] Path to downloaded file
100
+ def download_wasm(contract_id, output_path, block: :now)
101
+ _, _ = execute(
102
+ 'contract',
103
+ 'download-wasm', contract_id,
104
+ 'save-to-file', output_path,
105
+ 'network-config', @network,
106
+ *block_args(block)
107
+ )
108
+ output_path
109
+ end
110
+
111
+ ##
112
+ # Views contract storage state with JSON formatting.
113
+ #
114
+ # @param [String] contract_id
115
+ # @param [String, nil] prefix Filter keys by prefix
116
+ # @param [Block, Integer, String, Symbol] block
117
+ # @return [String] Contract storage state
118
+ def view_storage(contract_id, prefix: nil, block: :now)
119
+ args = [
120
+ 'contract',
121
+ 'view-storage', contract_id
122
+ ]
123
+
124
+ args += if prefix
125
+ ['keys-start-with-string', prefix]
126
+ else
127
+ ['all']
128
+ end
129
+
130
+ args += [
131
+ 'as-json',
132
+ 'network-config', @network,
133
+ *block_args(block)
134
+ ]
135
+
136
+ stdout, _ = execute(*args)
137
+ stdout
138
+ end
139
+
140
+ ##
141
+ # Views contract storage state with base64 key filtering.
142
+ #
143
+ # @param [String] contract_id
144
+ # @param [String] base64_prefix Filter keys by base64 prefix
145
+ # @param [Block, Integer, String, Symbol] block
146
+ # @return [String] Contract storage state
147
+ def view_storage_base64(contract_id, base64_prefix, block: :now)
148
+ stdout, _ = execute(
149
+ 'contract',
150
+ 'view-storage', contract_id,
151
+ 'keys-start-with-bytes-as-base64', base64_prefix,
152
+ 'as-json',
153
+ 'network-config', @network,
154
+ *block_args(block)
155
+ )
156
+ stdout
157
+ end
158
+ end # NEAR::CLI::Contract
@@ -0,0 +1,7 @@
1
+ # This is free and unencumbered software released into the public domain.
2
+
3
+ ##
4
+ # @see https://github.com/near/near-cli-rs/blob/main/docs/GUIDE.en.md#staking---Manage-staking-view-add-and-withdraw-stake
5
+ module NEAR::CLI::Staking
6
+ # TODO: implement the `NEAR::CLI::Staking` module.
7
+ end # NEAR::CLI::Staking
@@ -0,0 +1,70 @@
1
+ # This is free and unencumbered software released into the public domain.
2
+
3
+ ##
4
+ # @see https://github.com/near/near-cli-rs/blob/main/docs/GUIDE.en.md#tokens---Manage-token-assets-such-as-NEAR-FT-NFT
5
+ module NEAR::CLI::Tokens
6
+ ##
7
+ # Sends NEAR tokens from one account to another.
8
+ #
9
+ # @param [String] sender_id The account sending the tokens
10
+ # @param [String] receiver_id The account receiving the tokens
11
+ # @param [NEAR::Balance] amount The amount to send
12
+ # @return [Hash] Transaction result
13
+ def send_near(sender_id, receiver_id, amount)
14
+ raise ArgumentError, "amount must be a NEAR::Balance" unless amount.is_a?(NEAR::Balance)
15
+
16
+ _, stderr = execute(
17
+ 'tokens',
18
+ sender_id,
19
+ 'send-near', receiver_id, amount.to_cli_arg,
20
+ 'network-config', @network,
21
+ 'sign-with-keychain',
22
+ 'send'
23
+ )
24
+
25
+ # Extract transaction details from stderr:
26
+ if stderr.include?('Transaction sent')
27
+ tx_match = stderr.match(/Transaction ID: ([A-Za-z0-9]+)/)
28
+ tx_id = tx_match ? tx_match[1] : nil
29
+ {
30
+ success: true,
31
+ transaction_id: tx_id,
32
+ message: stderr.strip
33
+ }
34
+ else
35
+ {
36
+ success: false,
37
+ message: stderr.strip
38
+ }
39
+ end
40
+ end
41
+
42
+ ##
43
+ # Views the NEAR token balance of an account.
44
+ #
45
+ # @param [String] account_id The account to check
46
+ # @param [Block, Integer, String, Symbol] block The block to query (default: :now)
47
+ # @return [Hash] Balance information including available and total balance
48
+ def view_near_balance(account_id, block: :now)
49
+ _, stderr = execute(
50
+ 'tokens',
51
+ account_id,
52
+ 'view-near-balance',
53
+ 'network-config', @network,
54
+ *block_args(block)
55
+ )
56
+
57
+ # Parse the balance information from stderr:
58
+ available_match = stderr.match(/(\d+\.?\d*) NEAR available for transfer/)
59
+ total_match = stderr.match(/total balance is (\d+\.?\d*) NEAR/)
60
+ locked_match = stderr.match(/(\d+\.?\d*) NEAR is locked/)
61
+
62
+ {
63
+ account_id: account_id,
64
+ available_balance: available_match ? NEAR::Balance.from_near(available_match[1]) : nil,
65
+ total_balance: total_match ? NEAR::Balance.from_near(total_match[1]) : nil,
66
+ locked_balance: locked_match ? NEAR::Balance.from_near(locked_match[1]) : nil,
67
+ block: block,
68
+ }
69
+ end
70
+ end # NEAR::CLI::Tokens
@@ -0,0 +1,252 @@
1
+ # This is free and unencumbered software released into the public domain.
2
+
3
+ ##
4
+ # @see https://github.com/near/near-cli-rs/blob/main/docs/GUIDE.en.md#transaction---Operate-transactions
5
+ module NEAR::CLI::Transaction
6
+ ##
7
+ # Views status of a transaction.
8
+ #
9
+ # @param [String] tx_hash The transaction hash
10
+ # @return [Hash] Transaction status and details
11
+ def view_status(tx_hash)
12
+ stdout, _ = execute(
13
+ 'transaction',
14
+ 'view-status', tx_hash,
15
+ 'network-config', @network
16
+ )
17
+
18
+ # Parse the transaction details from the output
19
+ parse_transaction_status(stdout)
20
+ end
21
+
22
+ ##
23
+ # Reconstructs a CLI command from an existing transaction.
24
+ #
25
+ # @param [String] tx_hash The transaction hash
26
+ # @return [Hash] The reconstructed transaction details and CLI command
27
+ def reconstruct_transaction(tx_hash)
28
+ stdout, _ = execute(
29
+ 'transaction',
30
+ 'reconstruct-transaction', tx_hash,
31
+ 'network-config', @network
32
+ )
33
+
34
+ {
35
+ transaction: parse_transaction_reconstruction(stdout),
36
+ cli_command: extract_cli_command(stdout)
37
+ }
38
+ end
39
+
40
+ ##
41
+ # Constructs a new transaction with multiple actions.
42
+ #
43
+ # @param [String] signer_id Account that signs the transaction
44
+ # @param [String] receiver_id Account that receives the transaction
45
+ # @param [Array<Hash>] actions Array of action hashes, each containing :type and :args
46
+ # @return [String] Base64-encoded unsigned transaction
47
+ def construct_transaction(signer_id, receiver_id, actions)
48
+ args = [
49
+ 'transaction',
50
+ 'construct-transaction',
51
+ signer_id,
52
+ receiver_id
53
+ ]
54
+
55
+ actions.each do |action|
56
+ args += construct_action_args(action)
57
+ end
58
+
59
+ args += [
60
+ 'network-config', @network
61
+ ]
62
+
63
+ stdout, _ = execute(*args)
64
+ extract_unsigned_transaction(stdout)
65
+ end
66
+
67
+ ##
68
+ # Signs a previously prepared unsigned transaction.
69
+ #
70
+ # @param [String] unsigned_tx Base64-encoded unsigned transaction
71
+ # @param [Hash] options Signing options (e.g., :keychain, :ledger, :private_key)
72
+ # @return [String] Base64-encoded signed transaction
73
+ def sign_transaction(unsigned_tx, options = {})
74
+ args = [
75
+ 'transaction',
76
+ 'sign-transaction',
77
+ unsigned_tx,
78
+ 'network-config', @network
79
+ ]
80
+
81
+ args += case options[:method]
82
+ when :keychain
83
+ ['sign-with-keychain']
84
+ when :ledger
85
+ ['sign-with-ledger']
86
+ when :private_key
87
+ [
88
+ 'sign-with-plaintext-private-key',
89
+ options[:private_key]
90
+ ]
91
+ else
92
+ raise ArgumentError, "Invalid signing method: #{options[:method]}"
93
+ end
94
+
95
+ stdout, _ = execute(*args)
96
+ extract_signed_transaction(stdout)
97
+ end
98
+
99
+ ##
100
+ # Sends a signed transaction.
101
+ #
102
+ # @param [String] signed_tx Base64-encoded signed transaction
103
+ # @return [Hash] Transaction result
104
+ def send_signed_transaction(signed_tx)
105
+ stdout, stderr = execute(
106
+ 'transaction',
107
+ 'send-signed-transaction',
108
+ signed_tx,
109
+ 'network-config', @network
110
+ )
111
+
112
+ parse_transaction_result(stderr)
113
+ end
114
+
115
+ ##
116
+ # Sends a meta transaction (relayed transaction).
117
+ #
118
+ # @param [String] signed_tx Base64-encoded signed transaction
119
+ # @return [Hash] Transaction result
120
+ def send_meta_transaction(signed_tx)
121
+ stdout, stderr = execute(
122
+ 'transaction',
123
+ 'send-meta-transaction',
124
+ signed_tx,
125
+ 'network-config', @network
126
+ )
127
+
128
+ parse_transaction_result(stderr)
129
+ end
130
+
131
+ private
132
+
133
+ def parse_transaction_status(output)
134
+ # Parse the detailed transaction status output
135
+ # This would need to handle all the various fields from the transaction status
136
+ {
137
+ status: extract_status(output),
138
+ receipts: extract_receipts(output),
139
+ outcome: extract_outcome(output)
140
+ }
141
+ end
142
+
143
+ def parse_transaction_reconstruction(output)
144
+ # Parse the transaction reconstruction output
145
+ {
146
+ signer_id: extract_signer(output),
147
+ receiver_id: extract_receiver(output),
148
+ actions: extract_actions(output)
149
+ }
150
+ end
151
+
152
+ def construct_action_args(action)
153
+ case action[:type]
154
+ when :create_account
155
+ ['add-action', 'create-account']
156
+ when :transfer
157
+ ['add-action', 'transfer', action[:args][:amount].to_cli_arg]
158
+ when :add_key
159
+ construct_add_key_args(action[:args])
160
+ when :delete_key
161
+ ['add-action', 'delete-key', action[:args][:public_key]]
162
+ when :deploy
163
+ ['add-action', 'deploy', action[:args][:code_path]]
164
+ when :function_call
165
+ [
166
+ 'add-action', 'function-call',
167
+ action[:args][:method_name],
168
+ 'json-args', action[:args][:args].to_json,
169
+ '--prepaid-gas', action[:args][:gas],
170
+ '--attached-deposit', action[:args][:deposit].to_cli_arg
171
+ ]
172
+ else
173
+ raise ArgumentError, "Unknown action type: #{action[:type]}"
174
+ end
175
+ end
176
+
177
+ def construct_add_key_args(args)
178
+ base_args = ['add-action', 'add-key']
179
+
180
+ if args[:permission] == :full_access
181
+ base_args + ['grant-full-access']
182
+ else
183
+ base_args + [
184
+ 'grant-function-call-access',
185
+ '--allowance', args[:allowance].to_cli_arg,
186
+ '--receiver-account-id', args[:receiver_id],
187
+ '--method-names', Array(args[:method_names]).join(',')
188
+ ]
189
+ end + [
190
+ 'use-manually-provided-public-key', args[:public_key]
191
+ ]
192
+ end
193
+
194
+ def parse_transaction_result(output)
195
+ {
196
+ success: output.include?('Transaction sent'),
197
+ transaction_id: extract_transaction_id(output),
198
+ message: output
199
+ }
200
+ end
201
+
202
+ def extract_unsigned_transaction(output)
203
+ # Extract the base64 unsigned transaction from output
204
+ output[/Unsigned transaction: ([A-Za-z0-9+\/=]+)/, 1]
205
+ end
206
+
207
+ def extract_signed_transaction(output)
208
+ # Extract the base64 signed transaction from output
209
+ output[/Signed transaction: ([A-Za-z0-9+\/=]+)/, 1]
210
+ end
211
+
212
+ def extract_transaction_id(output)
213
+ output[/Transaction ID: ([A-Za-z0-9]+)/, 1]
214
+ end
215
+
216
+ def extract_cli_command(output)
217
+ output[/Here is your console command[^\n]*\n\s*(near.+)$/, 1]
218
+ end
219
+
220
+ # Additional helper methods for parsing transaction status output
221
+ def extract_status(output)
222
+ # Extract status from transaction status output
223
+ end
224
+
225
+ def extract_receipts(output)
226
+ # Extract receipts from transaction status output
227
+ end
228
+
229
+ def extract_outcome(output)
230
+ # Extract outcome from transaction status output
231
+ end
232
+
233
+ def extract_signer(output)
234
+ output[/signer_id:\s+(\S+)/, 1]
235
+ end
236
+
237
+ def extract_receiver(output)
238
+ output[/receiver_id:\s+(\S+)/, 1]
239
+ end
240
+
241
+ def extract_actions(output)
242
+ # Extract and parse actions list from output
243
+ actions = []
244
+ output.scan(/-- ([^:]+):\s+(.+)$/) do |type, details|
245
+ actions << {
246
+ type: type.strip.downcase.to_sym,
247
+ details: details.strip
248
+ }
249
+ end
250
+ actions
251
+ end
252
+ end # NEAR::CLI::Transaction
data/lib/near/cli.rb ADDED
@@ -0,0 +1,87 @@
1
+ # This is free and unencumbered software released into the public domain.
2
+
3
+ require 'json'
4
+ require 'open3'
5
+
6
+ require_relative 'block'
7
+
8
+ class NEAR::CLI; end
9
+
10
+ require_relative 'cli/account'
11
+ require_relative 'cli/config'
12
+ require_relative 'cli/contract'
13
+ require_relative 'cli/staking'
14
+ require_relative 'cli/tokens'
15
+ require_relative 'cli/transaction'
16
+
17
+ class NEAR::CLI
18
+ NEAR_ENV = ENV['NEAR_ENV'] || 'testnet'
19
+
20
+ class Error < StandardError; end
21
+ class ProgramNotFoundError < Error; end
22
+ class ExecutionError < Error; end
23
+
24
+ def self.find_program
25
+ # First, check `$HOME/.cargo/bin/near`:
26
+ path = File.expand_path('~/.cargo/bin/near')
27
+ return path if File.executable?(path)
28
+
29
+ # Next, check `$PATH`:
30
+ ENV['PATH'].split(File::PATH_SEPARATOR).each do |path|
31
+ path = File.join(path, 'near')
32
+ return path if File.executable?(path)
33
+ end
34
+
35
+ raise ProgramNotFoundError
36
+ end
37
+
38
+ ##
39
+ # @param [String, #to_s] path
40
+ # @param [String, #to_s] network
41
+ def initialize(path: self.class.find_program, network: NEAR_ENV)
42
+ @path, @network = path.to_s, network.to_s
43
+ end
44
+
45
+ ##
46
+ # @return [String]
47
+ def version
48
+ self.execute('--version').first.strip
49
+ end
50
+
51
+ include NEAR::CLI::Account
52
+ include NEAR::CLI::Tokens
53
+ include NEAR::CLI::Staking
54
+ include NEAR::CLI::Contract
55
+ include NEAR::CLI::Transaction
56
+ include NEAR::CLI::Config
57
+
58
+ private
59
+
60
+ ##
61
+ # @param [Array<String>] args
62
+ # @return [Array<String>]
63
+ def execute(*args)
64
+ command = [@path, *args.map(&:to_s)]
65
+ puts command.join(' ') if false
66
+ stdout, stderr, status = Open3.capture3(*command)
67
+
68
+ if status.success?
69
+ [stdout.strip, stderr.strip]
70
+ else
71
+ raise ExecutionError, "Command `#{command.join(' ')}` failed with exit code #{status.exitstatus}: #{stderr.strip}"
72
+ end
73
+ end
74
+
75
+ ##
76
+ # @param [Block, Integer, String, Symbol] block
77
+ def block_args(block)
78
+ block = case block
79
+ when :now then return ['now']
80
+ when NEAR::Block then block
81
+ when Integer then NEAR::Block.at_height(block)
82
+ when String then NEAR::Block.at_hash(block)
83
+ else raise "invalid block specifier: #{block.inspect}"
84
+ end
85
+ block.to_cli_args
86
+ end
87
+ end # NEAR::CLI
@@ -0,0 +1,21 @@
1
+ # This is free and unencumbered software released into the public domain.
2
+
3
+ ##
4
+ # Represents a NEAR transaction.
5
+ class NEAR::Transaction
6
+ ##
7
+ # @param [String, #to_s] hash
8
+ def initialize(hash)
9
+ @hash = hash.to_s
10
+ end
11
+
12
+ ##
13
+ # The transaction hash.
14
+ #
15
+ # @return [String]
16
+ attr_reader :hash
17
+
18
+ ##
19
+ # @return [String]
20
+ def to_s; @hash; end
21
+ end # NEAR::Transaction
@@ -0,0 +1 @@
1
+ # This is free and unencumbered software released into the public domain.
data/lib/near.rb ADDED
@@ -0,0 +1,10 @@
1
+ # This is free and unencumbered software released into the public domain.
2
+
3
+ module NEAR; end
4
+
5
+ require_relative 'near/account'
6
+ require_relative 'near/balance'
7
+ require_relative 'near/block'
8
+ require_relative 'near/cli'
9
+ require_relative 'near/transaction'
10
+ require_relative 'near/version'
metadata ADDED
@@ -0,0 +1,90 @@
1
+ --- !ruby/object:Gem::Specification
2
+ name: near
3
+ version: !ruby/object:Gem::Version
4
+ version: 0.1.0
5
+ platform: ruby
6
+ authors:
7
+ - Arto Bendiken
8
+ bindir: bin
9
+ cert_chain: []
10
+ date: 2025-01-19 00:00:00.000000000 Z
11
+ dependencies:
12
+ - !ruby/object:Gem::Dependency
13
+ name: rspec
14
+ requirement: !ruby/object:Gem::Requirement
15
+ requirements:
16
+ - - "~>"
17
+ - !ruby/object:Gem::Version
18
+ version: '3.12'
19
+ type: :development
20
+ prerelease: false
21
+ version_requirements: !ruby/object:Gem::Requirement
22
+ requirements:
23
+ - - "~>"
24
+ - !ruby/object:Gem::Version
25
+ version: '3.12'
26
+ - !ruby/object:Gem::Dependency
27
+ name: yard
28
+ requirement: !ruby/object:Gem::Requirement
29
+ requirements:
30
+ - - "~>"
31
+ - !ruby/object:Gem::Version
32
+ version: '0.9'
33
+ type: :development
34
+ prerelease: false
35
+ version_requirements: !ruby/object:Gem::Requirement
36
+ requirements:
37
+ - - "~>"
38
+ - !ruby/object:Gem::Version
39
+ version: '0.9'
40
+ description: A Ruby client library for the NEAR Protocol.
41
+ email: arto@bendiken.net
42
+ executables: []
43
+ extensions: []
44
+ extra_rdoc_files: []
45
+ files:
46
+ - AUTHORS
47
+ - CHANGES.md
48
+ - README.md
49
+ - UNLICENSE
50
+ - VERSION
51
+ - lib/near.rb
52
+ - lib/near/account.rb
53
+ - lib/near/balance.rb
54
+ - lib/near/block.rb
55
+ - lib/near/cli.rb
56
+ - lib/near/cli/account.rb
57
+ - lib/near/cli/config.rb
58
+ - lib/near/cli/contract.rb
59
+ - lib/near/cli/staking.rb
60
+ - lib/near/cli/tokens.rb
61
+ - lib/near/cli/transaction.rb
62
+ - lib/near/transaction.rb
63
+ - lib/near/version.rb
64
+ homepage: https://github.com/dryruby/near.rb
65
+ licenses:
66
+ - Unlicense
67
+ metadata:
68
+ bug_tracker_uri: https://github.com/dryruby/near.rb/issues
69
+ changelog_uri: https://github.com/dryruby/near.rb/blob/master/CHANGES.md
70
+ documentation_uri: https://rubydoc.info/gems/near
71
+ homepage_uri: https://github.com/dryruby/near.rb
72
+ source_code_uri: https://github.com/dryruby/near.rb
73
+ rdoc_options: []
74
+ require_paths:
75
+ - lib
76
+ required_ruby_version: !ruby/object:Gem::Requirement
77
+ requirements:
78
+ - - ">="
79
+ - !ruby/object:Gem::Version
80
+ version: '3.0'
81
+ required_rubygems_version: !ruby/object:Gem::Requirement
82
+ requirements:
83
+ - - ">="
84
+ - !ruby/object:Gem::Version
85
+ version: '0'
86
+ requirements: []
87
+ rubygems_version: 3.6.2
88
+ specification_version: 4
89
+ summary: 'NEAR.rb: NEAR for Ruby'
90
+ test_files: []