Search code examples
ruby-on-railsrubysequel

Ruby Sequel MatchData Error "can't express #<MatchData"


I have one variable with number and text concatenated (e.g. "[79511]Rocket"). If this variable does contains numbers inside the brackets, I must store then (numbers only) in one column of my table. I'm validating this with the below code:

enterprise_id = "[79511]Rocket".split(/[\[\]]/x)[1].match(/^(\d)+$/) rescue nil

When I test in with Puts, it works as 79511, fine. But then when I run the code to insert into database like below:

enterprise_id = "[79511]Rocket".split(/[\[\]]/x)[1].match(/^(\d)+$/) rescue nil
insert_ds = DB["INSERT INTO pd_deals ( enterprise_id ) VALUES (?)",  enterprise_id]
insert_ds.insert

The target column is Integer type in table.

It throws an error:

/home/bm93/.rbenv/versions/2.3.1/lib/ruby/gems/2.3.0/gems/sequel-4.34.0/lib/sequel/dataset/sql.rb:1252:in `literal_other_append': can't express #<MatchData "79511" 1:"1"> as a SQL literal (Sequel::Error)
    from /home/bm93/.rbenv/versions/2.3.1/lib/ruby/gems/2.3.0/gems/sequel-4.34.0/lib/sequel/dataset/sql.rb:108:in `literal_append'
    from /home/bm93/.rbenv/versions/2.3.1/lib/ruby/gems/2.3.0/gems/sequel-4.34.0/lib/sequel/dataset/sql.rb:673:in `block in placeholder_literal_string_sql_append'
    from /home/bm93/.rbenv/versions/2.3.1/lib/ruby/gems/2.3.0/gems/sequel-4.34.0/lib/sequel/dataset/sql.rb:670:in `loop'
    from /home/bm93/.rbenv/versions/2.3.1/lib/ruby/gems/2.3.0/gems/sequel-4.34.0/lib/sequel/dataset/sql.rb:670:in `placeholder_literal_string_sql_append'
    from /home/bm93/.rbenv/versions/2.3.1/lib/ruby/gems/2.3.0/gems/sequel-4.34.0/lib/sequel/sql.rb:109:in `to_s_append'
    from /home/bm93/.rbenv/versions/2.3.1/lib/ruby/gems/2.3.0/gems/sequel-4.34.0/lib/sequel/dataset/sql.rb:1214:in `literal_expression_append'
    from /home/bm93/.rbenv/versions/2.3.1/lib/ruby/gems/2.3.0/gems/sequel-4.34.0/lib/sequel/dataset/sql.rb:86:in `literal_append'
    from /home/bm93/.rbenv/versions/2.3.1/lib/ruby/gems/2.3.0/gems/sequel-4.34.0/lib/sequel/dataset/sql.rb:345:in `literal'
    from /home/bm93/.rbenv/versions/2.3.1/lib/ruby/gems/2.3.0/gems/sequel-4.34.0/lib/sequel/dataset/sql.rb:1534:in `static_sql'
    from /home/bm93/.rbenv/versions/2.3.1/lib/ruby/gems/2.3.0/gems/sequel-4.34.0/lib/sequel/dataset/sql.rb:23:in `insert_sql'
    from /home/bm93/.rbenv/versions/2.3.1/lib/ruby/gems/2.3.0/gems/sequel-4.34.0/lib/sequel/dataset/actions.rb:334:in `insert'
    from /home/bm93/.rbenv/versions/2.3.1/lib/ruby/gems/2.3.0/gems/sequel-4.34.0/lib/sequel/adapters/shared/postgres.rb:1355:in `insert'
    from tt.rb:12:in `<main>'

I'm doing this way because I need to extract a number from a string. Any hint there? Is there any way better?


Solution

  • If you execute:

    "[79511]Rocket".split(/[\[\]]/x)[1].match(/^(\d)+$/)
    

    in irb, you'll see:

    => #<MatchData "79511" 1:"1"> 
    

    because String#match returns a MatchData instance and calling inspect on a MatchData gives you #<MatchData ...>. If you puts that value:

    puts "[79511]Rocket".split(/[\[\]]/x)[1].match(/^(\d)+$/)
    

    you'll see 79511 because puts calls to_s on its arguments to convert them to strings.

    Sequel won't call to_s, it will try to figure out how to translate the argument to something the database understands on its own and it doesn't know what to do with a MatchData instance, hence the:

    can't express #<MatchData "79511" 1:"1"> as a SQL literal
    

    error.

    You could call to_s yourself:

    DB["INSERT INTO pd_deals ( enterprise_id ) VALUES (?)",  enterprise_id.to_s.presence]
    

    or use the MatchData#[] method:

    DB["...", enterprise_id ? enterise_id[0] : nil]
    

    or even use String#[] as Cary Swoveland suggests:

    enterprise_id = "[79511]Rocket"[/(?<=\[)\d+(?=\])/]
    DB["...", enterprise_id.presence]
    

    I'm not that familiar with Sequel so you might need some to_i calls in there to convert the '79511' strings to 79511 numbers.