selma 0.4.15 → 0.5.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.
@@ -5,7 +5,7 @@ use lol_html::{
5
5
  Settings,
6
6
  };
7
7
  use magnus::{
8
- exception, function, gc, method,
8
+ function, gc, method,
9
9
  r_hash::ForEach,
10
10
  scan_args,
11
11
  typed_data::Obj,
@@ -34,7 +34,11 @@ use crate::{
34
34
  #[derive(Clone)]
35
35
  pub struct Handler {
36
36
  rb_handler: Opaque<Value>,
37
- rb_selector: Opaque<Obj<SelmaSelector>>,
37
+ // Store the selector's data by value (Rust-owned). The `#selector` method may return a
38
+ // freshly-built `Selma::Selector` that nothing else on the Ruby side references; keeping a
39
+ // `Obj`/`Opaque` handle to it would make its lifetime depend on GC (use-after-free if it is
40
+ // collected while the Rewriter is alive). We only ever read Rust data off it, so clone it.
41
+ selector: SelmaSelector,
38
42
  // total_element_handler_calls: usize,
39
43
  // total_elapsed_element_handlers: f64,
40
44
 
@@ -43,7 +47,8 @@ pub struct Handler {
43
47
  }
44
48
 
45
49
  struct RewriterOptions {
46
- memory_options: MemorySettings,
50
+ max_allowed_memory_usage: usize,
51
+ preallocated_parsing_buffer_size: usize,
47
52
  }
48
53
 
49
54
  pub struct Rewriter {
@@ -84,12 +89,13 @@ impl SelmaRewriter {
84
89
  /// @return [Selma::Rewriter]
85
90
  fn new(args: &[Value]) -> Result<Self, magnus::Error> {
86
91
  let (rb_sanitizer, rb_handlers, rb_options) = Self::scan_parse_args(args)?;
92
+ let ruby = Ruby::get().unwrap();
87
93
 
88
94
  let sanitizer = match rb_sanitizer {
89
95
  None => {
90
96
  // no `sanitizer:` kwarg provided, use default
91
97
  let default_sanitizer = SelmaSanitizer::new(&[])?;
92
- let wrapped_sanitizer = Obj::wrap(default_sanitizer);
98
+ let wrapped_sanitizer = ruby.obj_wrap(default_sanitizer);
93
99
  // wrapped_sanitizer.funcall::<&str, (), Value>("setup", ())?;
94
100
  Some(wrapped_sanitizer.deref().to_owned())
95
101
  }
@@ -106,7 +112,7 @@ impl SelmaRewriter {
106
112
  if !rb_handler.respond_to("selector", true).unwrap() {
107
113
  let classname = unsafe { rb_handler.classname() };
108
114
  return Err(magnus::Error::new(
109
- exception::no_method_error(),
115
+ ruby.exception_no_method_error(),
110
116
  format!(
111
117
  "Could not call #selector on {classname:?}; is this an object that defines it?",
112
118
 
@@ -117,7 +123,7 @@ impl SelmaRewriter {
117
123
  let rb_selector: Obj<SelmaSelector> = match rb_handler.funcall("selector", ()) {
118
124
  Err(err) => {
119
125
  return Err(magnus::Error::new(
120
- exception::type_error(),
126
+ ruby.exception_type_error(),
121
127
  format!("Error instantiating selector: {err:?}"),
122
128
  ));
123
129
  }
@@ -125,7 +131,9 @@ impl SelmaRewriter {
125
131
  };
126
132
  let handler = Handler {
127
133
  rb_handler: Opaque::from(rb_handler),
128
- rb_selector: Opaque::from(rb_selector),
134
+ // clone the selector's data out of the Ruby object right away so the
135
+ // Handler no longer depends on that object surviving GC (see struct docs)
136
+ selector: (*rb_selector).clone(),
129
137
  // total_element_handler_calls: 0,
130
138
  // total_elapsed_element_handlers: 0.0,
131
139
 
@@ -140,7 +148,7 @@ impl SelmaRewriter {
140
148
 
141
149
  if sanitizer.is_none() && handlers.is_empty() {
142
150
  return Err(magnus::Error::new(
143
- exception::arg_error(),
151
+ ruby.exception_arg_error(),
144
152
  "Must provide a sanitizer or a handler",
145
153
  ));
146
154
  }
@@ -151,58 +159,55 @@ impl SelmaRewriter {
151
159
  None => {}
152
160
  Some(options) => {
153
161
  options.foreach(|key: Symbol, value: RHash| {
162
+ let ruby = Ruby::get().unwrap();
154
163
  let key = key.to_string();
155
164
  match key.as_str() {
156
165
  "memory" => {
157
- let max_allowed_memory_usage = value.get(Symbol::new("max_allowed_memory_usage"));
158
- if max_allowed_memory_usage.is_some() {
159
- let max_allowed_memory_usage = max_allowed_memory_usage.unwrap();
166
+ if let Some(max_allowed_memory_usage) = value.get(ruby.to_symbol("max_allowed_memory_usage")) {
160
167
  let max_allowed_memory_usage =
161
168
  Integer::from_value(max_allowed_memory_usage);
162
- if max_allowed_memory_usage.is_some() {
163
- match max_allowed_memory_usage.unwrap().to_u64() {
169
+ if let Some(max_allowed_memory_usage) = max_allowed_memory_usage {
170
+ match max_allowed_memory_usage.to_u64() {
164
171
  Ok(max_allowed_memory_usage) => {
165
- rewriter_options.memory_options.max_allowed_memory_usage =
172
+ rewriter_options.max_allowed_memory_usage =
166
173
  max_allowed_memory_usage as usize;
167
174
  }
168
175
  Err(_e) => {
169
176
  return Err(magnus::Error::new(
170
- exception::arg_error(),
177
+ ruby.exception_arg_error(),
171
178
  "max_allowed_memory_usage must be a positive integer",
172
179
  ));
173
180
  }
174
181
  }
175
182
  } else {
176
- rewriter_options.memory_options.max_allowed_memory_usage = MemorySettings::default().max_allowed_memory_usage;
183
+ rewriter_options.max_allowed_memory_usage = RewriterOptions::DEFAULT_MAX_ALLOWED_MEMORY_USAGE;
177
184
  }
178
185
  }
179
186
 
180
- let preallocated_parsing_buffer_size = value.get(Symbol::new("preallocated_parsing_buffer_size"));
181
- if preallocated_parsing_buffer_size.is_some() {
182
- let preallocated_parsing_buffer_size = preallocated_parsing_buffer_size.unwrap();
187
+ if let Some(preallocated_parsing_buffer_size) = value.get(ruby.to_symbol("preallocated_parsing_buffer_size")) {
183
188
  let preallocated_parsing_buffer_size =
184
189
  Integer::from_value(preallocated_parsing_buffer_size);
185
- if preallocated_parsing_buffer_size.is_some() {
186
- match preallocated_parsing_buffer_size.unwrap().to_u64() {
190
+ if let Some(preallocated_parsing_buffer_size) = preallocated_parsing_buffer_size {
191
+ match preallocated_parsing_buffer_size.to_u64() {
187
192
  Ok(preallocated_parsing_buffer_size) => {
188
- rewriter_options.memory_options.preallocated_parsing_buffer_size =
193
+ rewriter_options.preallocated_parsing_buffer_size =
189
194
  preallocated_parsing_buffer_size as usize;
190
195
  }
191
196
  Err(_e) => {
192
197
  return Err(magnus::Error::new(
193
- exception::arg_error(),
198
+ ruby.exception_arg_error(),
194
199
  "preallocated_parsing_buffer_size must be a positive integer",
195
200
  ));
196
201
  }
197
202
  }
198
203
  } else {
199
- rewriter_options.memory_options.preallocated_parsing_buffer_size = MemorySettings::default().preallocated_parsing_buffer_size;
204
+ rewriter_options.preallocated_parsing_buffer_size = RewriterOptions::DEFAULT_PREALLOCATED_PARSING_BUFFER_SIZE;
200
205
  }
201
206
  }
202
207
  }
203
208
  _ => {
204
209
  return Err(magnus::Error::new(
205
- exception::arg_error(),
210
+ ruby.exception_arg_error(),
206
211
  format!("Unknown option: {key:?}"),
207
212
  ));
208
213
  }
@@ -212,13 +217,11 @@ impl SelmaRewriter {
212
217
  }
213
218
  }
214
219
 
215
- if rewriter_options
216
- .memory_options
217
- .preallocated_parsing_buffer_size
218
- > rewriter_options.memory_options.max_allowed_memory_usage
220
+ if rewriter_options.preallocated_parsing_buffer_size
221
+ > rewriter_options.max_allowed_memory_usage
219
222
  {
220
223
  return Err(magnus::Error::new(
221
- exception::arg_error(),
224
+ ruby.exception_arg_error(),
222
225
  "max_allowed_memory_usage must be greater than preallocated_parsing_buffer_size",
223
226
  ));
224
227
  }
@@ -305,7 +308,7 @@ impl SelmaRewriter {
305
308
  None => match String::from_utf8(rewritten_html) {
306
309
  Ok(output) => Ok(output),
307
310
  Err(err) => Err(magnus::Error::new(
308
- exception::runtime_error(),
311
+ Ruby::get().unwrap().exception_runtime_error(),
309
312
  format!("{err:?}"),
310
313
  )),
311
314
  },
@@ -342,7 +345,7 @@ impl SelmaRewriter {
342
345
  Ok(rewritten_html) => match String::from_utf8(rewritten_html) {
343
346
  Ok(output) => Ok(output),
344
347
  Err(err) => Err(magnus::Error::new(
345
- exception::runtime_error(),
348
+ Ruby::get().unwrap().exception_runtime_error(),
346
349
  format!("{err:?}"),
347
350
  )),
348
351
  },
@@ -366,55 +369,42 @@ impl SelmaRewriter {
366
369
  handlers.iter().for_each(|handler| {
367
370
  let element_stack: Rc<RefCell<Vec<String>>> = Rc::new(RefCell::new(vec![]));
368
371
 
369
- let ruby = Ruby::get().unwrap();
370
-
371
- let selector = ruby.get_inner(handler.rb_selector);
372
+ let selector = &handler.selector;
372
373
 
373
374
  // TODO: test final raise by simulating errors
374
- if selector.match_element().is_some() {
375
+ if let Some(match_element) = selector.match_element() {
375
376
  let closure_element_stack = element_stack.clone();
376
377
 
377
- element_content_handlers.push(element!(
378
- selector.match_element().unwrap(),
379
- move |el| {
380
- match Self::process_element_handlers(
381
- handler,
382
- el,
383
- &closure_element_stack.borrow(),
384
- ) {
385
- Ok(_) => Ok(()),
386
- Err(err) => Err(err.to_string().into()),
387
- }
378
+ element_content_handlers.push(element!(match_element, move |el| {
379
+ match Self::process_element_handlers(
380
+ handler,
381
+ el,
382
+ &closure_element_stack.borrow(),
383
+ ) {
384
+ Ok(_) => Ok(()),
385
+ Err(err) => Err(err.to_string().into()),
388
386
  }
389
- ));
387
+ }));
390
388
  }
391
389
 
392
- if selector.match_text_within().is_some() {
390
+ if let Some(match_text_within) = selector.match_text_within() {
393
391
  let closure_element_stack = element_stack.clone();
394
392
 
395
- element_content_handlers.push(text!(
396
- selector.match_text_within().unwrap(),
397
- move |text| {
398
- let element_stack = closure_element_stack.as_ref().borrow();
399
- if selector.ignore_text_within().is_some() {
400
- // check if current tag is a tag we should be ignoring text within;
401
- // also checks if tag is within an ancestery of ignored tags
402
- if selector
403
- .ignore_text_within()
404
- .unwrap()
405
- .iter()
406
- .any(|t| element_stack.contains(t))
407
- {
408
- return Ok(());
409
- }
393
+ element_content_handlers.push(text!(match_text_within, move |text| {
394
+ let element_stack = closure_element_stack.as_ref().borrow();
395
+ // check if current tag is a tag we should be ignoring text within;
396
+ // also checks if tag is within an ancestery of ignored tags
397
+ if let Some(ignore_text_within) = selector.ignore_text_within() {
398
+ if ignore_text_within.iter().any(|t| element_stack.contains(t)) {
399
+ return Ok(());
410
400
  }
401
+ }
411
402
 
412
- match Self::process_text_handlers(handler, text) {
413
- Ok(_) => Ok(()),
414
- Err(err) => Err(err.to_string().into()),
415
- }
403
+ match Self::process_text_handlers(handler, text) {
404
+ Ok(_) => Ok(()),
405
+ Err(err) => Err(err.to_string().into()),
416
406
  }
417
- ));
407
+ }));
418
408
  }
419
409
 
420
410
  // we need to check *every* element we iterate over, to create a stack of elements
@@ -430,14 +420,12 @@ impl SelmaRewriter {
430
420
 
431
421
  let closure_element_stack = element_stack.clone();
432
422
 
433
- if let Some(end_tag_handlers) = el.end_tag_handlers() {
434
- end_tag_handlers.push(lol_html::EndTagHandler::into(Box::new(
435
- move |_end_tag| {
436
- closure_element_stack.as_ref().borrow_mut().pop();
437
- Ok(())
438
- },
439
- )));
440
- }
423
+ let handler: lol_html::EndTagHandler<'static> = Box::new(move |_end_tag| {
424
+ closure_element_stack.as_ref().borrow_mut().pop();
425
+ Ok(())
426
+ });
427
+ // ignore void elements (lol_html's void list may differ from selma's `self_closing`)
428
+ let _ = el.on_end_tag(handler);
441
429
 
442
430
  Ok(())
443
431
  }));
@@ -460,20 +448,21 @@ impl SelmaRewriter {
460
448
  let binding = &self.0.borrow();
461
449
  let mut output = vec![];
462
450
  {
463
- let mut rewriter = HtmlRewriter::new(
464
- Settings {
465
- document_content_handlers,
466
- element_content_handlers,
467
- memory_settings: Self::get_memory_options(binding),
468
- ..Settings::default()
469
- },
470
- |c: &[u8]| output.extend_from_slice(c),
471
- );
451
+ let mut settings =
452
+ Settings::new().with_memory_settings(Self::get_memory_options(binding));
453
+ for handler in document_content_handlers {
454
+ settings = settings.append_document_content_handler(handler);
455
+ }
456
+ for handler in element_content_handlers {
457
+ settings = settings.append_element_content_handler(handler);
458
+ }
459
+
460
+ let mut rewriter = HtmlRewriter::new(settings, |c: &[u8]| output.extend_from_slice(c));
472
461
  match rewriter.write(html) {
473
462
  Ok(_) => {}
474
463
  Err(err) => {
475
464
  return Err(magnus::Error::new(
476
- exception::runtime_error(),
465
+ Ruby::get().unwrap().exception_runtime_error(),
477
466
  format!("{err:?}"),
478
467
  ));
479
468
  }
@@ -487,15 +476,13 @@ impl SelmaRewriter {
487
476
  element: &mut Element,
488
477
  ancestors: &[String],
489
478
  ) -> Result<(), magnus::Error> {
490
- let rb_handler = handler.rb_handler.into_value();
479
+ let ruby = Ruby::get().unwrap();
480
+ let rb_handler = handler.rb_handler.into_value_with(&ruby);
491
481
 
492
482
  // if `on_end_tag` function is defined, call it
493
483
  if rb_handler.respond_to(Self::SELMA_ON_END_TAG, true).unwrap() {
494
- // TODO: error here is an "EndTagError"
495
484
  element
496
- .end_tag_handlers()
497
- .unwrap()
498
- .push(Box::new(move |end_tag| {
485
+ .on_end_tag(Box::new(move |end_tag| {
499
486
  let (ref_wrap, anchor) = NativeRefWrap::wrap(end_tag);
500
487
 
501
488
  let rb_end_tag = SelmaHTMLEndTag::new(ref_wrap);
@@ -509,7 +496,13 @@ impl SelmaRewriter {
509
496
  Ok(_) => Ok(()),
510
497
  Err(err) => Err(err.to_string().into()),
511
498
  }
512
- }));
499
+ }))
500
+ .map_err(|err| {
501
+ magnus::Error::new(
502
+ Ruby::get().unwrap().exception_runtime_error(),
503
+ err.to_string(),
504
+ )
505
+ })?;
513
506
  }
514
507
 
515
508
  let (ref_wrap, anchor) = NativeRefWrap::wrap(element);
@@ -521,7 +514,7 @@ impl SelmaRewriter {
521
514
  match result {
522
515
  Ok(_) => Ok(()),
523
516
  Err(err) => Err(magnus::Error::new(
524
- exception::runtime_error(),
517
+ ruby.exception_runtime_error(),
525
518
  format!("{err:?}"),
526
519
  )),
527
520
  }
@@ -531,7 +524,8 @@ impl SelmaRewriter {
531
524
  handler: &Handler,
532
525
  text_chunk: &mut TextChunk,
533
526
  ) -> Result<(), magnus::Error> {
534
- let rb_handler = handler.rb_handler.into_value();
527
+ let ruby = Ruby::get().unwrap();
528
+ let rb_handler = handler.rb_handler.into_value_with(&ruby);
535
529
 
536
530
  // prevents missing `handle_text_chunk` function
537
531
  let content = text_chunk.as_str();
@@ -553,32 +547,38 @@ impl SelmaRewriter {
553
547
  match result {
554
548
  Ok(_) => Ok(()),
555
549
  Err(err) => Err(magnus::Error::new(
556
- exception::runtime_error(),
550
+ ruby.exception_runtime_error(),
557
551
  format!("{err:?}"),
558
552
  )),
559
553
  }
560
554
  }
561
555
 
562
556
  fn get_memory_options(binding: &Ref<Rewriter>) -> MemorySettings {
563
- let options = &binding.options.memory_options;
564
- MemorySettings {
565
- max_allowed_memory_usage: options.max_allowed_memory_usage,
566
- preallocated_parsing_buffer_size: options.preallocated_parsing_buffer_size,
567
- }
557
+ let options = &binding.options;
558
+ MemorySettings::new()
559
+ .with_max_allowed_memory_usage(options.max_allowed_memory_usage)
560
+ .with_preallocated_parsing_buffer_size(options.preallocated_parsing_buffer_size)
568
561
  }
569
562
  }
570
563
 
571
564
  impl RewriterOptions {
565
+ // Mirrors lol_html's `MemorySettings` defaults, which are no longer exposed as
566
+ // public fields as of lol_html 3.0 (settings are now consuming builders).
567
+ const DEFAULT_MAX_ALLOWED_MEMORY_USAGE: usize = usize::MAX;
568
+ const DEFAULT_PREALLOCATED_PARSING_BUFFER_SIZE: usize = 1024;
569
+
572
570
  pub fn new() -> Self {
573
571
  Self {
574
- memory_options: MemorySettings::default(),
572
+ max_allowed_memory_usage: Self::DEFAULT_MAX_ALLOWED_MEMORY_USAGE,
573
+ preallocated_parsing_buffer_size: Self::DEFAULT_PREALLOCATED_PARSING_BUFFER_SIZE,
575
574
  }
576
575
  }
577
576
  }
578
577
 
579
578
  pub fn init(m_selma: RModule) -> Result<(), magnus::Error> {
579
+ let ruby = Ruby::get().unwrap();
580
580
  let c_rewriter = m_selma
581
- .define_class("Rewriter", magnus::class::object())
581
+ .define_class("Rewriter", ruby.class_object())
582
582
  .expect("cannot define class Selma::Rewriter");
583
583
 
584
584
  c_rewriter.define_singleton_method("new", function!(SelmaRewriter::new, -1))?;
@@ -5,7 +5,7 @@ use lol_html::{
5
5
  html_content::{Comment, ContentType, Doctype, Element, EndTag},
6
6
  };
7
7
  use magnus::{
8
- class, eval, exception, function, method,
8
+ eval, function, method,
9
9
  r_hash::ForEach,
10
10
  scan_args,
11
11
  value::{Opaque, ReprValue},
@@ -125,22 +125,22 @@ impl SelmaSanitizer {
125
125
  allowed_protocols.foreach(|element_name: String, protocols: RHash| {
126
126
  protocols.foreach(|attribute_name: String, protocol_list: Value| {
127
127
  let protocols: RArray;
128
- if protocol_list.is_kind_of(class::array()) {
128
+ if protocol_list.is_kind_of(ruby.class_array()) {
129
129
  protocols = RArray::from_value(protocol_list).unwrap();
130
130
  if protocols.includes(ruby.to_symbol("all")) {
131
131
  return Err(magnus::Error::new(
132
- exception::arg_error(),
132
+ ruby.exception_arg_error(),
133
133
  "`:all` must be passed outside of an array".to_string(),
134
134
  ));
135
135
  }
136
- } else if protocol_list.is_kind_of(class::symbol())
136
+ } else if protocol_list.is_kind_of(ruby.class_symbol())
137
137
  && Symbol::from_value(protocol_list) == eval(":all").unwrap()
138
138
  {
139
- protocols = RArray::new();
139
+ protocols = ruby.ary_new();
140
140
  protocols.push(ruby.to_symbol("all"))?;
141
141
  } else {
142
142
  return Err(magnus::Error::new(
143
- exception::arg_error(),
143
+ ruby.exception_arg_error(),
144
144
  "Protocol list must be an array, or just `:all`".to_string(),
145
145
  ));
146
146
  }
@@ -220,15 +220,15 @@ impl SelmaSanitizer {
220
220
  // end
221
221
  // end
222
222
  if let Some(remove_contents) = config.get(ruby.to_symbol("remove_contents")) {
223
- if remove_contents.is_kind_of(class::true_class())
224
- || remove_contents.is_kind_of(class::false_class())
223
+ if remove_contents.is_kind_of(ruby.class_true_class())
224
+ || remove_contents.is_kind_of(ruby.class_false_class())
225
225
  {
226
226
  Self::set_all_flags(
227
227
  flags,
228
228
  Self::SELMA_SANITIZER_REMOVE_CONTENTS,
229
229
  remove_contents.to_bool(),
230
230
  );
231
- } else if remove_contents.is_kind_of(class::array()) {
231
+ } else if remove_contents.is_kind_of(ruby.class_array()) {
232
232
  let elements = RArray::from_value(remove_contents).unwrap();
233
233
  elements
234
234
  .into_iter()
@@ -245,7 +245,7 @@ impl SelmaSanitizer {
245
245
  });
246
246
  } else {
247
247
  return Err(magnus::Error::new(
248
- exception::arg_error(),
248
+ ruby.exception_arg_error(),
249
249
  "remove_contents must be `true`, `false`, or an array".to_string(),
250
250
  ));
251
251
  }
@@ -354,11 +354,12 @@ impl SelmaSanitizer {
354
354
  attr_name: String,
355
355
  allow_list: RArray,
356
356
  ) {
357
+ let ruby = Ruby::get().unwrap();
357
358
  let protocol_sanitizers = &mut element_sanitizer.protocol_sanitizers.borrow_mut();
358
359
 
359
360
  for allowed_protocol in allow_list.into_iter() {
360
361
  let protocol_list = protocol_sanitizers.get_mut(&attr_name);
361
- if allowed_protocol.is_kind_of(class::string()) {
362
+ if allowed_protocol.is_kind_of(ruby.class_string()) {
362
363
  match protocol_list {
363
364
  None => {
364
365
  protocol_sanitizers
@@ -366,7 +367,7 @@ impl SelmaSanitizer {
366
367
  }
367
368
  Some(protocol_list) => protocol_list.push(allowed_protocol.to_string()),
368
369
  }
369
- } else if allowed_protocol.is_kind_of(class::symbol()) {
370
+ } else if allowed_protocol.is_kind_of(ruby.class_symbol()) {
370
371
  let protocol_config = allowed_protocol.inspect();
371
372
  if protocol_config == ":relative" {
372
373
  match protocol_list {
@@ -685,13 +686,11 @@ impl SelmaSanitizer {
685
686
 
686
687
  fn check_if_end_tag_needs_removal(element: &mut Element) {
687
688
  if element.removed() && !crate::tags::Tag::tag_from_element(element).self_closing {
688
- element
689
- .end_tag_handlers()
690
- .unwrap()
691
- .push(Box::new(move |end| {
692
- Self::remove_end_tag(end);
693
- Ok(())
694
- }));
689
+ // ignore void elements (lol_html's void list may differ from selma's `self_closing`)
690
+ let _ = element.on_end_tag(Box::new(move |end| {
691
+ Self::remove_end_tag(end);
692
+ Ok(())
693
+ }));
695
694
  }
696
695
  }
697
696
 
@@ -710,8 +709,9 @@ impl SelmaSanitizer {
710
709
  }
711
710
 
712
711
  pub fn init(m_selma: RModule) -> Result<(), magnus::Error> {
712
+ let ruby = Ruby::get().unwrap();
713
713
  let c_sanitizer = m_selma
714
- .define_class("Sanitizer", magnus::class::object())
714
+ .define_class("Sanitizer", ruby.class_object())
715
715
  .expect("cannot define class Selma::Sanitizer");
716
716
 
717
717
  c_sanitizer.define_singleton_method("new", function!(SelmaSanitizer::new, -1))?;
@@ -1,4 +1,4 @@
1
- use magnus::{exception, function, scan_args, Error, Module, Object, RModule, Value};
1
+ use magnus::{function, scan_args, Error, Module, Object, RModule, Ruby, Value};
2
2
 
3
3
  #[derive(Clone, Debug)]
4
4
  #[magnus::wrap(class = "Selma::Selector")]
@@ -14,31 +14,30 @@ impl SelmaSelector {
14
14
  fn new(args: &[Value]) -> Result<Self, Error> {
15
15
  let (match_element, match_text_within, rb_ignore_text_within) =
16
16
  Self::scan_parse_args(args)?;
17
+ let ruby = Ruby::get().unwrap();
17
18
 
18
19
  if match_element.is_none() && match_text_within.is_none() {
19
20
  return Err(Error::new(
20
- exception::arg_error(),
21
+ ruby.exception_arg_error(),
21
22
  "Neither `match_element` nor `match_text_within` option given",
22
23
  ));
23
24
  }
24
25
 
25
26
  // FIXME: not excited about this double parse work (`element!` does it too),
26
27
  // but at least we can bail ASAP if the CSS is invalid
27
- if match_element.is_some() {
28
- let css = match_element.as_ref().unwrap();
28
+ if let Some(css) = &match_element {
29
29
  if css.parse::<lol_html::Selector>().is_err() {
30
30
  return Err(Error::new(
31
- exception::arg_error(),
31
+ ruby.exception_arg_error(),
32
32
  format!("Could not parse `match_element` (`{css:?}`) as valid CSS"),
33
33
  ));
34
34
  }
35
35
  }
36
36
 
37
- if match_text_within.is_some() {
38
- let css = match_text_within.as_ref().unwrap();
37
+ if let Some(css) = &match_text_within {
39
38
  if css.parse::<lol_html::Selector>().is_err() {
40
39
  return Err(Error::new(
41
- exception::arg_error(),
40
+ ruby.exception_arg_error(),
42
41
  format!("Could not parse `match_text_within` (`{css:?}`) as valid CSS",),
43
42
  ));
44
43
  }
@@ -88,22 +87,23 @@ impl SelmaSelector {
88
87
  Ok((match_element, match_text_within, rb_ignore_text_within))
89
88
  }
90
89
 
91
- pub fn match_element(&self) -> Option<String> {
92
- self.match_element.clone()
90
+ pub fn match_element(&self) -> Option<&str> {
91
+ self.match_element.as_deref()
93
92
  }
94
93
 
95
- pub fn match_text_within(&self) -> Option<String> {
96
- self.match_text_within.clone()
94
+ pub fn match_text_within(&self) -> Option<&str> {
95
+ self.match_text_within.as_deref()
97
96
  }
98
97
 
99
- pub fn ignore_text_within(&self) -> Option<Vec<String>> {
100
- self.ignore_text_within.clone()
98
+ pub fn ignore_text_within(&self) -> Option<&[String]> {
99
+ self.ignore_text_within.as_deref()
101
100
  }
102
101
  }
103
102
 
104
103
  pub fn init(m_selma: RModule) -> Result<(), Error> {
104
+ let ruby = Ruby::get().unwrap();
105
105
  let c_selector = m_selma
106
- .define_class("Selector", magnus::class::object())
106
+ .define_class("Selector", ruby.class_object())
107
107
  .expect("cannot define class Selma::Selector");
108
108
 
109
109
  c_selector.define_singleton_method("new", function!(SelmaSelector::new, -1))?;
data/lib/selma/version.rb CHANGED
@@ -1,5 +1,5 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  module Selma
4
- VERSION = "0.4.15"
4
+ VERSION = "0.5.1"
5
5
  end
metadata CHANGED
@@ -1,7 +1,7 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: selma
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.4.15
4
+ version: 0.5.1
5
5
  platform: ruby
6
6
  authors:
7
7
  - Garen J. Torikian
@@ -112,7 +112,7 @@ required_rubygems_version: !ruby/object:Gem::Requirement
112
112
  - !ruby/object:Gem::Version
113
113
  version: '3.4'
114
114
  requirements: []
115
- rubygems_version: 4.0.3
115
+ rubygems_version: 4.0.10
116
116
  specification_version: 4
117
117
  summary: Selma selects and matches HTML nodes using CSS rules. Backed by Rust's lol_html
118
118
  parser.