extism 1.0.0.pre.rc.2 → 1.0.0.pre.rc.3

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: 9de040945d856411394ee9e9631aeffdf7b89a6e1269788e823191a26c0e50a8
4
- data.tar.gz: 9a588d3b134ee5e1d817d38bb4bd0b8abb92d48148c35d4b538870872dffafd3
3
+ metadata.gz: ff531578509d2c71f3db9d20e01fdbbba922e342f76dbb84aafa69ce94db3350
4
+ data.tar.gz: 163c31c8cddeec68fd774af387effcbd13e3a8e9ea40ab26445ab94fa6db75a3
5
5
  SHA512:
6
- metadata.gz: a675e36bf299fbb2dc2457a35569ef491f0f6be308f7afe7e01603d83971fe6b7f8a23051bb385eb1de5a9be6948d8658128975aa3b183f02b54c9e0312abb58
7
- data.tar.gz: '082c612825780d24869dd7a975adf67cf61f5a7e90b3c4b801cad6769ef69eb237e43621a118a95c6d376bc1d88e57fb0ba1f27519ffc31f52850a453c229865'
6
+ metadata.gz: a5b97afbbeddff3e52a0627832fa92b860ef921cee1b82d0f1a4753b2b9a47ae89810e2f2c8b061f94aff9fa639283a7886d06026e3c98a588693f86369cbdb1
7
+ data.tar.gz: 5399c2a2d9f0183bad275c212f7009b73ef9234ddc2cc4a90433972a3decb36926c967fd3935f20e58f2cb18966c9e76546e967db4882ecc01936eb0593092ab
data/.yardopts CHANGED
@@ -1 +1,2 @@
1
1
  --readme README.md
2
+ --exclude lib/extism/libextism.rb
data/Gemfile CHANGED
@@ -1,16 +1,16 @@
1
- # frozen_string_literal: true
2
-
3
- source 'https://rubygems.org'
4
-
5
- # Specify your gem's dependencies in extism.gemspec
6
- gemspec
7
-
8
- gem 'ffi', '~> 1.15.5'
9
- gem 'rake', '~> 13.0'
10
-
11
- group :development do
12
- gem 'debug'
13
- gem 'minitest', '~> 5.20.0'
14
- gem 'rufo', '~> 0.13.0'
15
- gem 'yard', '~> 0.9.28'
16
- end
1
+ # frozen_string_literal: true
2
+
3
+ source 'https://rubygems.org'
4
+
5
+ # Specify your gem's dependencies in extism.gemspec
6
+ gemspec
7
+
8
+ gem 'ffi', '~> 1.15.5'
9
+ gem 'rake', '~> 13.0'
10
+
11
+ group :development do
12
+ gem 'debug'
13
+ gem 'minitest', '~> 5.20.0'
14
+ gem 'rufo', '~> 0.13.0'
15
+ gem 'yard', '~> 0.9.28'
16
+ end
data/Gemfile.lock CHANGED
@@ -1,7 +1,7 @@
1
1
  PATH
2
2
  remote: .
3
3
  specs:
4
- extism (1.0.0.pre.rc.2)
4
+ extism (1.0.0.pre.rc.3)
5
5
  ffi (>= 1.0.0)
6
6
 
7
7
  GEM
@@ -29,6 +29,7 @@ GEM
29
29
 
30
30
  PLATFORMS
31
31
  arm64-darwin-22
32
+ x86_64-linux
32
33
 
33
34
  DEPENDENCIES
34
35
  debug
data/README.md CHANGED
@@ -1,14 +1,14 @@
1
1
  # Extism Ruby Host SDK
2
2
 
