primrose 0.0.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 +7 -0
- data/LICENSE +21 -0
- data/README.md +136 -0
- data/lib/primrose/components/button.rb +28 -0
- data/lib/primrose/components/checkbox.rb +24 -0
- data/lib/primrose/components/navbar.rb +25 -0
- data/lib/primrose/components/text_field.rb +23 -0
- data/lib/primrose/helpers.rb +39 -0
- data/lib/primrose/observable.rb +59 -0
- data/lib/primrose/prim.rb +30 -0
- data/lib/primrose/rose.rb +90 -0
- data/lib/primrose/router.rb +17 -0
- data/lib/primrose/store.rb +23 -0
- data/lib/primrose/utils/deep_clone.rb +7 -0
- data/lib/primrose/version.rb +3 -0
- data/lib/primrose.rb +7 -0
- metadata +71 -0
checksums.yaml
ADDED
@@ -0,0 +1,7 @@
|
|
1
|
+
---
|
2
|
+
SHA256:
|
3
|
+
metadata.gz: 7bd154f7df76d53c3db2057c499b67edf417e621e0ff44c537382e9c78d946f6
|
4
|
+
data.tar.gz: b1c996a5c456ca5f0407189656d99352918812559139ab2a9b35a82b44ffbab3
|
5
|
+
SHA512:
|
6
|
+
metadata.gz: dd085723441f9d29c8713a20f9bb3767771c0e26d3377e6c360bdd0407902446d88462a4940e73c984d09e4aa6718903d3d4fdeb2ccb392a91a6d4c31df3b444
|
7
|
+
data.tar.gz: f124f56efebe490b8cab7d368f3187ffcff27ce80fba1030579131eb2097c18108ea7a9448fdfbc618c99c50877bb5a7d92f78a65eae008edbf39dc03f385573
|
data/LICENSE
ADDED
@@ -0,0 +1,21 @@
|
|
1
|
+
MIT License
|
2
|
+
|
3
|
+
Copyright (c) 2023 Daniel M. Matongo
|
4
|
+
|
5
|
+
Permission is hereby granted, free of charge, to any person obtaining a copy
|
6
|
+
of this software and associated documentation files (the "Software"), to deal
|
7
|
+
in the Software without restriction, including without limitation the rights
|
8
|
+
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
9
|
+
copies of the Software, and to permit persons to whom the Software is
|
10
|
+
furnished to do so, subject to the following conditions:
|
11
|
+
|
12
|
+
The above copyright notice and this permission notice shall be included in all
|
13
|
+
copies or substantial portions of the Software.
|
14
|
+
|
15
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
16
|
+
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
17
|
+
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
18
|
+
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
19
|
+
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
20
|
+
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
21
|
+
SOFTWARE.
|
data/README.md
ADDED
@@ -0,0 +1,136 @@
|
|
1
|
+
# Primrose Framework Documentation
|
2
|
+
|
3
|
+
## Overview
|
4
|
+
|
5
|
+
Primrose is a Ruby library designed for building component-based web applications. It provides a collection of modules and classes to manage the state, routing, rendering, and event handling for a modern web application. Primrose offers the following key features:
|
6
|
+
|
7
|
+
- State Management using `Primrose::Store`
|
8
|
+
- Component-based UI building blocks via `Primrose::Rose` subclasses
|
9
|
+
- Routing capabilities with `Primrose::Router`
|
10
|
+
- Observable state via `Primrose::Observable`
|
11
|
+
- Built-in helper methods and utility functions
|
12
|
+
- Template-based rendering with ERB
|
13
|
+
|
14
|
+
---
|
15
|
+
|
16
|
+
## Table of Contents
|
17
|
+
- [Installation](#installation)
|
18
|
+
- [Usage](#usage)
|
19
|
+
- [State Management](#state-management)
|
20
|
+
- [Routing](#routing)
|
21
|
+
- [Components](#components)
|
22
|
+
- [Event Handling](#event-handling)
|
23
|
+
- [Examples](#examples)
|
24
|
+
- [Contributing](#contributing)
|
25
|
+
- [License](#license)
|
26
|
+
|
27
|
+
---
|
28
|
+
|
29
|
+
## Installation
|
30
|
+
|
31
|
+
You can install the library by adding it to your `Gemfile`:
|
32
|
+
|
33
|
+
```ruby
|
34
|
+
gem 'primrose', '~> 0.0.1'
|
35
|
+
```
|
36
|
+
|
37
|
+
Then run `bundle install` to install the dependency.
|
38
|
+
|
39
|
+
---
|
40
|
+
|
41
|
+
## Usage
|
42
|
+
|
43
|
+
### State Management
|
44
|
+
|
45
|
+
```ruby
|
46
|
+
store = Primrose::Store.new({ counter: 0 })
|
47
|
+
|
48
|
+
store.dispatch({
|
49
|
+
type: 'INCREMENT',
|
50
|
+
updates: {
|
51
|
+
counter: -> (current_value) { current_value + 1 }
|
52
|
+
}
|
53
|
+
})
|
54
|
+
```
|
55
|
+
|
56
|
+
### Routing
|
57
|
+
|
58
|
+
```ruby
|
59
|
+
router = Primrose::Router.new
|
60
|
+
|
61
|
+
router.route('/home') { puts "You're at home" }
|
62
|
+
router.route('/about') { puts "About page" }
|
63
|
+
|
64
|
+
router.navigate('/home') # Outputs "You're at home"
|
65
|
+
```
|
66
|
+
|
67
|
+
### Components
|
68
|
+
|
69
|
+
Creating a custom component is easy:
|
70
|
+
|
71
|
+
```ruby
|
72
|
+
class MyComponent < Primrose::Rose
|
73
|
+
def render
|
74
|
+
"Hello, world!"
|
75
|
+
end
|
76
|
+
end
|
77
|
+
```
|
78
|
+
|
79
|
+
### Event Handling
|
80
|
+
|
81
|
+
You can attach event handlers to your components:
|
82
|
+
|
83
|
+
```ruby
|
84
|
+
component = MyComponent.new
|
85
|
+
component.on(:click) { puts 'Component clicked!' }
|
86
|
+
component.trigger(:click) # Outputs "Component clicked!"
|
87
|
+
```
|
88
|
+
|
89
|
+
---
|
90
|
+
|
91
|
+
## Examples
|
92
|
+
|
93
|
+
Below are some example templates to showcase how components can be structured.
|
94
|
+
|
95
|
+
- Button Component:
|
96
|
+
|
97
|
+
```erb
|
98
|
+
<button onclick="<%= @action %>">
|
99
|
+
<%= @label %>
|
100
|
+
</button>
|
101
|
+
```
|
102
|
+
|
103
|
+
- Text Field Component:
|
104
|
+
|
105
|
+
```erb
|
106
|
+
<input type="text" placeholder="<%= @placeholder %>">
|
107
|
+
```
|
108
|
+
|
109
|
+
- Checkbox Component:
|
110
|
+
|
111
|
+
```erb
|
112
|
+
<div class="checkbox">
|
113
|
+
<input type="checkbox">
|
114
|
+
<label><%= @label %></label>
|
115
|
+
</div>
|
116
|
+
```
|
117
|
+
|
118
|
+
---
|
119
|
+
|
120
|
+
## Contributing
|
121
|
+
|
122
|
+
Pull requests are welcome. For major changes, please open an issue first to discuss what you would like to change.
|
123
|
+
|
124
|
+
---
|
125
|
+
|
126
|
+
## License
|
127
|
+
|
128
|
+
This project is licensed under the MIT License - see the [LICENSE.md](LICENSE.md) file for details.
|
129
|
+
|
130
|
+
---
|
131
|
+
|
132
|
+
For more detailed usage and full API documentation, please refer to the inline code comments and accompanying documentation.
|
133
|
+
|
134
|
+
Feel free to raise an issue for any bugs, feature requests, or questions.
|
135
|
+
|
136
|
+
**Note**: This documentation assumes that you are familiar with Ruby and ERB (Embedded Ruby).
|
@@ -0,0 +1,28 @@
|
|
1
|
+
require_relative '../prim'
|
2
|
+
|
3
|
+
module Primrose
|
4
|
+
module Components
|
5
|
+
class Button < Rose
|
6
|
+
def initialize(label:, action:, style: nil, disabled: false, loading: false)
|
7
|
+
@label = label
|
8
|
+
@action = action
|
9
|
+
@style = style
|
10
|
+
@disabled = disabled
|
11
|
+
@loading = loading
|
12
|
+
super()
|
13
|
+
end
|
14
|
+
|
15
|
+
def render
|
16
|
+
Prim.render('templates/components/button.prim.erb', self)
|
17
|
+
end
|
18
|
+
|
19
|
+
def set_disabled(value)
|
20
|
+
@state.alter({ disabled: value })
|
21
|
+
end
|
22
|
+
|
23
|
+
def set_loading(value)
|
24
|
+
@state.alter({ loading: value })
|
25
|
+
end
|
26
|
+
end
|
27
|
+
end
|
28
|
+
end
|
@@ -0,0 +1,24 @@
|
|
1
|
+
require_relative '../prim'
|
2
|
+
|
3
|
+
module Primrose
|
4
|
+
module Components
|
5
|
+
class Checkbox < Rose
|
6
|
+
def initialize(label:, checked: false, id: nil, name: nil, js: nil)
|
7
|
+
@label = label
|
8
|
+
@checked = checked
|
9
|
+
@id = id || label.downcase.gsub(/\s+/, "_") # Use label as id if id is not provided
|
10
|
+
@name = name || @id # Use id as name if name is not provided
|
11
|
+
@js = js
|
12
|
+
super()
|
13
|
+
end
|
14
|
+
|
15
|
+
def render
|
16
|
+
Prim.render('templates/partials/_checkbox.prim.erb', self)
|
17
|
+
end
|
18
|
+
|
19
|
+
def set_checked(value)
|
20
|
+
@state.alter({ checked: value })
|
21
|
+
end
|
22
|
+
end
|
23
|
+
end
|
24
|
+
end
|
@@ -0,0 +1,25 @@
|
|
1
|
+
require_relative '../prim'
|
2
|
+
|
3
|
+
module Primrose
|
4
|
+
module Components
|
5
|
+
class Navbar < Rose
|
6
|
+
def initialize(*args, links:, active_link: nil, sticky: false, brand: nil)
|
7
|
+
# puts "Non-keyword args: #{args.inspect}"
|
8
|
+
# puts "Initializing Navbar with #{links.inspect}, #{active_link}, #{sticky}, #{brand}"
|
9
|
+
@links = links
|
10
|
+
@active_link = active_link
|
11
|
+
@sticky = sticky
|
12
|
+
@brand = brand
|
13
|
+
super()
|
14
|
+
end
|
15
|
+
|
16
|
+
def render
|
17
|
+
Prim.render('templates/components/navbar.prim.erb', self)
|
18
|
+
end
|
19
|
+
|
20
|
+
def set_active_link(new_active_link)
|
21
|
+
@state.alter({ active_link: new_active_link })
|
22
|
+
end
|
23
|
+
end
|
24
|
+
end
|
25
|
+
end
|
@@ -0,0 +1,23 @@
|
|
1
|
+
require_relative '../prim'
|
2
|
+
|
3
|
+
module Primrose
|
4
|
+
module Components
|
5
|
+
class TextField < Rose
|
6
|
+
def initialize(placeholder:, name: nil, label: nil, min_length: nil, max_length: nil, read_only: false, default_value: nil, style_class: nil)
|
7
|
+
@placeholder = placeholder
|
8
|
+
@name = name
|
9
|
+
@label = label
|
10
|
+
@min_length = min_length
|
11
|
+
@max_length = max_length
|
12
|
+
@read_only = read_only
|
13
|
+
@default_value = default_value
|
14
|
+
@style_class = style_class
|
15
|
+
super()
|
16
|
+
end
|
17
|
+
|
18
|
+
def render
|
19
|
+
Prim.render('templates/components/text_field.prim.erb', self)
|
20
|
+
end
|
21
|
+
end
|
22
|
+
end
|
23
|
+
end
|
@@ -0,0 +1,39 @@
|
|
1
|
+
require_relative 'components/button'
|
2
|
+
require_relative 'components/navbar'
|
3
|
+
require_relative 'components/text_field'
|
4
|
+
require_relative 'components/checkbox'
|
5
|
+
|
6
|
+
module Primrose
|
7
|
+
module Helpers
|
8
|
+
# Load a component dynamically based on the name
|
9
|
+
def component(name, **args, &block)
|
10
|
+
klass = Object.const_get("Primrose::Components::#{name}")
|
11
|
+
begin
|
12
|
+
instance = klass.new(**args)
|
13
|
+
rescue ArgumentError => e
|
14
|
+
puts "Error while initializing #{klass}: #{e.message}"
|
15
|
+
puts "Backtrace: #{e.backtrace.join("\n")}"
|
16
|
+
raise e
|
17
|
+
end
|
18
|
+
block&.call(instance)
|
19
|
+
instance.render
|
20
|
+
end
|
21
|
+
|
22
|
+
# Specific helpers
|
23
|
+
def button(label:, action:, **opts)
|
24
|
+
component('Button', label: label, action: action, **opts)
|
25
|
+
end
|
26
|
+
|
27
|
+
def navbar(links:, **opts)
|
28
|
+
component('Navbar', links: links, **opts)
|
29
|
+
end
|
30
|
+
|
31
|
+
def text_field(placeholder:, **opts)
|
32
|
+
component('TextField', placeholder: placeholder, **opts)
|
33
|
+
end
|
34
|
+
|
35
|
+
def checkbox(label:, checked: false, **opts)
|
36
|
+
component('Checkbox', label: label, checked: checked, **opts)
|
37
|
+
end
|
38
|
+
end
|
39
|
+
end
|
@@ -0,0 +1,59 @@
|
|
1
|
+
require_relative 'utils/deep_clone'
|
2
|
+
|
3
|
+
module Primrose
|
4
|
+
class Observable
|
5
|
+
def initialize(value)
|
6
|
+
@value = value
|
7
|
+
@listeners = []
|
8
|
+
@previous_value = deep_clone(value)
|
9
|
+
@events = {}
|
10
|
+
end
|
11
|
+
|
12
|
+
def follow(&block)
|
13
|
+
@listeners << block
|
14
|
+
block.call(@value, @previous_value)
|
15
|
+
end
|
16
|
+
|
17
|
+
def on(event_name, &block)
|
18
|
+
(@events[event_name] ||= []) << block
|
19
|
+
end
|
20
|
+
|
21
|
+
def trigger(event_name, *args)
|
22
|
+
(@events[event_name] || []).each do |listener|
|
23
|
+
listener.call(*args)
|
24
|
+
end
|
25
|
+
end
|
26
|
+
|
27
|
+
# State Persistence
|
28
|
+
def save_state(key)
|
29
|
+
File.write("#{key}.json", @value.to_json)
|
30
|
+
end
|
31
|
+
|
32
|
+
def load_state(key)
|
33
|
+
if File.exist?("#{key}.json")
|
34
|
+
@value = JSON.parse(File.read("#{key}.json"))
|
35
|
+
notify_observers
|
36
|
+
end
|
37
|
+
end
|
38
|
+
|
39
|
+
def alter(new_value)
|
40
|
+
@previous_value = deep_clone(@value)
|
41
|
+
@value = new_value
|
42
|
+
notify_observers
|
43
|
+
end
|
44
|
+
|
45
|
+
def value
|
46
|
+
@value
|
47
|
+
end
|
48
|
+
|
49
|
+
private
|
50
|
+
|
51
|
+
def deep_clone(object)
|
52
|
+
Primrose::Utils.deep_clone(object)
|
53
|
+
end
|
54
|
+
|
55
|
+
def notify_observers
|
56
|
+
@listeners.each { |listener| listener.call(@value, @previous_value) }
|
57
|
+
end
|
58
|
+
end
|
59
|
+
end
|
@@ -0,0 +1,30 @@
|
|
1
|
+
require 'erb'
|
2
|
+
require 'logger'
|
3
|
+
|
4
|
+
module Primrose
|
5
|
+
class Prim
|
6
|
+
@template_cache = {}
|
7
|
+
@logger = Logger.new(STDOUT)
|
8
|
+
|
9
|
+
def self.render(template_path, context)
|
10
|
+
template = read_template(template_path)
|
11
|
+
erb = ERB.new(template)
|
12
|
+
rendered = erb.result(context.get_binding)
|
13
|
+
|
14
|
+
# replace partial tags with actual content
|
15
|
+
rendered.gsub(/<%= partial "(.*?)" %>/) do
|
16
|
+
partial_path = File.join('templates/partials', "#{$1}.prim.erb")
|
17
|
+
render(partial_path, context)
|
18
|
+
end
|
19
|
+
end
|
20
|
+
|
21
|
+
private
|
22
|
+
|
23
|
+
def self.read_template(template_path)
|
24
|
+
@template_cache[template_path] ||= File.read(template_path)
|
25
|
+
rescue => e
|
26
|
+
@logger.error("Could not read template: #{e.message}")
|
27
|
+
raise
|
28
|
+
end
|
29
|
+
end
|
30
|
+
end
|
@@ -0,0 +1,90 @@
|
|
1
|
+
module Primrose
|
2
|
+
class Rose
|
3
|
+
attr_reader :state, :event_handlers, :children
|
4
|
+
|
5
|
+
def initialize
|
6
|
+
@state = Observable.new({})
|
7
|
+
@event_handlers = {}
|
8
|
+
@children = []
|
9
|
+
lifecycle(:initialize)
|
10
|
+
end
|
11
|
+
|
12
|
+
def lifecycle(method)
|
13
|
+
send(method) if respond_to?(method)
|
14
|
+
end
|
15
|
+
|
16
|
+
def render
|
17
|
+
begin
|
18
|
+
lifecycle(:before_render)
|
19
|
+
lifecycle(:after_render)
|
20
|
+
rescue StandardError => e
|
21
|
+
handle_error(e)
|
22
|
+
end
|
23
|
+
end
|
24
|
+
|
25
|
+
def on(event, &block)
|
26
|
+
@event_handlers[event] ||= []
|
27
|
+
@event_handlers[event] << block
|
28
|
+
end
|
29
|
+
|
30
|
+
def trigger(event, *args)
|
31
|
+
return unless @event_handlers[event]
|
32
|
+
|
33
|
+
@event_handlers[event].each do |handler|
|
34
|
+
handler.call(*args)
|
35
|
+
end
|
36
|
+
end
|
37
|
+
|
38
|
+
def add_child(child)
|
39
|
+
@children << child
|
40
|
+
end
|
41
|
+
|
42
|
+
def render_children
|
43
|
+
@children.map { |child| child.render }.join("\n")
|
44
|
+
end
|
45
|
+
|
46
|
+
# Error Handling
|
47
|
+
def handle_error(error)
|
48
|
+
puts "An error occurred: #{error.message}"
|
49
|
+
# Log to a file or send to monitoring service
|
50
|
+
# You can add more specific logic here based on the type of error
|
51
|
+
error.backtrace.each { |line| puts line }
|
52
|
+
end
|
53
|
+
|
54
|
+
# Setup Hook
|
55
|
+
def setup
|
56
|
+
# Placeholder for logic to run only once during the object's lifetime,
|
57
|
+
# prior to the `initialize` lifecycle method
|
58
|
+
end
|
59
|
+
|
60
|
+
# Additional Lifecycle Hooks
|
61
|
+
def before_mount
|
62
|
+
# Placeholder for logic to run before the component is added to the DOM
|
63
|
+
end
|
64
|
+
|
65
|
+
def after_mount
|
66
|
+
# Placeholder for logic to run after the component is added to the DOM
|
67
|
+
end
|
68
|
+
|
69
|
+
def before_unmount
|
70
|
+
# Placeholder for logic to run before the component is removed from the DOM
|
71
|
+
end
|
72
|
+
|
73
|
+
def after_unmount
|
74
|
+
# Placeholder for logic to run after the component is removed from the DOM
|
75
|
+
end
|
76
|
+
|
77
|
+
# Existing hooks
|
78
|
+
def before_render
|
79
|
+
# Placeholder for logic to run immediately before the render method
|
80
|
+
end
|
81
|
+
|
82
|
+
def after_render
|
83
|
+
# Placeholder for logic to run immediately after the render method
|
84
|
+
end
|
85
|
+
|
86
|
+
def get_binding
|
87
|
+
binding
|
88
|
+
end
|
89
|
+
end
|
90
|
+
end
|
@@ -0,0 +1,23 @@
|
|
1
|
+
module Primrose
|
2
|
+
class Store
|
3
|
+
attr_reader :state
|
4
|
+
|
5
|
+
def initialize(initial_state = {})
|
6
|
+
@state = Observable.new(initial_state)
|
7
|
+
end
|
8
|
+
|
9
|
+
def dispatch(action)
|
10
|
+
new_state = @state.value.dup # Start with a copy of the current state
|
11
|
+
puts "Dispatching action: #{action[:type]}"
|
12
|
+
|
13
|
+
action[:updates].each do |key, value|
|
14
|
+
puts "Updating #{key} with #{value}"
|
15
|
+
new_state[key] = value.call(new_state[key]) # Apply update function to corresponding value in state
|
16
|
+
puts "New value: #{new_state[key]}"
|
17
|
+
end
|
18
|
+
|
19
|
+
@state.alter(new_state) # Update state
|
20
|
+
puts "New state: #{@state}"
|
21
|
+
end
|
22
|
+
end
|
23
|
+
end
|
data/lib/primrose.rb
ADDED
metadata
ADDED
@@ -0,0 +1,71 @@
|
|
1
|
+
--- !ruby/object:Gem::Specification
|
2
|
+
name: primrose
|
3
|
+
version: !ruby/object:Gem::Version
|
4
|
+
version: 0.0.1
|
5
|
+
platform: ruby
|
6
|
+
authors:
|
7
|
+
- Daniel M. Matongo
|
8
|
+
autorequire:
|
9
|
+
bindir: bin
|
10
|
+
cert_chain: []
|
11
|
+
date: 2023-10-18 00:00:00.000000000 Z
|
12
|
+
dependencies:
|
13
|
+
- !ruby/object:Gem::Dependency
|
14
|
+
name: erb
|
15
|
+
requirement: !ruby/object:Gem::Requirement
|
16
|
+
requirements:
|
17
|
+
- - "~>"
|
18
|
+
- !ruby/object:Gem::Version
|
19
|
+
version: '4.0'
|
20
|
+
type: :runtime
|
21
|
+
prerelease: false
|
22
|
+
version_requirements: !ruby/object:Gem::Requirement
|
23
|
+
requirements:
|
24
|
+
- - "~>"
|
25
|
+
- !ruby/object:Gem::Version
|
26
|
+
version: '4.0'
|
27
|
+
description: A gem for observable data structures and components
|
28
|
+
email: mmatongo_@hotmail.com
|
29
|
+
executables: []
|
30
|
+
extensions: []
|
31
|
+
extra_rdoc_files: []
|
32
|
+
files:
|
33
|
+
- LICENSE
|
34
|
+
- README.md
|
35
|
+
- lib/primrose.rb
|
36
|
+
- lib/primrose/components/button.rb
|
37
|
+
- lib/primrose/components/checkbox.rb
|
38
|
+
- lib/primrose/components/navbar.rb
|
39
|
+
- lib/primrose/components/text_field.rb
|
40
|
+
- lib/primrose/helpers.rb
|
41
|
+
- lib/primrose/observable.rb
|
42
|
+
- lib/primrose/prim.rb
|
43
|
+
- lib/primrose/rose.rb
|
44
|
+
- lib/primrose/router.rb
|
45
|
+
- lib/primrose/store.rb
|
46
|
+
- lib/primrose/utils/deep_clone.rb
|
47
|
+
- lib/primrose/version.rb
|
48
|
+
homepage: https://github.com/mmatongo/primrose.rb
|
49
|
+
licenses:
|
50
|
+
- MIT
|
51
|
+
metadata: {}
|
52
|
+
post_install_message:
|
53
|
+
rdoc_options: []
|
54
|
+
require_paths:
|
55
|
+
- lib
|
56
|
+
required_ruby_version: !ruby/object:Gem::Requirement
|
57
|
+
requirements:
|
58
|
+
- - ">="
|
59
|
+
- !ruby/object:Gem::Version
|
60
|
+
version: '0'
|
61
|
+
required_rubygems_version: !ruby/object:Gem::Requirement
|
62
|
+
requirements:
|
63
|
+
- - ">="
|
64
|
+
- !ruby/object:Gem::Version
|
65
|
+
version: '0'
|
66
|
+
requirements: []
|
67
|
+
rubygems_version: 3.4.21
|
68
|
+
signing_key:
|
69
|
+
specification_version: 4
|
70
|
+
summary: A gem for observable data structures and components
|
71
|
+
test_files: []
|