plasma-mcp 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/.rubocop.yml +16 -0
- data/.ruby-version +1 -0
- data/CHANGELOG.md +26 -0
- data/LICENSE +21 -0
- data/README.md +207 -0
- data/Rakefile +16 -0
- data/docs/ROADMAP.md +46 -0
- data/exe/plasma +8 -0
- data/lib/plasma/application.rb +193 -0
- data/lib/plasma/auth.rb +82 -0
- data/lib/plasma/auth_server.rb +45 -0
- data/lib/plasma/cli.rb +93 -0
- data/lib/plasma/component_generator.rb +111 -0
- data/lib/plasma/generator.rb +132 -0
- data/lib/plasma/loader.rb +32 -0
- data/lib/plasma/prompt.rb +10 -0
- data/lib/plasma/resource.rb +39 -0
- data/lib/plasma/server.rb +47 -0
- data/lib/plasma/storage/application_record.rb +10 -0
- data/lib/plasma/storage/record.rb +16 -0
- data/lib/plasma/storage/variable.rb +53 -0
- data/lib/plasma/storage.rb +101 -0
- data/lib/plasma/templates/.dockerignore.erb +40 -0
- data/lib/plasma/templates/.env.erb +2 -0
- data/lib/plasma/templates/.github/workflows/docker-build.yml.erb +52 -0
- data/lib/plasma/templates/.gitignore.erb +40 -0
- data/lib/plasma/templates/.ruby-version.erb +1 -0
- data/lib/plasma/templates/Dockerfile.erb +18 -0
- data/lib/plasma/templates/Gemfile.erb +6 -0
- data/lib/plasma/templates/README.md.erb +228 -0
- data/lib/plasma/templates/app/prompts/example_prompt.rb.erb +30 -0
- data/lib/plasma/templates/app/resources/example_resource.rb.erb +36 -0
- data/lib/plasma/templates/app/tools/example_tool.rb.erb +35 -0
- data/lib/plasma/templates/app/variables/example_variable.erb +20 -0
- data/lib/plasma/templates/config/application.rb.erb +24 -0
- data/lib/plasma/templates/config/boot.rb.erb +12 -0
- data/lib/plasma/templates/config/initializers/example.rb.erb +7 -0
- data/lib/plasma/templates/lib/version.rb.erb +5 -0
- data/lib/plasma/tool.rb +131 -0
- data/lib/plasma/version.rb +5 -0
- data/lib/plasma.rb +19 -0
- data/sig/plasma.rbs +4 -0
- metadata +257 -0
data/lib/plasma/cli.rb
ADDED
@@ -0,0 +1,93 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
require "thor"
|
4
|
+
require "fileutils"
|
5
|
+
require "zeitwerk"
|
6
|
+
|
7
|
+
module Plasma
|
8
|
+
# CLI interface for the plasma-mcp gem
|
9
|
+
class CLI < Thor
|
10
|
+
desc "new NAME", "Create a new MCP server project"
|
11
|
+
method_option :skip_git, type: :boolean, default: false, desc: "Skip Git initialization"
|
12
|
+
def new(name)
|
13
|
+
generator = Generator.new(name, options)
|
14
|
+
generator.generate
|
15
|
+
|
16
|
+
return if options[:skip_git]
|
17
|
+
|
18
|
+
Dir.chdir(name) do
|
19
|
+
system("git init .")
|
20
|
+
end
|
21
|
+
end
|
22
|
+
|
23
|
+
desc "server [/path/to/your/plasma/project]", "Launch your MCP server from the specified project path"
|
24
|
+
def server(path = nil)
|
25
|
+
# Change to the specified path if provided
|
26
|
+
Dir.chdir(path) if path
|
27
|
+
|
28
|
+
# Start the server
|
29
|
+
server = Server.new(options)
|
30
|
+
server.start
|
31
|
+
end
|
32
|
+
|
33
|
+
# Alias for server
|
34
|
+
map "s" => :server
|
35
|
+
|
36
|
+
desc "auth", "Launch the local authentication beacon"
|
37
|
+
method_option :port, type: :numeric, desc: "Port to run the auth server on"
|
38
|
+
method_option :host, type: :string, desc: "Host to bind the auth server to"
|
39
|
+
def auth
|
40
|
+
puts "Engaging local authentication system..."
|
41
|
+
|
42
|
+
# Start the auth server with options
|
43
|
+
auth = Auth.new(options)
|
44
|
+
auth.start
|
45
|
+
end
|
46
|
+
|
47
|
+
desc "console", "Start an interactive console with your PLASMA application loaded"
|
48
|
+
def console
|
49
|
+
require "debug"
|
50
|
+
|
51
|
+
# Load the application so we can use it in the console if we need to
|
52
|
+
require_relative "loader"
|
53
|
+
_application = Plasma::Loader.load_project
|
54
|
+
|
55
|
+
# Start an interactive console
|
56
|
+
binding.irb # rubocop:disable Lint/Debugger
|
57
|
+
end
|
58
|
+
|
59
|
+
desc "version", "Show PLASMA version"
|
60
|
+
def version
|
61
|
+
puts "PLASMA version #{Plasma::VERSION}"
|
62
|
+
end
|
63
|
+
|
64
|
+
# Generator commands
|
65
|
+
desc "generate TYPE NAME [PARAMETERS]", "Generate a new component (tool, prompt, resource, or variable)"
|
66
|
+
def generate(type, name, *parameters)
|
67
|
+
# Parse parameters into a hash
|
68
|
+
params = {}
|
69
|
+
parameters.each do |param|
|
70
|
+
key, value = param.split(":")
|
71
|
+
params[key] = value if key && value
|
72
|
+
end
|
73
|
+
|
74
|
+
# Create the generator
|
75
|
+
generator = ComponentGenerator.new(type, name, params)
|
76
|
+
generator.generate
|
77
|
+
end
|
78
|
+
|
79
|
+
# Alias for generate
|
80
|
+
map "g" => :generate
|
81
|
+
|
82
|
+
private
|
83
|
+
|
84
|
+
def plasma_project?
|
85
|
+
File.exist?("config/application.rb") &&
|
86
|
+
File.exist?("config/boot.rb") &&
|
87
|
+
Dir.exist?("app/prompts") &&
|
88
|
+
Dir.exist?("app/resources") &&
|
89
|
+
Dir.exist?("app/tools") &&
|
90
|
+
Dir.exist?("app/variables")
|
91
|
+
end
|
92
|
+
end
|
93
|
+
end
|
@@ -0,0 +1,111 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
require "fileutils"
|
4
|
+
require "erb"
|
5
|
+
require "active_support/inflector"
|
6
|
+
require "json"
|
7
|
+
|
8
|
+
module Plasma
|
9
|
+
# Handles generating individual MCP components (tools, prompts, resources, variables)
|
10
|
+
class ComponentGenerator
|
11
|
+
attr_reader :component_type, :name, :params
|
12
|
+
|
13
|
+
def initialize(component_type, name, params = {})
|
14
|
+
@component_type = component_type
|
15
|
+
@name = name
|
16
|
+
@params = params
|
17
|
+
@template_dir = File.expand_path("templates", __dir__)
|
18
|
+
puts "Template directory: #{@template_dir}"
|
19
|
+
end
|
20
|
+
|
21
|
+
def generate
|
22
|
+
# Check if we're in a PLASMA project directory
|
23
|
+
unless plasma_project?
|
24
|
+
puts "Error: Not a PLASMA project directory. Please run this command from the root of a PLASMA project."
|
25
|
+
return false
|
26
|
+
end
|
27
|
+
|
28
|
+
create_component_file
|
29
|
+
true
|
30
|
+
end
|
31
|
+
|
32
|
+
private
|
33
|
+
|
34
|
+
def plasma_project?
|
35
|
+
File.exist?("config/application.rb") &&
|
36
|
+
File.exist?("config/boot.rb") &&
|
37
|
+
Dir.exist?("app/prompts") &&
|
38
|
+
Dir.exist?("app/resources") &&
|
39
|
+
Dir.exist?("app/tools") &&
|
40
|
+
Dir.exist?("app/variables")
|
41
|
+
end
|
42
|
+
|
43
|
+
def create_component_file
|
44
|
+
template_path = find_template_path
|
45
|
+
content = render_template(template_path)
|
46
|
+
write_component_file(content)
|
47
|
+
end
|
48
|
+
|
49
|
+
def find_template_path
|
50
|
+
path = File.join(@template_dir, "app", "#{@component_type}s", "example_#{@component_type}.rb.erb")
|
51
|
+
raise "Template not found at: #{path}" unless File.exist?(path)
|
52
|
+
|
53
|
+
path
|
54
|
+
end
|
55
|
+
|
56
|
+
def render_template(template_path)
|
57
|
+
template = ERB.new(File.read(template_path), trim_mode: "-")
|
58
|
+
template.result(binding)
|
59
|
+
end
|
60
|
+
|
61
|
+
def write_component_file(content)
|
62
|
+
component_dir = File.join("app", "#{@component_type}s")
|
63
|
+
FileUtils.mkdir_p(component_dir)
|
64
|
+
|
65
|
+
file_path = File.join(component_dir, "#{@name.downcase}_#{@component_type}.rb")
|
66
|
+
File.write(file_path, content)
|
67
|
+
end
|
68
|
+
|
69
|
+
def module_name
|
70
|
+
# Get the application module name from the current directory
|
71
|
+
File.basename(Dir.pwd).gsub("-", "_").camelize
|
72
|
+
end
|
73
|
+
|
74
|
+
def parameter_schema
|
75
|
+
return {} if params.empty?
|
76
|
+
|
77
|
+
schema = {
|
78
|
+
type: "object",
|
79
|
+
properties: params.transform_values { |type| { type: type } },
|
80
|
+
required: params.keys
|
81
|
+
}
|
82
|
+
|
83
|
+
JSON.pretty_generate(schema)
|
84
|
+
end
|
85
|
+
|
86
|
+
# Convert the name to the appropriate format for the file
|
87
|
+
def file_name
|
88
|
+
# Convert camelCase to snake_case and append _tool
|
89
|
+
"#{@name.underscore}_#{@component_type}"
|
90
|
+
end
|
91
|
+
|
92
|
+
# Convert the name to kebab-case for the metadata
|
93
|
+
def kebab_name
|
94
|
+
@name.underscore.gsub("_", "-")
|
95
|
+
end
|
96
|
+
|
97
|
+
def component_class_name
|
98
|
+
@name.split("_").map(&:capitalize).join
|
99
|
+
end
|
100
|
+
|
101
|
+
def component_name
|
102
|
+
@name.downcase
|
103
|
+
end
|
104
|
+
|
105
|
+
def parameter_definitions
|
106
|
+
@params.map do |name, type|
|
107
|
+
"#{name}: { type: \"#{type}\" }"
|
108
|
+
end.join(",\n ")
|
109
|
+
end
|
110
|
+
end
|
111
|
+
end
|
@@ -0,0 +1,132 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
require "fileutils"
|
4
|
+
require "erb"
|
5
|
+
require "active_support/inflector"
|
6
|
+
|
7
|
+
module Plasma
|
8
|
+
# Handles generating new MCP server projects from templates
|
9
|
+
class Generator
|
10
|
+
attr_reader :name, :options
|
11
|
+
|
12
|
+
def initialize(name, options = {})
|
13
|
+
@name = name
|
14
|
+
@options = options
|
15
|
+
end
|
16
|
+
|
17
|
+
def generate
|
18
|
+
create_project_directory
|
19
|
+
create_directory_structure
|
20
|
+
generate_files_from_templates
|
21
|
+
copy_static_files
|
22
|
+
move_version_file
|
23
|
+
end
|
24
|
+
|
25
|
+
private
|
26
|
+
|
27
|
+
def create_project_directory
|
28
|
+
FileUtils.mkdir_p(name)
|
29
|
+
end
|
30
|
+
|
31
|
+
def create_directory_structure
|
32
|
+
dirs = %w[app/prompts app/resources app/tools app/variables app/records
|
33
|
+
config/initializers lib .github/workflows]
|
34
|
+
|
35
|
+
dirs.each do |dir|
|
36
|
+
dir_path = File.join(name, dir)
|
37
|
+
FileUtils.mkdir_p(dir_path)
|
38
|
+
FileUtils.touch(File.join(dir_path, ".gitkeep"))
|
39
|
+
end
|
40
|
+
end
|
41
|
+
|
42
|
+
def templates_dir
|
43
|
+
File.expand_path("templates", __dir__)
|
44
|
+
end
|
45
|
+
|
46
|
+
def move_version_file
|
47
|
+
# Create the namespace directory
|
48
|
+
namespace_dir = File.join(name, "lib", name.underscore)
|
49
|
+
FileUtils.mkdir_p(namespace_dir)
|
50
|
+
|
51
|
+
# Move the version file to the correct location
|
52
|
+
version_file = File.join(name, "lib/version.rb")
|
53
|
+
new_version_file = File.join(namespace_dir, "version.rb")
|
54
|
+
return unless File.exist?(version_file)
|
55
|
+
|
56
|
+
FileUtils.mv(version_file, new_version_file)
|
57
|
+
end
|
58
|
+
|
59
|
+
# rubocop:disable Metrics/AbcSize, Metrics/MethodLength
|
60
|
+
def generate_files_from_templates
|
61
|
+
# Exclude example component templates and include dot files
|
62
|
+
template_files = Dir.glob(File.join(templates_dir, "**", "*.erb"), File::FNM_DOTMATCH).reject do |file|
|
63
|
+
result = file.include?("/app/") && file.include?("example_")
|
64
|
+
result
|
65
|
+
end
|
66
|
+
|
67
|
+
template_files.each do |template_path|
|
68
|
+
# Get relative path from templates directory
|
69
|
+
relative_path = template_path.sub("#{templates_dir}/", "")
|
70
|
+
# Remove .erb extension
|
71
|
+
output_path = relative_path.sub(/\.erb$/, "")
|
72
|
+
# Replace placeholder with actual project name
|
73
|
+
output_path = output_path.gsub("PROJECT_NAME", name)
|
74
|
+
|
75
|
+
# Create full path for destination
|
76
|
+
dest_path = File.join(name, output_path)
|
77
|
+
|
78
|
+
# Ensure directory exists
|
79
|
+
FileUtils.mkdir_p(File.dirname(dest_path))
|
80
|
+
|
81
|
+
# Render the template
|
82
|
+
template = ERB.new(File.read(template_path))
|
83
|
+
result = template.result(binding)
|
84
|
+
|
85
|
+
# Write file
|
86
|
+
File.write(dest_path, result)
|
87
|
+
end
|
88
|
+
end
|
89
|
+
# rubocop:enable Metrics/AbcSize, Metrics/MethodLength
|
90
|
+
|
91
|
+
# rubocop:disable Metrics/AbcSize
|
92
|
+
def copy_static_files
|
93
|
+
static_files = Dir.glob(File.join(templates_dir, "**", "*"), File::FNM_DOTMATCH).reject do |file|
|
94
|
+
File.directory?(file) || file.end_with?(".erb")
|
95
|
+
end
|
96
|
+
|
97
|
+
static_files.each do |file_path|
|
98
|
+
relative_path = file_path.sub("#{templates_dir}/", "")
|
99
|
+
dest_path = File.join(name, relative_path.gsub("PROJECT_NAME", name))
|
100
|
+
|
101
|
+
FileUtils.mkdir_p(File.dirname(dest_path))
|
102
|
+
FileUtils.cp(file_path, dest_path)
|
103
|
+
end
|
104
|
+
end
|
105
|
+
# rubocop:enable Metrics/AbcSize
|
106
|
+
|
107
|
+
def module_name
|
108
|
+
name.gsub("-", "_").camelize
|
109
|
+
end
|
110
|
+
end
|
111
|
+
end
|
112
|
+
|
113
|
+
# Add extension methods for string transformations in ERB templates
|
114
|
+
class String
|
115
|
+
unless method_defined?(:camelize)
|
116
|
+
def camelize
|
117
|
+
ActiveSupport::Inflector.camelize(self)
|
118
|
+
end
|
119
|
+
end
|
120
|
+
|
121
|
+
unless method_defined?(:underscore)
|
122
|
+
def underscore
|
123
|
+
ActiveSupport::Inflector.underscore(self)
|
124
|
+
end
|
125
|
+
end
|
126
|
+
|
127
|
+
unless method_defined?(:dasherize)
|
128
|
+
def dasherize
|
129
|
+
ActiveSupport::Inflector.dasherize(self)
|
130
|
+
end
|
131
|
+
end
|
132
|
+
end
|
@@ -0,0 +1,32 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module Plasma
|
4
|
+
# Handles loading PLASMA projects
|
5
|
+
class Loader
|
6
|
+
def self.load_project
|
7
|
+
# Load the application boot file
|
8
|
+
require_relative File.join(Dir.pwd, "config/boot")
|
9
|
+
|
10
|
+
# Determine the application class name
|
11
|
+
app_name = File.basename(Dir.pwd).gsub("-", "_").camelize
|
12
|
+
|
13
|
+
# Load the application
|
14
|
+
require_relative File.join(Dir.pwd, "config/application")
|
15
|
+
|
16
|
+
# Get the application module and include it in the top-level scope
|
17
|
+
app_module = Object.const_get(app_name)
|
18
|
+
Object.include(app_module)
|
19
|
+
|
20
|
+
# Return the application class
|
21
|
+
Object.const_get("#{app_name}::Application")
|
22
|
+
rescue LoadError, NameError => e
|
23
|
+
handle_load_error(e)
|
24
|
+
end
|
25
|
+
|
26
|
+
def self.handle_load_error(error)
|
27
|
+
message = error.is_a?(LoadError) ? "Error loading application" : "Error initializing application"
|
28
|
+
puts "#{message}: #{error.message}"
|
29
|
+
raise error
|
30
|
+
end
|
31
|
+
end
|
32
|
+
end
|
@@ -0,0 +1,39 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
require "model_context_protocol"
|
4
|
+
|
5
|
+
module Plasma
|
6
|
+
# Base resource class for PLASMA applications
|
7
|
+
class Resource < ModelContextProtocol::Server::Resource
|
8
|
+
# Required by MCP - defines the resource schema
|
9
|
+
# rubocop:disable Metrics/MethodLength
|
10
|
+
def self.schema
|
11
|
+
{
|
12
|
+
name: name.underscore,
|
13
|
+
description: "Base resource for PLASMA applications",
|
14
|
+
content_schema: {
|
15
|
+
type: "object",
|
16
|
+
properties: {
|
17
|
+
id: { type: "string" }
|
18
|
+
},
|
19
|
+
required: ["id"]
|
20
|
+
}
|
21
|
+
}
|
22
|
+
end
|
23
|
+
# rubocop:enable Metrics/MethodLength
|
24
|
+
|
25
|
+
# Required by MCP - defines the resource content
|
26
|
+
def content
|
27
|
+
{
|
28
|
+
id: id
|
29
|
+
}
|
30
|
+
end
|
31
|
+
|
32
|
+
attr_reader :id
|
33
|
+
|
34
|
+
def initialize(id:)
|
35
|
+
@id = id
|
36
|
+
super
|
37
|
+
end
|
38
|
+
end
|
39
|
+
end
|
@@ -0,0 +1,47 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
require "model_context_protocol"
|
4
|
+
|
5
|
+
module Plasma
|
6
|
+
# Handles MCP server operations
|
7
|
+
class Server
|
8
|
+
attr_reader :options
|
9
|
+
|
10
|
+
def initialize(options = {})
|
11
|
+
@options = options
|
12
|
+
end
|
13
|
+
|
14
|
+
def start
|
15
|
+
# Check if we're in a PLASMA project directory
|
16
|
+
unless plasma_project?
|
17
|
+
puts "Error: Not a PLASMA project directory. Please run this command from the root of a PLASMA project."
|
18
|
+
return false
|
19
|
+
end
|
20
|
+
|
21
|
+
# Load the application
|
22
|
+
app_class = load_application
|
23
|
+
|
24
|
+
# Start the MCP server
|
25
|
+
app_class.start_server
|
26
|
+
|
27
|
+
true
|
28
|
+
rescue Interrupt
|
29
|
+
# puts "\nShutting down MCP server..."
|
30
|
+
end
|
31
|
+
|
32
|
+
private
|
33
|
+
|
34
|
+
def plasma_project?
|
35
|
+
File.exist?("config/application.rb") &&
|
36
|
+
File.exist?("config/boot.rb") &&
|
37
|
+
Dir.exist?("app/prompts") &&
|
38
|
+
Dir.exist?("app/resources") &&
|
39
|
+
Dir.exist?("app/tools")
|
40
|
+
end
|
41
|
+
|
42
|
+
def load_application
|
43
|
+
require_relative "loader"
|
44
|
+
Plasma::Loader.load_project
|
45
|
+
end
|
46
|
+
end
|
47
|
+
end
|
@@ -0,0 +1,16 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module Plasma
|
4
|
+
module Storage
|
5
|
+
# Model for storing serialized data with automatic JSON parsing
|
6
|
+
class Record < ApplicationRecord
|
7
|
+
class << self
|
8
|
+
def inherited(subclass)
|
9
|
+
super
|
10
|
+
const_name = subclass.to_s.split("::").last
|
11
|
+
Plasma.const_set(const_name, subclass) unless Plasma.const_defined?(const_name)
|
12
|
+
end
|
13
|
+
end
|
14
|
+
end
|
15
|
+
end
|
16
|
+
end
|
@@ -0,0 +1,53 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module Plasma
|
4
|
+
module Storage
|
5
|
+
# Model for storing key-value pairs with automatic JSON parsing
|
6
|
+
class Variable < ApplicationRecord
|
7
|
+
self.primary_key = "key"
|
8
|
+
|
9
|
+
serialize :value do |val|
|
10
|
+
case val
|
11
|
+
when String
|
12
|
+
begin
|
13
|
+
JSON.parse(val)
|
14
|
+
rescue JSON::ParserError
|
15
|
+
val
|
16
|
+
end
|
17
|
+
when Hash, Array
|
18
|
+
val
|
19
|
+
else
|
20
|
+
val.to_s
|
21
|
+
end
|
22
|
+
end
|
23
|
+
|
24
|
+
class << self
|
25
|
+
attr_accessor :default_value
|
26
|
+
|
27
|
+
def inherited(subclass)
|
28
|
+
super
|
29
|
+
const_name = subclass.to_s.split("::").last
|
30
|
+
Plasma.const_set(const_name, subclass) unless Plasma.const_defined?(const_name)
|
31
|
+
end
|
32
|
+
|
33
|
+
def key
|
34
|
+
name.demodulize.underscore
|
35
|
+
end
|
36
|
+
|
37
|
+
def set(value)
|
38
|
+
raise "Failed to set value for #{key}" unless create_or_find_by(key: key).update(value: value)
|
39
|
+
|
40
|
+
value
|
41
|
+
end
|
42
|
+
|
43
|
+
def get
|
44
|
+
find_by(key: key)&.value || default_value
|
45
|
+
end
|
46
|
+
|
47
|
+
def default(value)
|
48
|
+
@default_value = value
|
49
|
+
end
|
50
|
+
end
|
51
|
+
end
|
52
|
+
end
|
53
|
+
end
|
@@ -0,0 +1,101 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
require "active_record"
|
4
|
+
require "sqlite3"
|
5
|
+
|
6
|
+
# Load all models
|
7
|
+
Dir[File.join(__dir__, "storage", "*.rb")].each { |file| require file }
|
8
|
+
|
9
|
+
module Plasma
|
10
|
+
# This module handles the storage operations for PLASMA applications
|
11
|
+
module Storage
|
12
|
+
# rubocop:disable Metrics/MethodLength
|
13
|
+
def self.define_default_schema
|
14
|
+
ActiveRecord::Schema.define(verbose: false) do
|
15
|
+
create_table :records do |t|
|
16
|
+
t.json :data
|
17
|
+
t.timestamps
|
18
|
+
end
|
19
|
+
|
20
|
+
create_table :variables, id: false do |t|
|
21
|
+
t.string :key, primary_key: true
|
22
|
+
t.json :value
|
23
|
+
end
|
24
|
+
|
25
|
+
create_table :settings do |t|
|
26
|
+
t.string :group, null: false
|
27
|
+
t.json :data
|
28
|
+
t.timestamps
|
29
|
+
end
|
30
|
+
|
31
|
+
add_index :settings, :group, unique: true
|
32
|
+
end
|
33
|
+
end
|
34
|
+
# rubocop:enable Metrics/MethodLength
|
35
|
+
|
36
|
+
def self.setup
|
37
|
+
establish_connection
|
38
|
+
load_schema
|
39
|
+
end
|
40
|
+
|
41
|
+
def self.establish_connection
|
42
|
+
ActiveRecord::Base.establish_connection(
|
43
|
+
adapter: "sqlite3",
|
44
|
+
database: ":memory:"
|
45
|
+
)
|
46
|
+
ActiveRecord::Migration.verbose = false
|
47
|
+
end
|
48
|
+
|
49
|
+
def self.load_schema
|
50
|
+
schema_path = File.join(Dir.pwd, "db", "schema.rb")
|
51
|
+
if File.exist?(schema_path)
|
52
|
+
load schema_path
|
53
|
+
else
|
54
|
+
define_default_schema
|
55
|
+
end
|
56
|
+
end
|
57
|
+
|
58
|
+
def self.add_record(fields = {})
|
59
|
+
Plasma::Storage::Record.create(data: fields)
|
60
|
+
end
|
61
|
+
|
62
|
+
def self.find_records
|
63
|
+
Plasma::Storage::Record.all.map { |r| r.data.merge(id: r.id, created_at: r.created_at) }
|
64
|
+
end
|
65
|
+
|
66
|
+
def self.find_records_by_field(field, value)
|
67
|
+
Plasma::Storage::Record.all.select { |r| r.data[field.to_s] == value || r.data[field.to_sym] == value }
|
68
|
+
.map { |r| r.data.merge(id: r.id, created_at: r.created_at) }
|
69
|
+
end
|
70
|
+
|
71
|
+
def self.find_records_with_field(field)
|
72
|
+
records = Plasma::Storage::Record.all
|
73
|
+
filtered = records.select { |r| r.data.key?(field.to_s) || r.data.key?(field.to_sym) }
|
74
|
+
filtered.map { |r| r.data.merge(id: r.id, created_at: r.created_at) }
|
75
|
+
end
|
76
|
+
|
77
|
+
def self.update_record_field(id, field, value)
|
78
|
+
record = Plasma::Storage::Record.find(id)
|
79
|
+
record.data[field] = value
|
80
|
+
record.save!
|
81
|
+
end
|
82
|
+
|
83
|
+
def self.set_var(key, value)
|
84
|
+
Plasma::Storage::Variable.create_or_find_by(key: key).update(value: value)
|
85
|
+
end
|
86
|
+
|
87
|
+
def self.get_var(key)
|
88
|
+
Plasma::Storage::Variable.find_by(key: key)&.value
|
89
|
+
end
|
90
|
+
|
91
|
+
def self.dump_to_json
|
92
|
+
{
|
93
|
+
records: find_records,
|
94
|
+
variables: Plasma::Storage::Variable.all.pluck(:key, :value).to_h
|
95
|
+
}.to_json
|
96
|
+
end
|
97
|
+
end
|
98
|
+
end
|
99
|
+
|
100
|
+
# Setup database when module is loaded
|
101
|
+
Plasma::Storage.setup
|
@@ -0,0 +1,40 @@
|
|
1
|
+
# Git
|
2
|
+
.git
|
3
|
+
.gitignore
|
4
|
+
|
5
|
+
# Ruby
|
6
|
+
*.gem
|
7
|
+
*.rbc
|
8
|
+
/.config
|
9
|
+
/coverage/
|
10
|
+
/InstalledFiles
|
11
|
+
/pkg/
|
12
|
+
/spec/reports/
|
13
|
+
/spec/examples.txt
|
14
|
+
/test/tmp/
|
15
|
+
/test/version_tmp/
|
16
|
+
/tmp/
|
17
|
+
|
18
|
+
# Environment
|
19
|
+
.env
|
20
|
+
.env.*
|
21
|
+
!.env.example
|
22
|
+
|
23
|
+
# Logs
|
24
|
+
*.log
|
25
|
+
|
26
|
+
# Editor directories and files
|
27
|
+
.idea
|
28
|
+
.vscode
|
29
|
+
*.swp
|
30
|
+
*.swo
|
31
|
+
*~
|
32
|
+
|
33
|
+
# OS generated files
|
34
|
+
.DS_Store
|
35
|
+
.DS_Store?
|
36
|
+
._*
|
37
|
+
.Spotlight-V100
|
38
|
+
.Trashes
|
39
|
+
ehthumbs.db
|
40
|
+
Thumbs.db
|