3
- This repo houses the ruby gem for integrating with the [Extism](https://extism.org/) runtime. Install this library into your host ruby applications to run Extism plug-ins.
3
+ This repo contains the ruby gem for integrating with the [Extism](https://extism.org/) runtime. Install this library into your host ruby application to run Extism plug-ins.
4
4
 
5
- > **Note**: This repo is 1.0 alpha version of the Ruby SDK and is a work in progress. We'd love any feedback you have on it, but consider using the supported ruby SDK in [extism/extism](https://github.com/extism/extism/tree/main/ruby) until we hit 1.0.
5
+ > **Note**: This repo is 1.0 alpha version of the Ruby SDK and we may push breaking changes in between versions until we hit 1.0.0 in December, 2023. However, it is ready to use and you should use this if you're building a new integration. We'd love any feedback on it.
6
6
 
7
7
  ## Installation
8
8
 
9
- ### Install the Extism Runtime
9
+ ### Install the Extism Runtime Dependency
10
10
 
11
- You first need to install the Extism Runtime which is a native shared object that this library uses to load and run the Wasm code. You can [download the shared library directly from a release](https://github.com/extism/extism/releases) or use the [Extism CLI](https://github.com/extism/cli) to install it:
11
+ For this library, you first need to install the Extism Runtime. You can [download the shared object directly from a release](https://github.com/extism/extism/releases) or use the [Extism CLI](https://github.com/extism/cli) to install it:
12
12
 
13
13
  ```bash
14
14
  sudo extism lib install latest
@@ -18,14 +18,14 @@ sudo extism lib install latest
18
18
  #=> Copying extism.h to /usr/local/include/extism.h
19
19
  ```
20
20
 
21
- > **Note**: This library has breaking changes and targets 1.0 of the runtime. For the time being, install the runtime from our nightly development builds on git: `sudo extism lib install --version git`
21
+ > **Note**: This library has breaking changes and targets 1.0 of the runtime. For the time being, install the runtime from our nightly development builds on git: `sudo extism lib install --version git`.
22
22
 
23
- ### Install the Rubygem
23
+ ### Install the Gem
24
24
 
25
25
  Add this library to your [Gemfile](https://bundler.io/):
26
26
 
27
27
  ```ruby
28
- gem 'extism', '1.0.0.pre.rc.2'
28
+ gem 'extism', '1.0.0.pre.rc.3'
29
29
  ```
30
30
 
31
31
  Or if installing on the system level:
@@ -36,41 +36,37 @@ gem install extism --pre
36
36
 
37
37
  ## Getting Started
38
38
 
39
- > *Note*: You should be able to follow this guide by copy pasting the code into `irb`.
40
-
41
- First you should require `"extism"`:
39
+ This guide should walk you through some of the concepts in Extism and this ruby library.
42
40
 
43
- ```ruby
44
- require "extism"
45
- ```
41
+ > *Note*: You should be able to follow this guide by copy pasting the code into `irb`.
46
42
 
47
43
  ### Creating A Plug-in
48
44
 
49
- The primary concept in Extism is the plug-in. You can think of a plug-in as a code module stored in a `.wasm` file. You can [learn more about plug-ins here](https://extism.org/concepts/plug-in).
45
+ The primary concept in Extism is the [plug-in](https://extism.org/docs/concepts/plug-in). You can think of a plug-in as a code module stored in a `.wasm` file.
50
46
 
51
- You'll generally load the plug-in from disk, but for simplicity let's load a pre-built demo plug-in from the web:
47
+ Since you may not have an Extism plug-in on hand to test, let's load a demo plug-in from the web:
52
48
 
53
49
  ```ruby
54
- manifest = {
55
- wasm: [
56
- { url: "https://github.com/extism/plugins/releases/latest/download/count_vowels.wasm" }
57
- ]
58
- }
50
+ # First require the library
51
+ require "extism"
52
+
53
+ url = "https://github.com/extism/plugins/releases/latest/download/count_vowels.wasm"
54
+ manifest = Extism::Manifest.from_url url
59
55
  plugin = Extism::Plugin.new(manifest)
60
56
  ```
61
57
 
62
- > **Note**: The schema for this manifest can be found here: [https://extism.org/docs/concepts/manifest/](https://extism.org/docs/concepts/manifest/)
58
+ > **Note**: See [the Manifest docs](https://extism.github.io/ruby-sdk/Extism/Manifest.html) as it has a rich schema and a lot of options.
63
59
 
64
60
  ### Calling A Plug-in's Exports
65
61
 
66
- This plug-in was written in Rust and it does one thing, it counts vowels in a string. As such it exposes one "export" function: `count_vowels`. We can call exports using [Extism::Plugin#call](https://extism.github.io/ruby-sdk/Extism/Plugin.html#call-instance_method):
62
+ This plug-in was written in Rust and it does one thing, it counts vowels in a string. As such, it exposes one "export" function: `count_vowels`. We can call exports using [Extism::Plugin#call](https://extism.github.io/ruby-sdk/Extism/Plugin.html#call-instance_method):
67
63
 
68
64
  ```ruby
69
65
  plugin.call("count_vowels", "Hello, World!")
70
66
  # => {"count": 3, "total": 3, "vowels": "aeiouAEIOU"}
71
67
  ```
72
68
 
73
- All exports have a simple interface of optional bytes in, and optional bytes out. This plug-in happens to take a string and return a JSON encoded string with a report of results.
69
+ All exports have a simple interface of bytes-in and bytes-out. This plug-in happens to take a string and return a JSON encoded string with a report of results.
74
70
 
75
71
  ### Plug-in State
76
72
 
@@ -101,106 +97,73 @@ plugin.call("count_vowels", "Yellow, World!")
101
97
 
102
98
  ### Host Functions
103
99
 
104
- Host functions allow us to grant new capabilities to our plug-ins from our application. They are simply some ruby methods you write which can be passed to and invoked from any language inside the plug-in.
105
-
106
- > *Note*: Host functions can be a complicated topic. Please review this [concept doc](https://extism.org/docs/concepts/host-functions) if you are unsure how they work.
107
-
108
- ### Host Functions Example
100
+ Let's extend our count-vowels example a little bit: Instead of storing the `total` in an ephemeral plug-in var, let's store it in a persistent key-value store!
109
101
 
110
- We've created a contrived, but familiar example to illustrate this. Suppose you are a stripe-like payments platform.
111
- When a [charge.succeeded](https://stripe.com/docs/api/events/types#event_types-charge.succeeded) event occurs, we will call the `on_charge_succeeded` function on our merchant's plug-in and let them decide what to do with it. Here our merchant has some very specific requirements, if the account has spent more than $100, their currency is USD, and they have no credits on their account, it will add $10 credit to their account and then send them an email.
102
+ Wasm can't use our KV store on it's own. This is where [Host Functions](https://extism.org/docs/concepts/host-functions) come in.
112
103
 
113
- > *Note*: The source code for this is [here](https://github.com/extism/plugins/blob/main/store_credit/src/lib.rs) and is written in rust, but it could be written in any of our PDK languages.
104
+ [Host functions](https://extism.org/docs/concepts/host-functions) allow us to grant new capabilities to our plug-ins from our application. They are simply some ruby methods you write which can be passed down and invoked from any language inside the plug-in.
114
105
 
115
- First let's create the manifest for our plug-in like usual but load up the `store_credit` plug-in:
106
+ Let's load the manifest like usual but load up this `count_vowels_kvstore` plug-in:
116
107
 
117
108
  ```ruby
118
- manifest = {
119
- wasm: [
120
- { url: "https://github.com/extism/plugins/releases/latest/download/store_credit.wasm" }
121
- ]
122
- }
109
+ url = "https://github.com/extism/plugins/releases/latest/download/count_vowels_kvstore.wasm"
110
+ manifest = Extism::Manifest.from_url(url)
123
111
  ```
124
112
 
125
- But, unlike our `count_vowels` plug-in, this plug-in expects you to provide host functions that satisfy our plug-in's imports.
113
+ > *Note*: The source code for this is [here](https://github.com/extism/plugins/blob/main/count_vowels_kvstore/src/lib.rs) and is written in rust, but it could be written in any of our PDK languages.
126
114
 
127
- In the ruby sdk, we have a concept for this called a [Host Environment](https://extism.github.io/ruby-sdk/Extism/HostEnvironment.html). An environment is just an object that responds to `host_functions` and returns an array of `Extism::Function`s. We want to expose two capabilities to our plugin, `add_credit(customer_id, amount)` which adds credit to an account and `send_email(customer_id, email)` which sends them an email.
115
+ Unlike our previous plug-in, this plug-in expects you to provide host functions that satisfy our its import interface for a KV store.
128
116
 
129
- ```ruby
117
+ In the ruby sdk, we have a concept for this called a [Host Environment](https://extism.github.io/ruby-sdk/Extism/HostEnvironment.html). An environment is an instance of a class that implements any host functions your plug-in needs.
130
118
 
131
- # This is global is just for demo purposes but would in
132
- # reality be in a database or something
133
- CUSTOMER = {
134
- full_name: 'John Smith',
135
- customer_id: 'abcd1234',
136
- total_spend: {
137
- currency: 'USD',
138
- amount_in_cents: 20_000
139
- },
140
- credit: {
141
- currency: 'USD',
142
- amount_in_cents: 0
143
- }
144
- }
145
-
146
- class MyEnvironment
147
- include Extism::HostEnvironment
119
+ We want to expose two functions to our plugin, `kv_write(key: String, value: Bytes)` which writes a bytes value to a key and `kv_read(key: String) -> Bytes` which reads the bytes at the given `key`.
148
120
 
149
- # we need to register each import that the plug-in expects and match the Wasm signature
150
- # register_import takes the name, the param types, and the return types
151
- register_import :add_credit, [Extism::ValType::I64, Extism::ValType::I64], [Extism::ValType::I64]
152
- register_import :send_email, [Extism::ValType::I64, Extism::ValType::I64], []
121
+ ```ruby
122
+ # pretend this is Redis or something :)
123
+ KV_STORE = {}
153
124
 
154
- def add_credit(plugin, inputs, outputs, _user_data)
155
- # add_credit takes a string `customer_id` as the first parameter
156
- customer_id = plugin.input_as_string(inputs.first)
157
- # it takes an object `amount` { amount_in_cents: int, currency: string } as the second parameter
158
- amount = plugin.input_as_json(inputs[1])
125
+ class KvEnvironment
126
+ include Extism::HostEnvironment
159
127
 
160
- # we're just going to print it out and add to the CUSTOMER global
161
- puts "Adding Credit #{amount} to customer #{customer_id}"
162
- CUSTOMER[:credit][:amount_in_cents] += amount['amount_in_cents']
128
+ # We need to describe the wasm function signature of each host function
129
+ # to register them to this environment
130
+ register_import :kv_read, [Extism::ValType::PTR], [Extism::ValType::PTR]
131
+ register_import :kv_write, [Extism::ValType::PTR, Extism::ValType::PTR], []
163
132
 
164
- # add_credit returns a Json object with the new customer details
165
- plugin.return_json(outputs.first, CUSTOMER)
133
+ def kv_read(plugin, inputs, outputs, _user_data)
134
+ key = plugin.input_as_string(inputs.first)
135
+ val = KV_STORE[key] || [0].pack('V') # get 4 LE bytes for 0 default
136
+ puts "Read from key=#{key}"
137
+ plugin.output_string(outputs.first, val)
166
138
  end
167
139
 
168
- def send_email(plugin, inputs, _outputs, _user_data)
169
- # send_email takes a string `customer_id` as the first parameter
170
- customer_id = plugin.input_as_string(inputs.first)
171
- # it takes an object `email` { subject: string, body: string } as the second parameter
172
- email = plugin.input_as_json(inputs[1])
173
-
174
- # we'll just print it but you could imagine we'd put something
175
- # in a database or call an internal api to send this email
176
- puts "Sending email #{email} to customer #{customer_id}"
177
-
178
- # it doesn't return anything
140
+ def kv_write(plugin, inputs, _outputs, _user_data)
141
+ key = plugin.input_as_string(inputs.first)
142
+ val = plugin.input_as_string(inputs[1])
143
+ puts "Writing value=#{val.unpack1('V')} from key=#{key}"
144
+ KV_STORE[key] = val
179
145
  end
180
146
  end
181
147
  ```
182
148
 
183
- Now we just need to create a new host environment and pass it in when loading the plug-in. Here our environment initializer takes no arguments, but you could imagine putting some merchant specific instance variables in there:
149
+ > *Note*: In order to write host functions you should get familiar with the methods on the [Extism::CurrentPlugin](https://extism.github.io/ruby-sdk/Extism/CurrentPlugin.html) class. The `plugin` parameter is an instance of this class.
150
+
151
+ Now we just need to create a new host environment and pass it in when loading the plug-in. Here our environment initializer takes no arguments, but you could imagine putting some customer specific instance variables in there:
184
152
 
185
153
  ```ruby
186
- env = MyEnvironment.new
154
+ env = KvEnvironment.new
187
155
  plugin = Extism::Plugin.new(manifest, environment: env)
188
156
  ```
189
157
 
190
158
  Now we can invoke the event:
191
159
 
192
160
  ```ruby
193
- event = {
194
- event_type: 'charge.succeeded',
195
- customer: CUSTOMER
196
- }
197
- result = plugin.call('on_charge_succeeded', JSON.generate(event))
198
- ```
199
-
200
- This will print:
201
-
202
- ```
203
- Adding Credit {"amount_in_cents"=>1000, "currency"=>"USD"} for customer abcd1234
204
- Sending email {"subject"=>"A gift for you John Smith", "body"=>"You have received $10 in store credi
205
- t!"} to customer abcd1234
161
+ plugin.call("count_vowels", "Hello, World!")
162
+ # => Read from key=count-vowels"
163
+ # => Writing value=3 from key=count-vowels"
164
+ # => {"count": 3, "total": 3, "vowels": "aeiouAEIOU"}
165
+ plugin.call("count_vowels", "Hello, World!")
166
+ # => Read from key=count-vowels"
167
+ # => Writing value=6 from key=count-vowels"
168
+ # => {"count": 3, "total": 6, "vowels": "aeiouAEIOU"}
206
169
  ```
data/Rakefile CHANGED
@@ -1,12 +1,12 @@
1
- # frozen_string_literal: true
2
-
3
- require "bundler/gem_tasks"
4
- require "rake/testtask"
5
-
6
- Rake::TestTask.new(:test) do |t|
7
- t.libs << "test"
8
- t.libs << "lib"
9
- t.test_files = FileList["test/**/test_*.rb"]
10
- end
11
-
12
- task default: :test
1
+ # frozen_string_literal: true
2
+
3
+ require "bundler/gem_tasks"
4
+ require "rake/testtask"
5
+
6
+ Rake::TestTask.new(:test) do |t|
7
+ t.libs << "test"
8
+ t.libs << "lib"
9
+ t.test_files = FileList["test/**/test_*.rb"]
10
+ end
11
+
12
+ task default: :test
@@ -16,15 +16,15 @@ module Extism
16
16
 
17
17
  # Allocates a memory block in the plugin
18
18
  #
19
- # @example
19
+ # @example Allocate 1kB
20
20
  # mem = current_plugin.alloc(1_024)
21
- # mem.put_bytes(0, "Hello, World!")
21
+ # current_plugin.free(mem)
22
22
  #
23
- # @param amount [Integer] The amount in bytes to allocate
23
+ # @param num_bytes [Integer] The amount in bytes to allocate
24
24
  # @return [Extism::Memory] The reference to the freshly allocated memory
25
- def alloc(amount)
26
- offset = LibExtism.extism_current_plugin_memory_alloc(@ptr, amount)
27
- Memory.new(offset, amount)
25
+ def alloc(num_bytes)
26
+ offset = LibExtism.extism_current_plugin_memory_alloc(@ptr, num_bytes)
27
+ Memory.new(offset, num_bytes)
28
28
  end
29
29
 
30
30
  # Frees the memory block
@@ -142,7 +142,7 @@ module Extism
142
142
  private
143
143
 
144
144
  # Returns a raw pointer (absolute to the host) to the given memory block
145
- # Be careful with this. it's not exposed for a reason.
145
+ # **Danger**: Be careful with this. it's not exposed for a reason.
146
146
  # This is a pointer in host memory so it could read outside of the plugin
147
147
  # if manipulated
148
148
  def memory_ptr(mem)
@@ -21,6 +21,12 @@ module Extism
21
21
  base.class_variable_set(:@@import_funcs, [])
22
22
  end
23
23
 
24
+ # Creates the host functions to pass to the plug-in on intialization.
25
+ # Used internally by the Plugin initializer
26
+ #
27
+ # @see Extism::Plugin::new
28
+ #
29
+ # @return [Array<Extism::Function>]
24
30
  def host_functions
25
31
  import_funcs = self.class.class_variable_get(:@@import_funcs)
26
32
  import_funcs.map do |f|
@@ -33,12 +39,21 @@ module Extism
33
39
  )
34
40
  end
35
41
  end
36
- end
37
42
 
38
- module ClassMethods
39
- def register_import(func_name, parameters, returns)
40
- import_funcs = class_variable_get(:@@import_funcs)
41
- import_funcs << [func_name, parameters, returns]
43
+ module ClassMethods
44
+ # Register an import by name. You must know the wasm signature
45
+ # of the function to do this.
46
+ #
47
+ # @example
48
+ # register_import :my_func, [Extism::ValType::I64], [Extism::ValType::F64]
49
+ #
50
+ # @param [Symbol | String] func_name The name of the wasm import function. Assumes `env` namespace.
51
+ # @param [Array<Extism::ValType>] parameters The Wasm types of the parameters that the import takes
52
+ # @param [Array<Extism::ValType>] returns The Wasm types of the returns that the import returns. Will usually be just be one of these.
53
+ def register_import(func_name, parameters, returns)
54
+ import_funcs = class_variable_get(:@@import_funcs)
55
+ import_funcs << [func_name, parameters, returns]
56
+ end
42
57
  end
43
58
  end
44
59
  end
@@ -0,0 +1,68 @@
1
+ module Extism
2
+ # The manifest represents a recipe to build a plug-in.
3
+ # It generally consists of a path to one wasm module
4
+ # but could contain more. It also helps you define some
5
+ # options and restrictions on the runtime behavior of the plug-in.
6
+ # See https://extism.org/docs/concepts/manifest for more info.
7
+ class Manifest
8
+ attr_reader :manifest_data
9
+
10
+ # Create a manifest of a single wasm from url.
11
+ # Look at {Manifest#initialize} for an interface with more control
12
+ #
13
+ # @see Manifest::initialize
14
+ # @param [String] url The url to the wasm module
15
+ # @param [String | nil] hash An optional sha256 integrity hash. Defaults to nil
16
+ # @param [String | nil] name An optional name. Defaults to nil
17
+ # @return [Extism::Manifest]
18
+ def self.from_url(url, hash: nil, name: nil)
19
+ wasm = { url: url }
20
+ wasm[:hash] = hash unless hash.nil?
21
+ wasm[:name] = name unless hash.nil?
22
+
23
+ Manifest.new({ wasm: [wasm] })
24
+ end
25
+
26
+ # Create a manifest of a single wasm from file path.
27
+ # Look at {Manifest#initialize} for an interface with more control
28
+ #
29
+ # @see Manifest::initialize
30
+ # @param [String] path The path to the wasm module on disk
31
+ # @param [String | nil] hash An optional sha256 integrity hash. Defaults to nil
32
+ # @param [String | nil] name An optional name. Defaults to nil
33
+ # @return [Extism::Manifest]
34
+ def self.from_path(path, hash: nil, name: nil)
35
+ wasm = { path: path }
36
+ wasm[:hash] = hash unless hash.nil?
37
+ wasm[:name] = name unless hash.nil?
38
+
39
+ Manifest.new({ wasm: [wasm] })
40
+ end
41
+
42
+ # Create a manifest of a single wasm module with raw binary data.
43
+ # Look at {Manifest#initialize} for an interface with more control
44
+ # Consider using a file path instead of the raw wasm binary in memory.
45
+ # The performance is often better letting the runtime load the binary itself.
46
+ #
47
+ # @see Manifest::initialize
48
+ # @param [String] data The binary data of the wasm module
49
+ # @param [String | nil] hash An optional sha256 integrity hash. Defaults to nil
50
+ # @param [String | nil] name An optional name. Defaults to nil
51
+ # @return [Extism::Manifest]
52
+ def self.from_bytes(data, hash: nil, name: nil)
53
+ wasm = { data: data }
54
+ wasm[:hash] = hash unless hash.nil?
55
+ wasm[:name] = name unless hash.nil?
56
+
57
+ Manifest.new({ wasm: [wasm] })
58
+ end
59
+
60
+ # Initialize a manifest
61
+ # See https://extism.org/docs/concepts/manifest for schema
62
+ #
63
+ # @param [Hash] data The Hash data that conforms the Manifest schema
64
+ def initialize(data)
65
+ @manifest_data = data
66
+ end
67
+ end
68
+ end
data/lib/extism/plugin.rb CHANGED
@@ -4,19 +4,29 @@ module Extism
4
4
  class Plugin
5
5
  # Intialize a plugin
6
6
  #
7
- # @example
8
- # manifest = {
9
- # wasm: [
10
- # { url: "https://github.com/extism/plugins/releases/latest/download/count_vowels.wasm" }
11
- # ]
12
- # }
7
+ # @example Initialize a plugin from a url
8
+ # manifest = Extism::Manifest.from_url "https://github.com/extism/plugins/releases/latest/download/count_vowels.wasm"
13
9
  # plugin = Extism::Plugin.new(manifest)
14
10
  #
15
- # @param wasm [Hash, String] The manifest as a Hash or WASM binary as a String. See https://extism.org/docs/concepts/manifest/.
11
+ # @example Pass a config object to configure the plug-in
12
+ # plugin = Extism::Plugin.new(manifest, config: { hello: "world" })
13
+ #
14
+ # @example Initalize a plug-in that needs WASI
15
+ # plugin = Extism::Plugin.new(manifest, wasi: true)
16
+ #
17
+ # @param wasm [Hash, String, Manifest] The manifest as a Hash or WASM binary as a String. See https://extism.org/docs/concepts/manifest/.
16
18
  # @param wasi [Boolean] Enable WASI support
17
19
  # @param config [Hash] The plugin config
18
20
  def initialize(wasm, environment: nil, functions: [], wasi: false, config: nil)
19
- wasm = JSON.generate(wasm) if wasm.instance_of?(Hash)
21
+ wasm = case wasm
22
+ when Hash
23
+ JSON.generate(wasm)
24
+ when Manifest
25
+ JSON.generate(wasm.manifest_data)
26
+ else
27
+ wasm
28
+ end
29
+
20
30
  code = FFI::MemoryPointer.new(:char, wasm.bytesize)
21
31
  errmsg = FFI::MemoryPointer.new(:pointer)
22
32
  code.put_bytes(0, wasm)
@@ -1,5 +1,5 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  module Extism
4
- VERSION = '1.0.0-rc.2'
4
+ VERSION = '1.0.0-rc.3'
5
5
  end
data/lib/extism/wasm.rb CHANGED
@@ -1,7 +1,13 @@
1
1
  module Extism
2
+ # Extism specific values for Wasm types. Useful when you need to describe
3
+ # something in pure wasm like host function signatures.
4
+ #
5
+ # @example
6
+ # register_import :hostfunc, [Extism::ValType::I32, Extism::ValType::F64], [Extism::ValType::I64]
2
7
  module ValType
3
8
  I32 = 0
4
9
  I64 = 1
10
+ PTR = 1
5
11
  F32 = 2
6
12
  F64 = 3
7
13
  V128 = 4
@@ -9,6 +15,7 @@ module Extism
9
15
  EXTERN_REF = 6
10
16
  end
11
17
 
18
+ # A raw Wasm value. Contains the type and the data
12
19
  class Val
13
20
  def initialize(ptr)
14
21
  @c_val = LibExtism::ExtismVal.new(ptr)
@@ -20,6 +27,8 @@ module Extism
20
27
  :i32
21
28
  when :I64
22
29
  :i64
30
+ when :PTR
31
+ :i64
23
32
  when :F32
24
33
  :f32
25
34
  when :F64
@@ -50,7 +59,10 @@ module Extism
50
59
  end
51
60
  end
52
61
 
53
- # Represents a host function
62
+ # Represents a host function. This is mostly for internal use and you should
63
+ # try to use HostEnvironment instead
64
+ #
65
+ # @see Extism::HostEnvironment
54
66
  class Function
55
67
  # Create a new host function
56
68
  #
@@ -81,6 +93,9 @@ module Extism
81
93
  returns = LibExtism.from_int_array(@returns)
82
94
  @_pointer = LibExtism.extism_function_new(@name, args, @params.length, returns, @returns.length, c_func, free,
83
95
  nil)
96
+ $FUNCTIONS[object_id] = { function: @_pointer}
97
+ ObjectSpace.define_finalizer(self, $FREE_FUNCTION)
98
+ return @_pointer
84
99
  end
85
100
 
86
101
  def c_func
data/lib/extism.rb CHANGED
@@ -1,38 +1,59 @@
1
- require 'ffi'
2
- require 'json'
3
- require_relative './extism/version'
4
- require_relative './extism/plugin'
5
- require_relative './extism/current_plugin'
6
- require_relative './extism/libextism'
7
- require_relative './extism/wasm'
8
- require_relative './extism/host_environment'
9
-
10
- module Extism
11
- class Error < StandardError
12
- end
13
-
14
- # Return the version of Extism
15
- #
16
- # @return [String] The version string of the Extism runtime
17
- def self.extism_version
18
- LibExtism.extism_version
19
- end
20
-
21
- # Set log file and level, this is a global configuration
22
- # @param name [String] The path to the logfile
23
- # @param level [String] The log level. One of {"debug", "error", "info", "trace" }
24
- def self.set_log_file(name, level = nil)
25
- LibExtism.extism_log_file(name, level)
26
- end
27
-
28
- $PLUGINS = {}
29
- $FREE_PLUGIN = proc { |ptr|
30
- x = $PLUGINS[ptr]
31
- unless x.nil?
32
- LibExtism.extism_plugin_free(x[:plugin])
33
- $PLUGINS.delete(ptr)
34
- end
35
- }
36
-
37
- Memory = Struct.new(:offset, :len)
38
- end
1
+ require 'ffi'
2
+ require 'json'
3
+ require_relative './extism/manifest'
4
+ require_relative './extism/version'
5
+ require_relative './extism/plugin'
6
+ require_relative './extism/current_plugin'
7
+ require_relative './extism/libextism'
8
+ require_relative './extism/wasm'
9
+ require_relative './extism/host_environment'
10
+
11
+ module Extism
12
+ class Error < StandardError
13
+ end
14
+
15
+ # Return the version of Extism
16
+ #
17
+ # @return [String] The version string of the Extism runtime
18
+ def self.extism_version
19
+ LibExtism.extism_version
20
+ end
21
+
22
+ # Set log file and level, this is a global configuration
23
+ # @param name [String] The path to the logfile
24
+ # @param level [String] The log level. One of {"debug", "error", "info", "trace" }
25
+ def self.set_log_file(name, level = nil)
26
+ LibExtism.extism_log_file(name, level)
27
+ end
28
+
29
+ $PLUGINS = {}
30
+ $FREE_PLUGIN = proc { |ptr|
31
+ x = $PLUGINS[ptr]
32
+ unless x.nil?
33
+ LibExtism.extism_plugin_free(x[:plugin])
34
+ $PLUGINS.delete(ptr)
35
+ end
36
+ }
37
+
38
+
39
+ $FUNCTIONS = {}
40
+ $FREE_FUNCTION = proc { |ptr|
41
+ x = $FUNCTIONS[ptr]
42
+ unless x.nil?
43
+ LibExtism.extism_function_free(x[:function])
44
+ $FUNCTIONS.delete(ptr)
45
+ end
46
+ }
47
+
48
+ # Represents a "block" of memory in Extism.
49
+ # This memory is in the communication buffer b/w the
50
+ # guest in the host and technically lives in host memory.
51
+ class Memory
52
+ attr_reader :offset, :len
53
+
54
+ def initialize(offset, len)
55
+ @offset = offset
56
+ @len = len
57
+ end
58
+ end
59
+ end
Binary file
Binary file
data/wasm/reflect.wasm CHANGED
Binary file
Binary file
metadata CHANGED
@@ -1,14 +1,14 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: extism
3
3
  version: !ruby/object:Gem::Version
4
- version: 1.0.0.pre.rc.2
4
+ version: 1.0.0.pre.rc.3
5
5
  platform: ruby
6
6
  authors:
7
7
  - zach
8
8
  autorequire:
9
9
  bindir: exe
10
10
  cert_chain: []
11
- date: 2023-09-29 00:00:00.000000000 Z
11
+ date: 2023-11-04 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: ffi
@@ -42,11 +42,13 @@ files:
42
42
  - lib/extism/current_plugin.rb
43
43
  - lib/extism/host_environment.rb
44
44
  - lib/extism/libextism.rb
45
+ - lib/extism/manifest.rb
45
46
  - lib/extism/plugin.rb
46
47
  - lib/extism/version.rb
47
48
  - lib/extism/wasm.rb
48
49
  - sig/extism.rbs
49
50
  - wasm/count_vowels.wasm
51
+ - wasm/count_vowels_kvstore.wasm
50
52
  - wasm/reflect.wasm
51
53
  - wasm/store_credit.wasm
52
54
  homepage: https://github.com/extism/extism