prompt_manager 0.3.1 → 0.3.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: add8f0261dd0fd2e91e8195330d923e8806c2e3a7243cfb51ce70a589fc76ef2
4
- data.tar.gz: 73b69061afdd242679c231949fba6a3b075f486feb20113cb838d0345dd2ffcf
3
+ metadata.gz: 62f11c67b06c2d28e14fb63c7fc170c9424d45d52bca52e6dbd0b256823a9150
4
+ data.tar.gz: 22a2092d82ff86b7ff4cfbba4cb6c9436545fd52326f0d1ede57e0aa08d00c12
5
5
  SHA512:
6
- metadata.gz: a65630d9623b49c34265b137f03cfb032ec645f736e9ca9327e2aaf045cdd0e2818e28e1b000bc80b32016f32a59192861db6cc7596c8ea6834b2c386ce1aac2
7
- data.tar.gz: ec16e90d781c354875932d454d04783bcd46b10f7a815ffe94204fd17475bc2930cb6627e55072ec9b937512031033bdf01eda83d62ed126d2833801fd156e0c
6
+ metadata.gz: b1d1d4111ee86007fa824781e7f9ff87092517854b67238d9fca5ec7aef68f2489ebf18e1b790d937588cc991060e61374d64c06635f23a76863afd4b6f8e8b8
7
+ data.tar.gz: f310abbef689d4e9a4e323aa7f83edcf003447bb92e01c6a5785f807d0d7a90784e6e4891ac984cde8895f236290c68297b07c8bef825560c5edddfe090878b5
data/CHANGELOG.md CHANGED
@@ -1,5 +1,13 @@
1
1
  ## [Unreleased]
2
2
 
3
+ ## [0.3.3] = 2023-12-01
4
+ - Added example of using the `search_proc` config parameter with the FileSystemAdapter.
5
+
6
+ ## [0.3.2] = 2023-12-01
7
+
8
+ - The ActiveRecordAdapter is passing its unit tests
9
+ - Dropped the concept of an sqlite3 adapter since active record can be used to access sqlite3 databases as well as the big boys.
10
+
3
11
  ## [0.3.0] = 2023-11-28
4
12
 
