sage-rails 0.0.3
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/README.md +202 -0
- data/app/assets/images/chevron-down-zinc-500.svg +1 -0
- data/app/assets/images/chevron-right.svg +1 -0
- data/app/assets/images/loading.svg +4 -0
- data/app/assets/images/sage/chevron-down-zinc-500.svg +1 -0
- data/app/assets/images/sage/chevron-right.svg +1 -0
- data/app/assets/images/sage/loading.svg +4 -0
- data/app/assets/javascripts/sage/application.js +18 -0
- data/app/assets/stylesheets/sage/application.css +308 -0
- data/app/controllers/sage/actions_controller.rb +5 -0
- data/app/controllers/sage/application_controller.rb +4 -0
- data/app/controllers/sage/base_controller.rb +10 -0
- data/app/controllers/sage/checks_controller.rb +65 -0
- data/app/controllers/sage/dashboards_controller.rb +130 -0
- data/app/controllers/sage/queries/messages_controller.rb +62 -0
- data/app/controllers/sage/queries_controller.rb +596 -0
- data/app/helpers/sage/application_helper.rb +30 -0
- data/app/helpers/sage/queries_helper.rb +23 -0
- data/app/javascript/controllers/element_removal_controller.js +7 -0
- data/app/javascript/sage/controllers/clipboard_controller.js +26 -0
- data/app/javascript/sage/controllers/dashboard_controller.js +132 -0
- data/app/javascript/sage/controllers/reverse_infinite_scroll_controller.js +146 -0
- data/app/javascript/sage/controllers/search_controller.js +47 -0
- data/app/javascript/sage/controllers/select_controller.js +215 -0
- data/app/javascript/sage.js +19 -0
- data/app/jobs/sage/application_job.rb +4 -0
- data/app/jobs/sage/process_report_job.rb +80 -0
- data/app/mailers/sage/application_mailer.rb +6 -0
- data/app/models/sage/application_record.rb +5 -0
- data/app/models/sage/message.rb +8 -0
- data/app/schemas/sage/report_response_schema.rb +8 -0
- data/app/views/layouts/application.html.erb +34 -0
- data/app/views/layouts/sage/application.html.erb +94 -0
- data/app/views/sage/checks/_form.html.erb +81 -0
- data/app/views/sage/checks/_search.html.erb +8 -0
- data/app/views/sage/checks/edit.html.erb +10 -0
- data/app/views/sage/checks/index.html.erb +58 -0
- data/app/views/sage/checks/new.html.erb +8 -0
- data/app/views/sage/dashboards/_form.html.erb +50 -0
- data/app/views/sage/dashboards/_search.html.erb +8 -0
- data/app/views/sage/dashboards/index.html.erb +58 -0
- data/app/views/sage/dashboards/new.html.erb +8 -0
- data/app/views/sage/dashboards/show.html.erb +58 -0
- data/app/views/sage/messages/_form.html.erb +14 -0
- data/app/views/sage/queries/_caching.html.erb +17 -0
- data/app/views/sage/queries/_form.html.erb +72 -0
- data/app/views/sage/queries/_input.html.erb +17 -0
- data/app/views/sage/queries/_message.html.erb +25 -0
- data/app/views/sage/queries/_message.turbo_stream.erb +10 -0
- data/app/views/sage/queries/_new_form.html.erb +43 -0
- data/app/views/sage/queries/_run.html.erb +232 -0
- data/app/views/sage/queries/_search.html.erb +8 -0
- data/app/views/sage/queries/_statement_box.html.erb +241 -0
- data/app/views/sage/queries/_streaming_message.html.erb +14 -0
- data/app/views/sage/queries/create.turbo_stream.erb +114 -0
- data/app/views/sage/queries/edit.html.erb +48 -0
- data/app/views/sage/queries/index.html.erb +59 -0
- data/app/views/sage/queries/messages/create.turbo_stream.erb +22 -0
- data/app/views/sage/queries/messages/index.html.erb +44 -0
- data/app/views/sage/queries/messages/index.turbo_stream.erb +15 -0
- data/app/views/sage/queries/new.html.erb +195 -0
- data/app/views/sage/queries/run.html.erb +1 -0
- data/app/views/sage/queries/run.turbo_stream.erb +3 -0
- data/app/views/sage/queries/show.html.erb +49 -0
- data/app/views/sage/queries/table_schema.html.erb +77 -0
- data/app/views/sage/shared/_navigation.html.erb +26 -0
- data/app/views/sage/shared/_overlay.html.erb +11 -0
- data/config/importmap.rb +11 -0
- data/config/initializers/pagy.rb +2 -0
- data/config/initializers/ransack.rb +152 -0
- data/config/routes.rb +31 -0
- data/lib/generators/sage/USAGE +13 -0
- data/lib/generators/sage/install/install_generator.rb +128 -0
- data/lib/generators/sage/install/templates/sage.rb +22 -0
- data/lib/sage/database_schema_context.rb +56 -0
- data/lib/sage/engine.rb +260 -0
- data/lib/sage/model_scopes_context.rb +185 -0
- data/lib/sage/report_processor.rb +263 -0
- data/lib/sage/version.rb +3 -0
- data/lib/sage.rb +25 -0
- data/lib/tasks/sage_tasks.rake +4 -0
- metadata +245 -0
checksums.yaml
ADDED
@@ -0,0 +1,7 @@
|
|
1
|
+
---
|
2
|
+
SHA256:
|
3
|
+
metadata.gz: d3809df36d3eecf4a5b214bf1fc0538973a7f58199e64647c4e91e4dbfa5f582
|
4
|
+
data.tar.gz: cbe5378c43e18800e501a93dd6fb054251886dff09a22a051682a41dbba8aa3e
|
5
|
+
SHA512:
|
6
|
+
metadata.gz: ec94c0b90087cfc1cb1dab13808b5f10bbe3706e83f0c4b10e97e42f01fc1ab252e92163874db03bc408afbf498f9e8efd019b1170af4f3286a5247f0dde4907
|
7
|
+
data.tar.gz: a4c193ef6d4603ee02a1f72fa3820c963394cf2f523ccef743b8f55a9933053b206ad59a87ace4219e07828a68e6c5c024c6b8e3793d1ce27bb5a12f78ac6f72
|
data/README.md
ADDED
@@ -0,0 +1,202 @@
|
|
1
|
+
# Sage
|
2
|
+
|
3
|
+

