primrose 0.0.1
Sign up to get free protection for your applications and to get access to all the features.
- 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: []
|