5
13
  - **Breaking change** The value of the parameters Hash for a keyword is now an Array instead of a single value. The last value in the Array is always the most recent value used for the given keyword. This was done to support the use of a Readline::History object editing in the [aia](https://github.com/MadBomber/aia) CLI tool
data/README.md CHANGED
@@ -9,26 +9,30 @@ Manage the parameterized prompts (text) used in generative AI (aka chatGPT, Open
9
9
 
10
10
  ## Table of Contents
11
11
 
12
- - [Installation](#installation)
13
- - [Usage](#usage)
14
- - [Overview](#overview)
12
+ - [Installation](#installation)
13
+ - [Usage](#usage)
14
+ - [Overview](#overview)
15
15
  - [Generative AI (gen-AI)](#generative-ai-gen-ai)
16
16
  - [What does a keyword look like?](#what-does-a-keyword-look-like)
17
- - [Storage Adapters](#storage-adapters)
18
- - [FileSystemAdapter](#filesystemadapter)
17
+ - [Storage Adapters](#storage-adapters)
18
+ - [FileSystemAdapter](#filesystemadapter)
19
19
  - [Configuration](#configuration)
20
- - [prompts_dir](#prompts_dir)
21
- - [search_proc](#search_proc)
22
- - [File Extensions](#file-extensions)
20
+ - [prompts_dir](#prompts_dir)
21
+ - [search_proc](#search_proc)
22
+ - [File Extensions](#file-extensions)
23
23
  - [Example Prompt Text File](#example-prompt-text-file)
24
24
  - [Example Prompt Parameters JSON File](#example-prompt-parameters-json-file)
25
25
  - [Extra Functionality](#extra-functionality)
26
- - [SqliteAdapter](#sqliteadapter)
27
- - [ActiveRecordAdapter](#activerecordadapter)
28
- - [Other Potential Storage Adapters](#other-potential-storage-adapters)
29
- - [Development](#development)
30
- - [Contributing](#contributing)
31
- - [License](#license)
26
+ - [ActiveRecordAdapter](#activerecordadapter)
27
+ - [Configuration](#configuration-1)
28
+ - [model](#model)
29
+ - [id_column](#id_column)
30
+ - [text_column](#text_column)
31
+ - [parameters_column](#parameters_column)
32
+ - [Other Potential Storage Adapters](#other-potential-storage-adapters)
33
+ - [Development](#development)
34
+ - [Contributing](#contributing)
35
+ - [License](#license)
32
36
 
33
37
  <!-- Tocer[finish]: Auto-generated, don't remove. -->
34
38
 
@@ -46,6 +50,8 @@ If bundler is not being used to manage dependencies, install the gem by executin
46
50
 
47
51
  See [examples/simple.rb](examples/simple.rb)
48
52
 
53
+ See also [examples/using_search_proc.rb](examples/using_search_proc.rb)
54
+
49
55
  ## Overview
50
56
 
51
57
  ### Generative AI (gen-AI)
@@ -60,13 +66,13 @@ The current hard-coded REGEX for a [KEYWORD] identifies any all [UPPERCASE_TEXT]
60
66
 
61
67
  This is just the initial convention adopted by prompt_manager. It is intended that this REGEX be configurable so that the prompt_manager can be used with other conventions.
62
68
 
63
- ### Storage Adapters
69
+ ## Storage Adapters
64
70
 
65
71
  A storage adapter is a class instance that ties the `PromptManager::Prompt` class to a storage facility that holds the actual prompts. Currently there are 3 storage adapters planned for implementation.
66
72
 
67
73
  The `PromptManager::Prompt` to support a small set of methods. A storage adapter can provide "extra" class or instance methods that can be used through the Prompt class. See the `test/prompt_manager/prompt_test.rb` for guidance on creating a new storage adapter.
68
74
 
69
- #### FileSystemAdapter
75
+ ### FileSystemAdapter
70
76
 
71
77
  This is the first storage adapter developed. It saves prompts as text files within the file system inside a designated `prompts_dir` (directory) such as `~/.prompts` or where it makes the most sense to you. Another example would be to have your directory on a shared file system so that others can use the same prompts.
72
78
 
@@ -74,16 +80,16 @@ The `prompt ID` is the basename of the text file. For example `todo.txt` is the
74
80
 
75
81
  The parameters for the `todo` prompt ID are saved in the same directory as `todo.txt` in a JSON file named `todo.json` (also in the examples directory.)
76
82
 
77
- ##### Configuration
83
+ #### Configuration
78
84
 
79
85
  Use a `config` block to establish the configuration for the class.
80
86
 
81
87
  ```ruby
82
88
  PromptManager::Storage::FileSystemAdapter.config do |o|
83
- o.prompts_dir = "path/to/prompts_directory"
84
- o.search_proc = -> (q) { "ag -l #{q} #{prompts_dir} | reformat" }
85
- o.prompt_extension = '.txt' # default
86
- o.params_extension = '.json' # the default
89
+ o.prompts_dir = "path/to/prompts_directory"
90
+ o.search_proc = nil # default
91
+ o.prompt_extension = '.txt' # default
92
+ o.params_extension = '.json' # default
87
93
  end
88
94
  ```
89
95
 
@@ -98,19 +104,21 @@ PromptManager::Prompt
98
104
  end.new
99
105
  ```
100
106
 
101
- ###### prompts_dir
107
+ ##### prompts_dir
102
108
 
103
109
  This is either a `String` or a `Pathname` object. All file paths are maintained in the class as `Pathname` objects. If you provide a `String` it will be converted. Relative paths will be converted to absolute paths.
104
110
 
105
111
  An `ArgumentError` will be raised when `prompts_dir` does not exist or if it is not a directory.
106
112
 
107
- ###### search_proc
113
+ ##### search_proc
114
+
115
+ The default for `search_proc` is nil which means that the search will be preformed by a default `search` method which is basically reading all the prompt files to see which ones contain the search term. It will return an Array of prompt IDs for each prompt file found that contains the search term. Its up to the application to select which returned prompt ID to use.
108
116
 
109
- The default for `search_proc` is nil. In this case the search will be preformed by a default `search` method which is basically reading all the prompt files to see which ones contain the search term. There are faster ways to do this kind of thing using CLI=based utilities.
117
+ There are faster ways to search and select files. For example there are specialized search and selection utilities that are available for the command line. The `examples` directory contains a `bash` script named `rgfzf` that uses `rg` (aka `ripgrep`) to do the searching and `fzf` to do the selecting.
110
118
 
111
- TODO: add a example to the examples directory on how to integrate with command line utilities.
119
+ See [examples/using_search_proc.rb](examples/using_search_proc.rb)
112
120
 
113
- ###### File Extensions
121
+ ##### File Extensions
114
122
 
115
123
  These two configuration options are `String` objects that must start with a period "." utherwise an `ArgumentError` will be raised.
116
124
 
@@ -121,7 +129,7 @@ Currently the `FileSystemAdapter` only supports a JSON serializer for its parame
121
129
 
122
130
  They exist so that there is a platform on to which other storage adapters can be built or serializers added. This is not currently on the roadmap.
123
131
 
124
- ##### Example Prompt Text File
132
+ #### Example Prompt Text File
125
133
 
126
134
  ```text
127
135
  # ~/.prompts/joke.txt
@@ -132,7 +140,7 @@ Tell me a few [KIND] jokes about [SUBJECT]
132
140
 
133
141
  Note the command lines at the top. This is a convention I use. It is not part of the software. I find it helpful in documenting the prompt.
134
142
 
135
- ##### Example Prompt Parameters JSON File
143
+ #### Example Prompt Parameters JSON File
136
144
 
137
145
  ```json
138
146
  {
@@ -151,7 +159,7 @@ Note the command lines at the top. This is a convention I use. It is not part
151
159
 
152
160
  The last value in the keyword's Array is the most recent value used for that keyword. This is a functionality established since v0.3.0. Its purpose is to provide a history of values from which a user can select to repeat a previous value or to select ta previous value and edit it into something new.
153
161
 
154
- ##### Extra Functionality
162
+ #### Extra Functionality
155
163
 
156
164
  The `FileSystemAdapter` adds two new methods for use by the `Prompt` class:
157
165
  * list - returns an Array of prompt IDs
@@ -160,15 +168,50 @@ The `FileSystemAdapter` adds two new methods for use by the `Prompt` class:
160
168
  Use the `path(prompt_id)` form against the `Prompt` class
161
169
  Use `prompt.path` when you have an instance of a `Prompt`
162
170
 
163
- #### SqliteAdapter
171
+ ### ActiveRecordAdapter
172
+
173
+ The `ActiveRecordAdapter` assumes that there is a database already configured by the application program that is requiring `prompt_manager` which has a model that contains prompt content. This model must have at least three columns which contain content for:
174
+
175
+ - a prompt ID
176
+ - prompt text
177
+ - prompt parameters
178
+
179
+ The model and the columns for these three elements can have any name. Those names are provided to the `ActiveRecordAdapter` in its config block.
180
+
181
+
182
+ #### Configuration
183
+
184
+ Use a `config` block to establish the configuration for the class.
185
+
186
+ The `PromptManager::Prompt` class expects an instance of a storage adapter class. By convention storage adapter class config methods will return `self` so that a simple `new` after the config will establish the instance.
187
+
188
+ ```ruby
189
+ PromptManager::Prompt
190
+ .storage_adapter =
191
+ PromptManager::Storage::ActiveRecordAdapter.config do |config|
192
+ config.model = DbPromptModel # any ActiveRecord::Base model
193
+ config.id_column = :prompt_name
194
+ config.text_column = :prompt_text
195
+ config.parameters_column = :prompt_params
196
+ end.new # adapters an instances of the adapter class
197
+ ```
198
+
199
+ ##### model
200
+ The `model` configuration parameter is the actual class name of the `ActiveRecord::Base` or `ApplicationRecord` (if you are using a rails application) that contains the content used for prompts.
201
+
202
+ ##### id_column
203
+ The `id_column` contains the name of the column that contains the "prompt ID" content. It can be either a `String` or `Symbol` value.
204
+
205
+ ##### text_column
206
+ The `text_column` contains name of the column that contains the actual raw text of the prompt. This raw text can include the keywords which will be replaced by values from the parameters Hash. The column name value can be either a `String` or a `Symbol`.
164
207
 
165
- TODO: This may be the next adapter to be implemented.
208
+ ##### parameters_column
209
+ The `parameters_column` contains the name of the column that contains the parameters used to replace keywords in the prompt text. This column in the database model is expected to be serialized. The `ActiveRecordAdapter` currently has a kludge bit of code that assumes that the serialization is done with JSON. The value of the parameters_column can be either a `String` or a `Symbol`.
166
210
 
167
- #### ActiveRecordAdapter
211
+ TODO: fix the kludge so that any serialization can be used.
168
212
 
169
- TODO: Still looking for requirements on how to integrate with an existing `rails` app. Looking for ideas.
170
213
 
171
- #### Other Potential Storage Adapters
214
+ ### Other Potential Storage Adapters
172
215
 
173
216
  There are many possibilities to example this plugin concept of the storage adapter. Here are some for consideration:
174
217
 
data/examples/rgfzf ADDED
@@ -0,0 +1,44 @@
1
+ #!/usr/bin/env bash
2
+ # Refactored examples/rgfzf to take search term and directory path as parameters
3
+
4
+ search_term="$1"
5
+ directory_path="${2:-$(pwd)}" # Use given directory path, or current working directory if not provided
6
+
7
+ # Verify that a search term is provided
8
+ if [[ -z "$search_term" ]]; then
9
+ echo "Usage: $0 search_term [directory_path]"
10
+ exit 1
11
+ fi
12
+
13
+ # Ensure ripgrep (rg) and fzf are installed
14
+ if ! command -v rg &> /dev/null || ! command -v fzf &> /dev/null; then
15
+ echo "Please ensure ripgrep and fzf are installed before running this script."
16
+ exit 1
17
+ fi
18
+
19
+ # Perform the file search using ripgrep and selection using fzf
20
+ selected_file=$(
21
+ rg --files-with-matches \
22
+ --no-messages \
23
+ --smart-case "$search_term" "$directory_path" |
24
+ sed "s#^$directory_path/##" | # Strip out the directory path
25
+ fzf --ansi \
26
+ --color "hl:-1:underline,hl+:-1:underline:reverse" \
27
+ --preview "cat $directory_path/{}" \
28
+ --preview-window "up,60%,border-bottom" \
29
+ --no-multi \
30
+ --exit-0 \
31
+ --query "$search_term"
32
+ )
33
+
34
+
35
+ # If no file was selected, exit the script
36
+ if [[ -z "$selected_file" ]]; then
37
+ # echo "No file selected."
38
+ exit 0
39
+ fi
40
+
41
+ # Remove the file extension
42
+ prompt_id=$(echo "$selected_file" | sed "s/\.[^.]*$//")
43
+
44
+ echo "$prompt_id"
data/examples/simple.rb CHANGED
@@ -9,9 +9,6 @@
9
9
  ## By: Dewayne VanHoozer (dvanhoozer@gmail.com)
10
10
  ##
11
11
  #
12
- # TODO: Add `list` to get an Array of prompt IDs
13
- # TODO: Add `path` to get a path to the prompt file
14
- #
15
12
 
16
13
  require 'prompt_manager'
17
14
  require 'prompt_manager/storage/file_system_adapter'
@@ -140,3 +137,23 @@ puts <<~EOS
140
137
  EOS
141
138
 
142
139
  puts PromptManager::Prompt.path('toy/8-ball')
140
+
141
+ puts
142
+
143
+ puts "Default Search for Prompts"
144
+ puts "=========================="
145
+
146
+ print "Search Proc Class: "
147
+ puts PromptManager::Prompt.storage_adapter.search_proc.class
148
+
149
+ search_term = "txt" # some comment lines show the file name example: todo.txt
150
+
151
+ puts "Search for '#{search_term}' ..."
152
+
153
+ prompt_ids = PromptManager::Prompt.search search_term
154
+
155
+ # NOTE: prompt+ids is an Array of prompt IDs even if there is only one entry.
156
+ # or and empty array if there are no prompts have the search term.
157
+
158
+ puts "Found: #{prompt_ids}"
159
+
@@ -0,0 +1,71 @@
1
+ #!/usr/bin/env ruby
2
+ # encoding: utf-8
3
+ # frozen_string_literal: true
4
+ # warn_indent: true
5
+ ##########################################################
6
+ ###
7
+ ## File: using_search_proc.rb
8
+ ## Desc: Simple demo of the PromptManager and FileStorageAdapter
9
+ ## By: Dewayne VanHoozer (dvanhoozer@gmail.com)
10
+ ##
11
+ #
12
+
13
+ require 'debug_me'
14
+ include DebugMe
15
+
16
+ require 'prompt_manager'
17
+ require 'prompt_manager/storage/file_system_adapter'
18
+
19
+ require 'amazing_print'
20
+ require 'pathname'
21
+
22
+ HERE = Pathname.new( __dir__ )
23
+ PROMPTS_DIR = HERE + "prompts_dir"
24
+ SEARCH_SCRIPT = HERE + 'rgfzf' # a bash script using rg and fzf
25
+
26
+ ######################################################
27
+ # Main
28
+
29
+ at_exit do
30
+ puts
31
+ puts "Done."
32
+ puts
33
+ end
34
+
35
+ # Configure the Storage Adapter to use
36
+ PromptManager::Storage::FileSystemAdapter.config do |config|
37
+ config.prompts_dir = PROMPTS_DIR
38
+ config.search_proc = ->(q) {`#{SEARCH_SCRIPT} #{q} #{PROMPTS_DIR}`} # default
39
+ # config.prompt_extension = '.txt' # default
40
+ # config.parms+_extension = '.json' # default
41
+ end
42
+
43
+ PromptManager::Prompt.storage_adapter = PromptManager::Storage::FileSystemAdapter.new
44
+
45
+
46
+
47
+ puts "Using Custom Search Proc"
48
+ puts "========================"
49
+
50
+ print "Search Proc Class: "
51
+ puts PromptManager::Prompt.storage_adapter.search_proc.class
52
+
53
+ search_term = "txt" # some comment lines show the file name example: todo.txt
54
+
55
+ puts "Search for '#{search_term}' ..."
56
+
57
+ prompt_id = PromptManager::Prompt.search search_term
58
+
59
+ # NOTE: the search proc uses fzf as a selection tool. In this
60
+ # case only one selected prompt ID that matches the search
61
+ # term will be returned.
62
+
63
+ puts "Found: '#{prompt_id}' which is a #{prompt_id.class}. empty? #{prompt_id.empty?}"
64
+
65
+ puts <<~EOS
66
+
67
+ When the rgfzf bash script does not find a prompt ID or if the
68
+ ESC key is pressed, the prompt ID that is returned will be an empty String.
69
+
70
+ EOS
71
+
@@ -47,8 +47,8 @@ class PromptManager::Prompt
47
47
 
48
48
  # SMELL: Does the db (aka storage adapter) really need
49
49
  # to be accessible by the main program?
50
- attr_accessor :db, :id, :text, :parameters, :keywords
51
-
50
+ attr_accessor :db, :id, :text, :parameters
51
+
52
52
 
53
53
  # Retrieve the specific prompt ID from the Storage system.
54
54
  def initialize(
@@ -120,6 +120,7 @@ class PromptManager::Prompt
120
120
  update_keywords
121
121
  end
122
122
 
123
+
123
124
  ######################################
124
125
  private
125
126
 
@@ -1,161 +1,146 @@
1
1
  # prompt_manager/lib/prompt_manager/storage/active_record_adapter.rb
2
2
 
3
- require 'active_record'
4
-
5
- # TODO: Will need a database.yml file
6
- # will need to know the column names that coorespond
7
- # with the things that the Prompt class wants.
3
+ # This class acts as an adapter for interacting with an ActiveRecord model
4
+ # to manage storage operations for PromptManager::Prompt instances. It defines
5
+ # methods that allow for saving, searching, retrieving by ID, and deleting
6
+ # prompts.
8
7
 
9
8
  class PromptManager::Storage::ActiveRecordAdapter
10
- attr_reader :model_class
9
+
10
+ class << self
11
+ attr_accessor :model,
12
+ :id_column,
13
+ :text_column,
14
+ :parameters_column
11
15
 
12
- def initialize(model_class)
13
- @model_class = model_class
14
- end
16
+ def config
17
+ if block_given?
18
+ yield self
19
+ validate_configuration
20
+ else
21
+ raise ArgumentError, "No block given to config"
22
+ end
23
+
24
+ self
25
+ end
15
26
 
16
27
 
17
- def prompt_text(prompt_id)
18
- prompt = find_prompt(prompt_id)
19
- prompt.text
20
- end
28
+ def validate_configuration
29
+ validate_model
30
+ validate_columns
31
+ end
21
32
 
22
33
 
23
- def parameter_values(prompt_id)
24
- prompt = find_prompt(prompt_id)
25
- JSON.parse(prompt.params, symbolize_names: true)
26
- end
34
+ def validate_model
35
+ raise ArgumentError, "AR Model not set" unless model
36
+ raise ArgumentError, "AR Model is not an ActiveRecord model" unless model < ActiveRecord::Base
37
+ end
27
38
 
28
39
 
29
- def save(prompt_id, prompt_text, parameter_values)
30
- prompt = model_class.find_or_initialize_by(id: prompt_id)
31
- prompt.text = prompt_text
32
- prompt.params = parameter_values.to_json
33
- prompt.save!
34
- end
40
+ def validate_columns
41
+ columns = model.column_names # Array of Strings
42
+ [id_column, text_column, parameters_column].each do |column|
43
+ raise ArgumentError, "#{column} is not a valid column for model #{model}" unless columns.include?(column.to_s)
44
+ end
45
+ end
35
46
 
36
47
 
37
- def delete(prompt_id)
38
- prompt = find_prompt(prompt_id)
39
- prompt.destroy
40
- end
41
48
 
49
+ def method_missing(method_name, *args, &block)
50
+ if model.respond_to?(method_name)
51
+ model.send(method_name, *args, &block)
52
+ else
53
+ super
54
+ end
55
+ end
42
56
 
43
- def search(for_what)
44
- # TODO: search through all prompts. Return an Array of
45
- # prompt_id where the text of the prompt contains
46
- # for_what is being searched.
47
57
 
48
- []
58
+ def respond_to_missing?(method_name, include_private = false)
59
+ model.respond_to?(method_name, include_private) || super
60
+ end
49
61
  end
62
+
50
63
 
64
+ ##############################################
65
+ attr_accessor :record
51
66
 
52
- class << self
53
- def config
54
- # TODO: establish a connection to the database
55
- # maybe define the prompts table and its
56
- # columns of interest.
57
- end
58
- end
59
67
 
60
- ###############################################
61
- private
68
+ # Avoid code littered with self.class prefixes ...
69
+ def model = self.class.model
70
+ def id_column = self.class.id_column
71
+ def text_column = self.class.text_column
72
+ def parameters_column = self.class.parameters_column
73
+
62
74
 
63
- def find_prompt(prompt_id)
64
- model_class.find_by(id: prompt_id) || raise('Prompt not found')
75
+ def initialize
76
+ self.class.send(:validate_configuration) # send gets around private designations of a method
77
+ @record = model.first
65
78
  end
66
- end
67
79
 
68
80
 
69
- __END__
81
+ def get(id:)
82
+ @record = model.find_by(id_column => id)
83
+ raise ArgumentError, "Prompt not found with id: #{id}" unless @record
70
84
 
71
- # prompt_manager/lib/prompt_manager/storage/active_record_adapter.rb
85
+ # kludge? testing showed that parameters was being
86
+ # returned as a String. Did serialization fail or is
87
+ # there something else going on?
88
+ # FIXME: expected the parameters_column to be a HAsh after de-serialization
89
+ parameters = @record[parameters_column]
72
90
 
73
- require 'active_record'
91
+ if parameters.is_a? String
92
+ parameters = JSON.parse parameters
93
+ end
74
94
 
75
- module PromptManager
76
- module Storage
77
- class ActiveRecordAdapter
95
+ {
96
+ id: id, # same as the id_column
97
+ text: @record[text_column],
98
+ parameters: parameters
99
+ }
100
+ end
78
101
 
79
- # Define models for ActiveRecord
80
- class Prompt < ActiveRecord::Base
81
- validates :unique_id, presence: true
82
- validates :text, presence: true
83
- end
84
102
 
85
- class PromptParameter < ActiveRecord::Base
86
- belongs_to :prompt
87
- validates :key, presence: true
88
- serialize :value
89
- end
103
+ def save(id:, text: "", parameters: {})
104
+ @record = model.find_or_initialize_by(id_column => id)
90
105
 
91
- def initialize
92
- unless ActiveRecord::Base.connected?
93
- raise ArgumentError, "ActiveRecord is not connected"
94
- end
95
- end
106
+ @record[text_column] = text
107
+ @record[parameters_column] = parameters
108
+ @record.save!
109
+ end
96
110
 
97
- def get(id:)
98
- prompt = Prompt.find_by(unique_id: id)
99
- return nil unless prompt
100
111
 
101
- parameters = prompt.prompt_parameters.index_by(&:key)
112
+ def delete(id:)
113
+ @record = model.find_by(id_column => id)
114
+ @record&.destroy
115
+ end
102
116
 
103
- {
104
- id: prompt.unique_id,
105
- text: prompt.text,
106
- parameters: parameters.transform_values(&:value)
107
- }
108
- end
109
117
 
110
- def save(id:, text: "", parameters: {})
111
- prompt = Prompt.find_or_initialize_by(unique_id: id)
112
- prompt.text = text
113
- prompt.save!
118
+
119
+ def list(*)
120
+ model.all.pluck(id_column)
121
+ end
114
122
 
115
- parameters.each do |key, value|
116
- parameter = PromptParameter.find_or_initialize_by(prompt: prompt, key: key)
117
- parameter.value = value
118
- parameter.save!
119
- end
120
- end
121
123
 
122
- def delete(id:)
123
- prompt = Prompt.find_by(unique_id: id)
124
- return unless prompt
125
-
126
- prompt.prompt_parameters.destroy_all
127
- prompt.destroy
128
- end
124
+ def search(for_what)
125
+ model.where("#{text_column} LIKE ?", "%#{for_what}%").pluck(id_column)
126
+ end
129
127
 
130
- def search(for_what)
131
- query = '%' + for_what.downcase + '%'
132
- Prompt.where('LOWER(text) LIKE ?', query).pluck(:unique_id)
133
- end
134
128
 
135
- def list(*)
136
- Prompt.pluck(:unique_id)
137
- end
129
+ ##############################################
130
+ private
138
131
 
139
- private
140
132
 
141
- # This is an example of how the database connection setup could look like,
142
- # but it should be handled externally in the actual application setup.
143
- def self.setup_database_connection
144
- ActiveRecord::Base.establish_connection(
145
- adapter: 'sqlite3',
146
- database: 'prompts.db'
147
- )
148
- end
133
+ def method_missing(method_name, *args, &block)
134
+ if @record.respond_to?(method_name)
135
+ model.send(method_name, args.first, &block)
136
+ else
137
+ super
149
138
  end
150
139
  end
151
- end
152
-
153
- # After this, you would need to create a database migration to generate the required tables.
154
- # Additionally, you have to establish an ActiveRecord connection before using this adapter,
155
- # typically in the environment setup of your application.
156
140
 
157
- # Keep in mind you need to create migrations for both the Prompt and PromptParameter models,
158
- # and manage the database schema using ActiveRecord migrations. This adapter assumes that the
159
- # database structure is already in place and follows the schema inferred by the models in the adapter.
160
141
 
142
+ def respond_to_missing?(method_name, include_private = false)
143
+ model.respond_to?(method_name, include_private) || super
144
+ end
145
+ end
161
146
 
@@ -211,8 +211,8 @@ class PromptManager::Storage::FileSystemAdapter
211
211
  def search(for_what)
212
212
  search_term = for_what.downcase
213
213
 
214
- if @search_proc
215
- @search_proc.call(search_term)
214
+ if search_proc.is_a? Proc
215
+ search_proc.call(search_term)
216
216
  else
217
217
  search_prompts(search_term)
218
218
  end
@@ -1,5 +1,5 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  module PromptManager
4
- VERSION = "0.3.1"
4
+ VERSION = "0.3.3"
5
5
  end
metadata CHANGED
@@ -1,15 +1,29 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: prompt_manager
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.3.1
4
+ version: 0.3.3
5
5
  platform: ruby
6
6
  authors:
7
7
  - Dewayne VanHoozer
8
8
  autorequire:
9
9
  bindir: bin
10
10
  cert_chain: []
11
- date: 2023-11-29 00:00:00.000000000 Z
11
+ date: 2023-12-01 00:00:00.000000000 Z
12
12
  dependencies:
13
+ - !ruby/object:Gem::Dependency
14
+ name: activerecord
15
+ requirement: !ruby/object:Gem::Requirement
16
+ requirements:
17
+ - - ">="
18
+ - !ruby/object:Gem::Version
19
+ version: '0'
20
+ type: :development
21
+ prerelease: false
22
+ version_requirements: !ruby/object:Gem::Requirement
23
+ requirements:
24
+ - - ">="
25
+ - !ruby/object:Gem::Version
26
+ version: '0'
13
27
  - !ruby/object:Gem::Dependency
14
28
  name: amazing_print
15
29
  requirement: !ruby/object:Gem::Requirement
@@ -84,13 +98,14 @@ files:
84
98
  - examples/prompts_dir/todo.json
85
99
  - examples/prompts_dir/todo.txt
86
100
  - examples/prompts_dir/toy/8-ball.txt
101
+ - examples/rgfzf
87
102
  - examples/simple.rb
103
+ - examples/using_search_proc.rb
88
104
  - lib/prompt_manager.rb
89
105
  - lib/prompt_manager/prompt.rb
90
106
  - lib/prompt_manager/storage.rb
91
107
  - lib/prompt_manager/storage/active_record_adapter.rb
92
108
  - lib/prompt_manager/storage/file_system_adapter.rb
93
- - lib/prompt_manager/storage/sqlite_adapter.rb
94
109
  - lib/prompt_manager/version.rb
95
110
  homepage: https://github.com/MadBomber/prompt_manager
96
111
  licenses:
@@ -1,156 +0,0 @@
1
- # prompt_manager/lib/prompt_manager/storage/sqlite_adapter.rb
2
-
3
- require 'json'
4
- require 'sqlite3'
5
-
6
- class PromptManager::Storage::SqliteAdapter
7
- def initialize(db_path = 'prompts.db')
8
- @db = SQLite3::Database.new(db_path)
9
- create_tables
10
- end
11
-
12
- def save_prompt(prompt_id, text, params)
13
- @db.execute("INSERT INTO prompts (id, text, params) VALUES (?, ?, ?)",
14
- [prompt_id, text, params.to_json])
15
- end
16
-
17
- def prompt_text(prompt_id)
18
- result = @db.get_first_value("SELECT text FROM prompts WHERE id = ?", [prompt_id])
19
- raise 'Prompt not found' if result.nil?
20
- result
21
- end
22
-
23
- def parameter_values(prompt_id)
24
- json_content = @db.get_first_value("SELECT params FROM prompts WHERE id = ?", [prompt_id])
25
- raise 'Parameters not found' if json_content.nil?
26
- JSON.parse(json_content, symbolize_names: true)
27
- end
28
-
29
- def save(prompt_id, prompt_text, parameter_values)
30
- @db.execute(
31
- 'REPLACE INTO prompts (id, text, params) VALUES (?, ?, ?)',
32
- [prompt_id, prompt_text, parameter_values.to_json]
33
- )
34
- end
35
-
36
- def delete(prompt_id)
37
- @db.execute('DELETE FROM prompts WHERE id = ?', [prompt_id])
38
- end
39
-
40
-
41
- def search(for_what)
42
- # TODO: search through all prompts. Return an Array of
43
- # prompt_id where the text of the prompt contains
44
- # for_what is being searched.
45
-
46
- []
47
- end
48
-
49
- ###################################################
50
- private
51
-
52
- def create_tables
53
- @db.execute <<-SQL
54
- CREATE TABLE IF NOT EXISTS prompts (
55
- id TEXT PRIMARY KEY,
56
- text TEXT NOT NULL,
57
- params TEXT NOT NULL
58
- );
59
- SQL
60
- end
61
- end
62
-
63
-
64
- __END__
65
-
66
-
67
- require 'sequel'
68
- require 'pathname'
69
- require 'json'
70
-
71
- module PromptManager
72
- module Storage
73
- class SqliteAdapter
74
- DEFAULT_DB_FILE = 'prompts.sqlite3'.freeze
75
-
76
- class << self
77
- attr_accessor :db_file, :db
78
-
79
- def config
80
- if block_given?
81
- yield self
82
- else
83
- raise ArgumentError, 'No block given to config'
84
- end
85
-
86
- self.db = Sequel.sqlite(db_file || DEFAULT_DB_FILE) # Use provided db_file or default
87
- create_tables unless db.table_exists?(:prompts)
88
- end
89
-
90
- # Define the necessary tables within the SQLite database if they don't exist
91
- def create_tables
92
- db.create_table :prompts do
93
- primary_key :id
94
- String :prompt_id, unique: true, null: false
95
- Text :text
96
- Json :parameters
97
-
98
- index :prompt_id
99
- end
100
- end
101
- end
102
-
103
- def initialize
104
- @db = self.class.db
105
- end
106
-
107
- def get(id:)
108
- validate_id(id)
109
- result = @db[:prompts].where(prompt_id: id).first
110
- raise ArgumentError, 'Prompt not found' unless result
111
-
112
- {
113
- id: result[:prompt_id],
114
- text: result[:text],
115
- parameters: result[:parameters]
116
- }
117
- end
118
-
119
- def save(id:, text: '', parameters: {})
120
- validate_id(id)
121
- rec = @db[:prompts].where(prompt_id: id).first
122
- if rec
123
- @db[:prompts].where(prompt_id: id).update(text: text, parameters: Sequel.pg_json(parameters))
124
- else
125
- @db[:prompts].insert(prompt_id: id, text: text, parameters: Sequel.pg_json(parameters))
126
- end
127
- end
128
-
129
- def delete(id:)
130
- validate_id(id)
131
- @db[:prompts].where(prompt_id: id).delete
132
- end
133
-
134
- # Return an Array of prompt IDs
135
- def list()
136
- @db[:prompts].select_map(:prompt_id)
137
- end
138
-
139
- def search(for_what)
140
- search_term = for_what.downcase
141
- @db[:prompts].where(Sequel.ilike(:text, "%#{search_term}%")).select_map(:prompt_id)
142
- end
143
-
144
- private
145
-
146
- # Validate that the ID contains good characters.
147
- def validate_id(id)
148
- raise ArgumentError, "Invalid ID format id: #{id}" unless id =~ /^[a-zA-Z0-9\-\/_]+$/
149
- end
150
- end
151
- end
152
- end
153
-
154
-
155
-
156
-