- database.view now raises SimplyBased::UnsupportedView when a declared view carries CouchDB-era options the N1QL translation silently ignored before: :conditions (a JS fragment) and :emit_value, plus CouchDB list functions. Silently dropping :conditions was the worst of these — a 'view :completed, key: :name, conditions: ...' query returned *unfiltered* results with no hint anything was wrong. Covered by unsupported_view_spec. - All column/property names interpolated into N1QL are now routed through quote_ident, which rejects backticks/backslashes. These names come from the model DSL rather than user input, but a smuggled backtick must never become query text. Class names in index predicates go through a proper string literal quoting helper for the same reason. - ensure_index: failures are still swallowed (an index build in progress must not take down queries) but now emit a warning naming the index, because the silent version left every subsequent query on a primary-index scan with no trace. The ensured-indexes bookkeeping is mutex-guarded for multithreaded servers (Puma). - bulk_load / bulk_destroy now actually batch (collection.get_multi / remove_multi — one KV round trip). The README already advertised 'bulk (batched KV)' but the code looped one get/remove per id. Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
35 lines
1.4 KiB
Ruby
35 lines
1.4 KiB
Ruby
require 'spec_helper'
|
|
|
|
class ConditionalViewWidget
|
|
include SimplyBased::Model
|
|
property :name
|
|
property :completed, type: :boolean
|
|
# A CouchDB-era declaration: the :conditions JavaScript fragment cannot be
|
|
# translated to N1QL. Declaring it must stay legal (simply_couch models load
|
|
# unchanged), but *querying* it must fail loudly — the previous behaviour
|
|
# silently dropped the condition and returned every document of the class.
|
|
view :completed_by_name, key: :name, conditions: 'doc.completed === true'
|
|
view :names_only, key: :name, emit_value: :name
|
|
end
|
|
|
|
RSpec.describe 'Untranslatable view declarations' do
|
|
it 'still allows the model to load and plain views to work' do
|
|
ConditionalViewWidget.create(name: 'a', completed: false)
|
|
expect(ConditionalViewWidget.all.size).to eq 1
|
|
end
|
|
|
|
it 'raises UnsupportedView when querying a :conditions view instead of silently ignoring the condition' do
|
|
ConditionalViewWidget.create(name: 'done', completed: true)
|
|
ConditionalViewWidget.create(name: 'done', completed: false)
|
|
expect {
|
|
ConditionalViewWidget.database.view(ConditionalViewWidget.completed_by_name(key: 'done'))
|
|
}.to raise_error(SimplyBased::UnsupportedView, /conditions/)
|
|
end
|
|
|
|
it 'raises UnsupportedView for :emit_value views' do
|
|
expect {
|
|
ConditionalViewWidget.database.view(ConditionalViewWidget.names_only(key: 'x'))
|
|
}.to raise_error(SimplyBased::UnsupportedView, /emit_value/)
|
|
end
|
|
end
|