Ruby Sequel MatchData Error "can't express #<MatchData" - ruby-on-rails

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?

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.

Related

what is the meaning of "#$e" in ruby 2.1.2

I have one string which is contain char like "Pi#$e77L09!($".
But when i tried to print on Console its prints like "Pi!($"
2.1.2 :002 > str = "Pi#$e77L09!($" => "Pi!($"
2.1.2 :003 > puts str Pi!($ => nil
The magic is in #. As you know "#{ ruby code }" executes the code in the block and prints it. Similarly, # allows you to access global variables of a program by putting a hash in front of it like so "#$a".
so if you try this code.
$a = 'test'
puts "#$a"
you will see that it prints test. So in your case since $e77L09 is a non-existent global variable it prints nothing.
if you want to print that string as a string you will need to print it in single quotes. 'Pi#$e77L09!($'
The above usage of "#$1 #$2" is really useful when you use regex. When a string has multiple matching regex it becomes accessible using $1, $2, $3, etc. This stuff came from the Perl world I believe (I am not 100% sure about its history).
EDIT: Like Global Variable supports class and instance variables can be accessed as well.
try
puts 'Pi#$e77L09!($'
'#' is the special character which uses for interpolation of string in ruby.
if you use 'Pi#$e77L09!($' it will add \ String Literals before the '#'

using a variable name with a column name in rails model

I've a piece of code in my product model where I assign values to columns by fetching from s3. The column names includes a counter "i" as well -
The 3 sample column names are -
pic1_file_name
pic2_file_name
pic3_file_name
The problematic code is -
prod = Product.find(id)
i=1
s3 = AWS::S3.new
bucket=s3.buckets['bucket_name']
bucket.objects.each do |obj|
prod.("pic"+"#{i}".to_s+"_file_name")=obj.key[45..1]
# the above line give a syntax error, unexpected '=', expecting end-of-input
prod.("pic"+"#{i}".to_s+"_file_name").to_sym = obj.key[45..-1]
# The above line gives an error undefined method `call' for #<Product:0x7773f18>
prod.send("pic"+"#{i}".to_s+"_file_name")=obj.key[45..-1]
# The above gives syntax error, unexpected '=', expecting end-of-input
i+=1
end
prod.save
Could you please advise as to how should I structure my column name with a variable so that I can assign a value to it without having to type 15 separate columns every time.
Any pointers will be appreciated.
Thanks in advance!
You almost got the last one right. You see, when doing
obj.pic1_file_name = 'foo'
you're actually calling method pic1_file_name=, not pic1_file_name. That line is equivalent to this:
obj.pic1_file_name=('foo')
With that in mind, your last line becomes
prod.send("pic#{i}_file_name=", obj.key[45..-1])
You can use the send method to call a method from a string:
prod.send("pic#{i}_file_name") # to read
prod.send("pic#{i}_file_name=", obj.key[45..-1]) # to assign

Regex in Ruby: expression not found

I'm having trouble with a regex in Ruby (on Rails). I'm relatively new to this.
The test string is:
http://www.xyz.com/017010830343?$ProdLarge$
I am trying to remove "$ProdLarge$". In other words, the $ signs and anything between.
My regular expression is:
\$\w+\$
Rubular says my expression is ok. http://rubular.com/r/NDDQxKVraK
But when I run my code, the app says it isn't finding a match. Code below:
some_array.each do |x|
logger.debug "scan #{x.scan('\$\w+\$')}"
logger.debug "String? #{x.instance_of?(String)}"
x.gsub!('\$\w+\$','scl=1')
...
My logger debug line shows a result of "[]". String is confirmed as being true. And the gsub line has no effect.
What do I need to correct?
Use /regex/ instead of 'regex':
> "http://www.xyz.com/017010830343?$ProdLarge$".gsub(/\$\w+\$/, 'scl=1')
=> "http://www.xyz.com/017010830343?scl=1"
Don't use a regex for this task, use a tool designed for it, URI. To remove the query:
require 'uri'
url = URI.parse('http://www.xyz.com/017010830343?$ProdLarge$')
url.query = nil
puts url.to_s
=> http://www.xyz.com/017010830343
To change to a different query use this instead of url.query = nil:
url.query = 'scl=1'
puts url.to_s
=> http://www.xyz.com/017010830343?scl=1
URI will automatically encode values if necessary, saving you the trouble. If you need even more URL management power, look at Addressable::URI.

undefined method `gsub' for nil:NilClass

I'm newvbie in ruby on rails.. I'm having problem with gsub.. I everytime I go to the list of my store page it says "undefined method `gsub' for nil:NilClass"..
here is mycode :
def self.search(search_val, page = 1)
#search_val = search_val.gsub("'", "\\\\'")
search_query = "store_id LIKE '%#{ #search_val }%' OR english_name LIKE '%#{ #search_val }%' OR chinese_name LIKE '%#{ #search_val }%'"
select("jos_store.id, store_id, english_name, chinese_name, store_manager, delivery_area,year, week").joins("LEFT OUTER JOIN (SELECT id as store_replenishment, store, MAX(stock_movement) AS stock_movement FROM jos_store_replenishment GROUP BY store) AS replenishment ON replenishment.store = jos_store.id").joins("LEFT OUTER JOIN jos_stock_movement ON jos_stock_movement.id = replenishment.stock_movement").where(search_query).order("year DESC, week DESC").paginate :page => page, :per_page => 15
end
thanks in advance
A good practice is doing .to_s when you are using string methods.
You can use the & operator on search_val. It allows you to avoid null pointer exceptions without adding additional checks or using to_s to convert a string to a string.
So, you'll have something like this:
#search_val = search_val&.gsub("'", "\\\\'")
You can read more on the safe navigation operator here: http://mitrev.net/ruby/2015/11/13/the-operator-in-ruby/
This means that search_val is in fact nil. You can easily verify this by printing out the value of search_val.
I'm not sure if this is your case, but the same undefined method gsub for nil:NilClass error happened with me after a few rollbacks and migrations.
Then, I restarted the server and works. Maybe this could be the case for some people that reached this topic searching on Google.

Why am I getting this TypeError - "can't convert Symbol into Integer"?

I have an array of hashes. Each entry looks like this:
- !map:Hashie::Mash
name: Connor H Peters
id: "506253404"
I'm trying to create a second array, which contains just the id values.
["506253404"]
This is how I'm doing it
second_array = first_array.map { |hash| hash[:id] }
But I'm getting this error
TypeError in PagesController#home
can't convert Symbol into Integer
If I try
second_array = first_array.map { |hash| hash["id"] }
I get
TypeError in PagesController#home
can't convert String into Integer
What am I doing wrong? Thanks for reading.
You're using Hashie, which isn't the same as Hash from ruby core. Looking at the Hashie github repo, it seems that you can access hash keys as methods:
first_array.map { |hash| hash.id }
Try this out and see if that works--make sure that it doesn't return the object_id. As such, you may want to double-check by doing first_array.map { |hash| hash.name } to see if you're really accessing the right data.
Then, provided it's correct, you can use a proc to get the id (but with a bit more brevity):
first_array.map(&:id)
This sounds like inside the map block that hash is not actually a hashie - it's an array for some reason.
The result is that the [] method is actually an array accessor method and requires an integer. Eg. hash[0] would be valid, but not hash["id"].
You could try:
first_array.flatten.map{|hash| hash.id}
which would ensure that if you do have any nested arrays that nesting is removed.
Or perhaps
first_array.map{|hash| hash.id if hash.respond_to?(:id)}
But either way you may end up with unexpected behaviour.

Resources