|
4
|
+
|
5
|
+
**Natural language reporting to help your team build accurate reports, faster.**
|
6
|
+
|
7
|
+
Sage is a Rails engine built on top of the excellent [Blazer](https://github.com/ankane/blazer) gem, adding an LLM interface to make data exploration accessible via natural language.
|
8
|
+
|
9
|
+
## Installation
|
10
|
+
|
11
|
+
Add Sage to your application's Gemfile:
|
12
|
+
|
13
|
+
```ruby
|
14
|
+
gem "sage-rails"
|
15
|
+
```
|
16
|
+
|
17
|
+
Run bundle install:
|
18
|
+
```bash
|
19
|
+
$ bundle install
|
20
|
+
```
|
21
|
+
|
22
|
+
## Getting Started
|
23
|
+
|
24
|
+
Run the install generator to set up Sage in your Rails application:
|
25
|
+
|
26
|
+
```bash
|
27
|
+
$ rails generate sage:install
|
28
|
+
```
|
29
|
+
|
30
|
+
This generator will:
|
31
|
+
- Install Blazer if not already present
|
32
|
+
- Mount Sage at `/sage` in your routes
|
33
|
+
- Create an initializer at `config/initializers/sage.rb`
|
34
|
+
- Set up database migrations for message storage
|
35
|
+
- Configure JavaScript and CSS dependencies
|
36
|
+
|
37
|
+
After installation, run the migrations:
|
38
|
+
```bash
|
39
|
+
$ rails db:migrate
|
40
|
+
```
|
41
|
+
|
42
|
+
## LLM Configuration
|
43
|
+
|
44
|
+
Sage supports both Anthropic Claude and OpenAI models for SQL generation. Configure your preferred AI service in `config/initializers/sage.rb`:
|
45
|
+
|
46
|
+
### Using Anthropic
|
47
|
+
|
48
|
+
```ruby
|
49
|
+
Sage.configure do |config|
|
50
|
+
config.provider = :anthropic
|
51
|
+
|
52
|
+
# API key configuration
|
53
|
+
config.anthropic_api_key = Rails.application.credentials.dig(:anthropic, :api_key) ||
|
54
|
+
ENV["ANTHROPIC_API_KEY"]
|
55
|
+
|
56
|
+
# Model selection (defaults to claude-3-opus-20240229)
|
57
|
+
config.anthropic_model = "claude-3-opus-20240229"
|
58
|
+
end
|
59
|
+
```
|
60
|
+
|
61
|
+
### Using OpenAI
|
62
|
+
|
63
|
+
```ruby
|
64
|
+
Sage.configure do |config|
|
65
|
+
config.provider = :openai
|
66
|
+
|
67
|
+
# API Key Configuration
|
68
|
+
config.openai_api_key = Rails.application.credentials.dig(:openai, :api_key) ||
|
69
|
+
ENV["OPENAI_API_KEY"]
|
70
|
+
|
71
|
+
# Model selection
|
72
|
+
config.openai_model = "gpt-4" # or "gpt-3.5-turbo" for faster responses
|
73
|
+
end
|
74
|
+
```
|
75
|
+
|
76
|
+
## Blazer
|
77
|
+
|
78
|
+
Sage is built on top of Blazer and honors all existing Blazer configurations.
|
79
|
+
|
80
|
+
All Blazer features are fully supported:
|
81
|
+
|
82
|
+
- **Database Setup**: Configure your database connections through Blazer
|
83
|
+
- **Multiple Data Sources**: Switch between different databases seamlessly
|
84
|
+
- **Smart Variables**: Use dynamic variables in generated queries
|
85
|
+
- **Checks & Alerts**: Set up automated monitoring on your queries
|
86
|
+
- **Auditing**: Track query usage and performance
|
87
|
+
- **Security**: Leverage Blazer's authentication and authorization features
|
88
|
+
|
89
|
+
For detailed information on Blazer-specific features, refer to the [Blazer documentation](https://github.com/ankane/blazer).
|
90
|
+
|
91
|
+
## Database Context
|
92
|
+
|
93
|
+
Sage introspects your database schema to provide context for more accurate SQL generation. This feature works out of the box with Blazer's data sources.
|
94
|
+
|
95
|
+
### Multiple Data Sources
|
96
|
+
|
97
|
+
If you have multiple Blazer data sources configured, Sage will use the appropriate schema for each:
|
98
|
+
|
99
|
+
```ruby
|
100
|
+
# config/blazer.yml
|
101
|
+
data_sources:
|
102
|
+
main:
|
103
|
+
url: <%= ENV["DATABASE_URL"] %>
|
104
|
+
analytics:
|
105
|
+
url: <%= ENV["ANALYTICS_DATABASE_URL"] %>
|
106
|
+
```
|
107
|
+
|
108
|
+
When querying from different data sources in Blazer, Sage automatically switches schema context.
|
109
|
+
|
110
|
+
## Model Scope Context
|
111
|
+
|
112
|
+
Sage leverages your Rails model scopes as documentation for query patterns, dramatically improving the accuracy of generated SQL queries, especially for complex multi-table reports.
|
113
|
+
|
114
|
+
### Example
|
115
|
+
|
116
|
+
Given these model scopes:
|
117
|
+
|
118
|
+
```ruby
|
119
|
+
# app/models/user.rb
|
120
|
+
class User < ApplicationRecord
|
121
|
+
scope :active, -> { where(status: 'active') }
|
122
|
+
scope :recent, -> { where('created_at > ?', 30.days.ago) }
|
123
|
+
scope :with_orders, -> { joins(:orders).distinct }
|
124
|
+
scope :high_value, -> {
|
125
|
+
joins(:orders)
|
126
|
+
.group('users.id')
|
127
|
+
.having('SUM(orders.total) > ?', 1000)
|
128
|
+
}
|
129
|
+
end
|
130
|
+
|
131
|
+
# app/models/order.rb
|
132
|
+
class Order < ApplicationRecord
|
133
|
+
scope :completed, -> { where(status: 'completed') }
|
134
|
+
scope :recent, -> { where('created_at > ?', 7.days.ago) }
|
135
|
+
scope :high_value, -> { where('total > ?', 500) }
|
136
|
+
end
|
137
|
+
```
|
138
|
+
|
139
|
+
When prompted: *"Show me high-value customers from the last month"*
|
140
|
+
|
141
|
+
Sage understands:
|
142
|
+
- "high-value customers" → Use the `high_value` scope pattern from User model
|
143
|
+
- "last month" → Similar to the `recent` scope pattern, adjust the time range
|
144
|
+
- Combines these patterns to generate accurate SQL with proper JOINs and aggregations
|
145
|
+
|
146
|
+
### Benefits
|
147
|
+
|
148
|
+
1. **Business Logic Awareness**: Scopes encode your business rules (what makes a customer "active" or "high-value")
|
149
|
+
2. **Correct JOIN Patterns**: Scopes show the proper way to join tables in your application
|
150
|
+
3. **Aggregation Patterns**: Complex scopes with GROUP BY and HAVING clauses guide report generation
|
151
|
+
4. **Consistency**: Generated queries follow the same patterns as your application code
|
152
|
+
|
153
|
+
Scopes now serve dual purposes:
|
154
|
+
1. Reusable query logic in your Rails application
|
155
|
+
2. Documentation for report generation
|
156
|
+
|
157
|
+
## Development
|
158
|
+
|
159
|
+
After checking out the repo:
|
160
|
+
|
161
|
+
```bash
|
162
|
+
$ bundle install
|
163
|
+
$ cd test/dummy
|
164
|
+
$ rails db:create
|
165
|
+
$ rails db:migrate
|
166
|
+
$ rails server
|
167
|
+
```
|
168
|
+
|
169
|
+
Visit http://localhost:3000/sage to see the engine in action.
|
170
|
+
|
171
|
+
## Testing
|
172
|
+
|
173
|
+
Run the test suite:
|
174
|
+
|
175
|
+
```bash
|
176
|
+
$ rails test
|
177
|
+
```
|
178
|
+
|
179
|
+
## Troubleshooting
|
180
|
+
|
181
|
+
### API Key Issues
|
182
|
+
- Verify your API key is correctly set in credentials or environment variables
|
183
|
+
- Check Rails logs for authentication errors
|
184
|
+
- Ensure your API key has appropriate permissions
|
185
|
+
|
186
|
+
### Query Generation Issues
|
187
|
+
- Verify database schema is being loaded (check logs for schema context)
|
188
|
+
- Ensure model files are in standard Rails locations (`app/models/`)
|
189
|
+
- Check that Blazer is properly configured and can execute queries
|
190
|
+
|
191
|
+
### Performance
|
192
|
+
- Use lighter models (Claude Haiku, GPT-3.5) for faster response times
|
193
|
+
- Consider caching frequently used queries
|
194
|
+
- Scope context is cached per request to minimize processing
|
195
|
+
|
196
|
+
## Contributing
|
197
|
+
|
198
|
+
Bug reports and pull requests are welcome on GitHub. This project is intended to be a safe, welcoming space for collaboration.
|
199
|
+
|
200
|
+
## License
|
201
|
+
|
202
|
+
The gem is available as open source under the terms of the [MIT License](https://opensource.org/licenses/MIT).
|
@@ -0,0 +1 @@
|
|
1
|
+
<svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24" fill="none" stroke="#71717a" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" class="lucide lucide-chevron-down-icon lucide-chevron-down"><path d="m6 9 6 6 6-6"/></svg>
|
@@ -0,0 +1 @@
|
|
1
|
+
<svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" class="lucide lucide-chevron-right-icon lucide-chevron-right"><path d="m9 18 6-6-6-6"/></svg>
|
@@ -0,0 +1,4 @@
|
|
1
|
+
<svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24" fill="none">
|
2
|
+
<circle opacity=".25" cx="12" cy="12" r="10" stroke="currentColor" stroke-width="4"></circle>
|
3
|
+
<path opacity=".75" fill="currentColor" d="M4 12a8 8 0 018-8V0C5.373 0 0 5.373 0 12h4zm2 5.291A7.962 7.962 0 014 12H0c0 3.042 1.135 5.824 3 7.938l3-2.647z"></path>
|
4
|
+
</svg>
|
@@ -0,0 +1 @@
|
|
1
|
+
<svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24" fill="none" stroke="#71717a" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" class="lucide lucide-chevron-down-icon lucide-chevron-down"><path d="m6 9 6 6 6-6"/></svg>
|
@@ -0,0 +1 @@
|
|
1
|
+
<svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" class="lucide lucide-chevron-right-icon lucide-chevron-right"><path d="m9 18 6-6-6-6"/></svg>
|
@@ -0,0 +1,4 @@
|
|
1
|
+
<svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24" fill="none">
|
2
|
+
<circle opacity=".25" cx="12" cy="12" r="10" stroke="currentColor" stroke-width="4"></circle>
|
3
|
+
<path opacity=".75" fill="currentColor" d="M4 12a8 8 0 018-8V0C5.373 0 0 5.373 0 12h4zm2 5.291A7.962 7.962 0 014 12H0c0 3.042 1.135 5.824 3 7.938l3-2.647z"></path>
|
4
|
+
</svg>
|
@@ -0,0 +1,18 @@
|
|
1
|
+
// Sage application JavaScript
|
2
|
+
// Configure your import map in config/importmap.rb. Read more: https://github.com/rails/importmap-rails
|
3
|
+
import "@hotwired/turbo-rails"
|
4
|
+
import { Application } from "@hotwired/stimulus"
|
5
|
+
|
6
|
+
// Import and register Sage controllers
|
7
|
+
import { SearchController, ClipboardController, SelectController, DashboardController, ReverseInfiniteScrollController } from "sage"
|
8
|
+
|
9
|
+
const application = Application.start()
|
10
|
+
application.debug = true
|
11
|
+
window.Stimulus = application
|
12
|
+
|
13
|
+
// Register controllers
|
14
|
+
application.register("sage--search", SearchController)
|
15
|
+
application.register("sage--clipboard", ClipboardController)
|
16
|
+
application.register("sage--select", SelectController)
|
17
|
+
application.register("sage--dashboard", DashboardController)
|
18
|
+
application.register("sage--reverse-infinite-scroll", ReverseInfiniteScrollController)
|
@@ -0,0 +1,308 @@
|
|
1
|
+
/*
|
2
|
+
* This is a manifest file that'll be compiled into application.css, which will include all the files
|
3
|
+
* listed below.
|
4
|
+
*
|
5
|
+
* Any CSS and SCSS file within this directory, lib/assets/stylesheets, vendor/assets/stylesheets,
|
6
|
+
* or any plugin's vendor/assets/stylesheets directory can be referenced here using a relative path.
|
7
|
+
*
|
8
|
+
* You're free to add application-wide styles to this file and they'll appear at the bottom of the
|
9
|
+
* compiled file so the styles you add here take precedence over styles defined in any other CSS/SCSS
|
10
|
+
* files in this directory. Styles in this file should be added after the last require_* statement.
|
11
|
+
* It is generally better to create a new file per style scope.
|
12
|
+
*
|
13
|
+
*= require_tree .
|
14
|
+
*= require_self
|
15
|
+
*/
|
16
|
+
|
17
|
+
/* select container */
|
18
|
+
.select-container {
|
19
|
+
position: relative;
|
20
|
+
}
|
21
|
+
|
22
|
+
.select-dropdown {
|
23
|
+
position: absolute;
|
24
|
+
top: 100%;
|
25
|
+
left: 0;
|
26
|
+
right: 0;
|
27
|
+
background: var(--surface-container);
|
28
|
+
border: 1px solid var(--outline);
|
29
|
+
border-radius: var(--small);
|
30
|
+
max-height: 200px;
|
31
|
+
overflow-y: auto;
|
32
|
+
z-index: 1000;
|
33
|
+
box-shadow: var(--elevation-2);
|
34
|
+
margin-top: 4px;
|
35
|
+
}
|
36
|
+
|
37
|
+
.select-dropdown.hidden {
|
38
|
+
display: none;
|
39
|
+
}
|
40
|
+
|
41
|
+
.select-dropdown.visible {
|
42
|
+
display: block;
|
43
|
+
}
|
44
|
+
|
45
|
+
.select-option {
|
46
|
+
padding: 12px 16px;
|
47
|
+
cursor: pointer;
|
48
|
+
color: var(--on-surface);
|
49
|
+
transition: background-color 0.2s;
|
50
|
+
}
|
51
|
+
|
52
|
+
.select-option:hover,
|
53
|
+
.select-option.active {
|
54
|
+
background-color: var(--surface-container-high);
|
55
|
+
}
|
56
|
+
|
57
|
+
.select-option.no-results {
|
58
|
+
color: var(--on-surface-variant);
|
59
|
+
cursor: default;
|
60
|
+
}
|
61
|
+
|
62
|
+
.select-option.no-results:hover {
|
63
|
+
background-color: transparent;
|
64
|
+
}
|
65
|
+
|
66
|
+
|
67
|
+
/* blazer/github.css */
|
68
|
+
.hljs {
|
69
|
+
display: block;
|
70
|
+
color: #333;
|
71
|
+
}
|
72
|
+
|
73
|
+
.hljs-comment,
|
74
|
+
.hljs-template_comment,
|
75
|
+
.diff .hljs-header,
|
76
|
+
.hljs-javadoc {
|
77
|
+
color: #998;
|
78
|
+
font-style: italic
|
79
|
+
}
|
80
|
+
|
81
|
+
.hljs-keyword,
|
82
|
+
.css .rule .hljs-keyword,
|
83
|
+
.hljs-winutils,
|
84
|
+
.javascript .hljs-title,
|
85
|
+
.nginx .hljs-title,
|
86
|
+
.hljs-subst,
|
87
|
+
.hljs-request,
|
88
|
+
.hljs-status {
|
89
|
+
color: #333;
|
90
|
+
font-weight: bold
|
91
|
+
}
|
92
|
+
|
93
|
+
.hljs-number,
|
94
|
+
.hljs-hexcolor,
|
95
|
+
.ruby .hljs-constant {
|
96
|
+
color: #099;
|
97
|
+
}
|
98
|
+
|
99
|
+
.hljs-string,
|
100
|
+
.hljs-tag .hljs-value,
|
101
|
+
.hljs-phpdoc,
|
102
|
+
.tex .hljs-formula {
|
103
|
+
color: #d14
|
104
|
+
}
|
105
|
+
|
106
|
+
.hljs-title,
|
107
|
+
.hljs-id,
|
108
|
+
.coffeescript .hljs-params,
|
109
|
+
.scss .hljs-preprocessor {
|
110
|
+
color: #900;
|
111
|
+
font-weight: bold
|
112
|
+
}
|
113
|
+
|
114
|
+
.javascript .hljs-title,
|
115
|
+
.lisp .hljs-title,
|
116
|
+
.clojure .hljs-title,
|
117
|
+
.hljs-subst {
|
118
|
+
font-weight: normal
|
119
|
+
}
|
120
|
+
|
121
|
+
.hljs-class .hljs-title,
|
122
|
+
.haskell .hljs-type,
|
123
|
+
.vhdl .hljs-literal,
|
124
|
+
.tex .hljs-command {
|
125
|
+
color: #458;
|
126
|
+
font-weight: bold
|
127
|
+
}
|
128
|
+
|
129
|
+
.hljs-tag,
|
130
|
+
.hljs-tag .hljs-title,
|
131
|
+
.hljs-rules .hljs-property,
|
132
|
+
.django .hljs-tag .hljs-keyword {
|
133
|
+
color: #000080;
|
134
|
+
font-weight: normal
|
135
|
+
}
|
136
|
+
|
137
|
+
.hljs-attribute,
|
138
|
+
.lisp .hljs-body {
|
139
|
+
color: #008080
|
140
|
+
}
|
141
|
+
|
142
|
+
.hljs-variable,
|
143
|
+
.hljs-regexp {
|
144
|
+
color: #009926;
|
145
|
+
font-weight: bold
|
146
|
+
}
|
147
|
+
|
148
|
+
.hljs-symbol,
|
149
|
+
.ruby .hljs-symbol .hljs-string,
|
150
|
+
.lisp .hljs-keyword,
|
151
|
+
.tex .hljs-special,
|
152
|
+
.hljs-prompt {
|
153
|
+
color: #990073
|
154
|
+
}
|
155
|
+
|
156
|
+
.hljs-built_in,
|
157
|
+
.lisp .hljs-title,
|
158
|
+
.clojure .hljs-built_in {
|
159
|
+
color: #0086b3
|
160
|
+
}
|
161
|
+
|
162
|
+
.hljs-preprocessor,
|
163
|
+
.hljs-pragma,
|
164
|
+
.hljs-pi,
|
165
|
+
.hljs-doctype,
|
166
|
+
.hljs-shebang,
|
167
|
+
.hljs-cdata {
|
168
|
+
color: #999;
|
169
|
+
font-weight: bold
|
170
|
+
}
|
171
|
+
|
172
|
+
.hljs-deletion {
|
173
|
+
background: #fdd
|
174
|
+
}
|
175
|
+
|
176
|
+
.hljs-addition {
|
177
|
+
background: #dfd
|
178
|
+
}
|
179
|
+
|
180
|
+
.diff .hljs-change {
|
181
|
+
background: #0086b3
|
182
|
+
}
|
183
|
+
|
184
|
+
.hljs-chunk {
|
185
|
+
color: #aaa
|
186
|
+
}
|
187
|
+
|
188
|
+
/* Dark mode styles for syntax highlighting */
|
189
|
+
body.dark .hljs {
|
190
|
+
display: block;
|
191
|
+
color: #e6e6e6;
|
192
|
+
}
|
193
|
+
|
194
|
+
body.dark .hljs-comment,
|
195
|
+
body.dark .hljs-template_comment,
|
196
|
+
body.dark .diff .hljs-header,
|
197
|
+
body.dark .hljs-javadoc {
|
198
|
+
color: #8b8b8b;
|
199
|
+
font-style: italic;
|
200
|
+
}
|
201
|
+
|
202
|
+
body.dark .hljs-keyword,
|
203
|
+
body.dark .css .rule .hljs-keyword,
|
204
|
+
body.dark .hljs-winutils,
|
205
|
+
body.dark .javascript .hljs-title,
|
206
|
+
body.dark .nginx .hljs-title,
|
207
|
+
body.dark .hljs-subst,
|
208
|
+
body.dark .hljs-request,
|
209
|
+
body.dark .hljs-status {
|
210
|
+
color: #e6e6e6;
|
211
|
+
font-weight: bold;
|
212
|
+
}
|
213
|
+
|
214
|
+
body.dark .hljs-number,
|
215
|
+
body.dark .hljs-hexcolor,
|
216
|
+
body.dark .ruby .hljs-constant {
|
217
|
+
color: #6dd3ce;
|
218
|
+
}
|
219
|
+
|
220
|
+
body.dark .hljs-string,
|
221
|
+
body.dark .hljs-tag .hljs-value,
|
222
|
+
body.dark .hljs-phpdoc,
|
223
|
+
body.dark .tex .hljs-formula {
|
224
|
+
color: #87d068;
|
225
|
+
}
|
226
|
+
|
227
|
+
body.dark .hljs-title,
|
228
|
+
body.dark .hljs-id,
|
229
|
+
body.dark .coffeescript .hljs-params,
|
230
|
+
body.dark .scss .hljs-preprocessor {
|
231
|
+
color: #ffa657;
|
232
|
+
font-weight: bold;
|
233
|
+
}
|
234
|
+
|
235
|
+
body.dark .javascript .hljs-title,
|
236
|
+
body.dark .lisp .hljs-title,
|
237
|
+
body.dark .clojure .hljs-title,
|
238
|
+
body.dark .hljs-subst {
|
239
|
+
font-weight: normal;
|
240
|
+
}
|
241
|
+
|
242
|
+
body.dark .hljs-class .hljs-title,
|
243
|
+
body.dark .haskell .hljs-type,
|
244
|
+
body.dark .vhdl .hljs-literal,
|
245
|
+
body.dark .tex .hljs-command {
|
246
|
+
color: #61afef;
|
247
|
+
font-weight: bold;
|
248
|
+
}
|
249
|
+
|
250
|
+
body.dark .hljs-tag,
|
251
|
+
body.dark .hljs-tag .hljs-title,
|
252
|
+
body.dark .hljs-rules .hljs-property,
|
253
|
+
body.dark .django .hljs-tag .hljs-keyword {
|
254
|
+
color: #e06c75;
|
255
|
+
font-weight: normal;
|
256
|
+
}
|
257
|
+
|
258
|
+
body.dark .hljs-attribute,
|
259
|
+
body.dark .lisp .hljs-body {
|
260
|
+
color: #56b6c2;
|
261
|
+
}
|
262
|
+
|
263
|
+
body.dark .hljs-variable,
|
264
|
+
body.dark .hljs-regexp {
|
265
|
+
color: #98c379;
|
266
|
+
font-weight: bold;
|
267
|
+
}
|
268
|
+
|
269
|
+
body.dark .hljs-symbol,
|
270
|
+
body.dark .ruby .hljs-symbol .hljs-string,
|
271
|
+
body.dark .lisp .hljs-keyword,
|
272
|
+
body.dark .tex .hljs-special,
|
273
|
+
body.dark .hljs-prompt {
|
274
|
+
color: #c678dd;
|
275
|
+
}
|
276
|
+
|
277
|
+
body.dark .hljs-built_in,
|
278
|
+
body.dark .lisp .hljs-title,
|
279
|
+
body.dark .clojure .hljs-built_in {
|
280
|
+
color: #61afef;
|
281
|
+
}
|
282
|
+
|
283
|
+
body.dark .hljs-preprocessor,
|
284
|
+
body.dark .hljs-pragma,
|
285
|
+
body.dark .hljs-pi,
|
286
|
+
body.dark .hljs-doctype,
|
287
|
+
body.dark .hljs-shebang,
|
288
|
+
body.dark .hljs-cdata {
|
289
|
+
color: #7c7c7c;
|
290
|
+
font-weight: bold;
|
291
|
+
}
|
292
|
+
|
293
|
+
body.dark .hljs-deletion {
|
294
|
+
background: #5c2626;
|
295
|
+
}
|
296
|
+
|
297
|
+
body.dark .hljs-addition {
|
298
|
+
background: #2b5c2b;
|
299
|
+
}
|
300
|
+
|
301
|
+
body.dark .diff .hljs-change {
|
302
|
+
background: #1e4f6b;
|
303
|
+
}
|
304
|
+
|
305
|
+
body.dark .hljs-chunk {
|
306
|
+
color: #848484;
|
307
|
+
}
|
308
|
+
|
@@ -0,0 +1,65 @@
|
|
1
|
+
module Sage
|
2
|
+
class ChecksController < BaseController
|
3
|
+
before_action :set_check, only: [ :edit, :update, :destroy, :run ]
|
4
|
+
|
5
|
+
def index
|
6
|
+
@q = Blazer::Check.ransack(params[:q])
|
7
|
+
@checks = @q.result.joins(:query).includes(:query)
|
8
|
+
|
9
|
+
# Apply basic ordering first
|
10
|
+
@checks = @checks.order("blazer_queries.name, blazer_checks.id")
|
11
|
+
|
12
|
+
# Apply pagination with Pagy
|
13
|
+
@pagy, @checks = pagy(@checks)
|
14
|
+
|
15
|
+
# Apply state-based sorting on the paginated results
|
16
|
+
state_order = [ nil, "disabled", "error", "timed out", "failing", "passing" ]
|
17
|
+
@checks = @checks.sort_by { |q| state_order.index(q.state) || 99 }
|
18
|
+
end
|
19
|
+
|
20
|
+
def new
|
21
|
+
@check = Blazer::Check.new(query_id: params[:query_id])
|
22
|
+
end
|
23
|
+
|
24
|
+
def create
|
25
|
+
@check = Blazer::Check.new(check_params)
|
26
|
+
# use creator_id instead of creator
|
27
|
+
# since we setup association without checking if column exists
|
28
|
+
@check.creator = blazer_user if @check.respond_to?(:creator_id=) && blazer_user
|
29
|
+
|
30
|
+
if @check.save
|
31
|
+
redirect_to query_path(@check.query)
|
32
|
+
else
|
33
|
+
render_errors @check
|
34
|
+
end
|
35
|
+
end
|
36
|
+
|
37
|
+
def update
|
38
|
+
if @check.update(check_params)
|
39
|
+
redirect_to query_path(@check.query)
|
40
|
+
else
|
41
|
+
render_errors @check
|
42
|
+
end
|
43
|
+
end
|
44
|
+
|
45
|
+
def destroy
|
46
|
+
@check.destroy
|
47
|
+
redirect_to checks_path
|
48
|
+
end
|
49
|
+
|
50
|
+
def run
|
51
|
+
@query = @check.query
|
52
|
+
redirect_to query_path(@query)
|
53
|
+
end
|
54
|
+
|
55
|
+
private
|
56
|
+
|
57
|
+
def check_params
|
58
|
+
params.require(:check).permit(:query_id, :emails, :slack_channels, :invert, :check_type, :schedule)
|
59
|
+
end
|
60
|
+
|
61
|
+
def set_check
|
62
|
+
@check = Blazer::Check.find(params[:id])
|
63
|
+
end
|
64
|
+
end
|
65
|
+
end
|