1

How does one write Ruby methods for modification in place?

I want to accomplish the following:

def fulljoin(ruby_array)
  r = ''
  ruby_array.each {|item| r += "'#{ item }', "}
  r.chop!.chop!
end

a = ['Alex', 'Bert', 'Charlie']
a = fulljoin(a)             # => 'Alex', 'Bert', 'Charlie'

But I want to modify the array a in place:

a.fulljoin!

What is the syntax to accomplish this?

Rich_F
  • 1,830
  • 3
  • 24
  • 45
  • 1
    Not what you are asking but `a = a.map{ |e| "'#{e}'" }.join(', ')` delivers the same result so I am not sure whether you actually need the whole method overhead. – ulferts Feb 13 '19 at 15:55
  • I'd rather go to an inline method due to the number of times I need this formatting for `PostgreSQL`. I don't want to write it out every time. – Rich_F Feb 13 '19 at 15:57
  • 2
    `a.to_s[1...-1]` might suit. As for your question, to be able to write `a.fullyjoin!` you could reopen the Array class and add the method (but this is discouraged). – Sagar Pandya Feb 13 '19 at 16:01
  • 2
    It appears you want to use this method to format arrays to be used as possible values in a SQL query. In that case, your method results in a SQL injection vulnerability. Instead, you should use [the capabilities for prepared statements in the `pg` gem](https://stackoverflow.com/a/10841500/421705). – Holger Just Feb 13 '19 at 16:01
  • @HolgerJust Sorry, can you elaborate? This is exactly why I want this optimized. I am using `exec_params`, and all this is for application of arrays for either `fieldnames` or `values`. – Rich_F Feb 13 '19 at 16:02
  • Hmmm, it appears that this is indeed a bit more complex with just the basic pg gem. There is something like `PG::TextEncoder::QuotedLiteral.new.encode(string_value)` to correctly quote a string but this only marginally helps with prepared statements... I think, you'll likely be better of with using something like [Sequel](https://github.com/jeremyevans/sequel) with its higher-level API, including automatic quoting which is still very fast. This is likely simpler and much more secure rather than trying to handroll complex escaping of value. – Holger Just Feb 13 '19 at 16:29
  • 1
    Initially `a` is an Array. Do you want `a` become a String after `a.fulljoin!`? If yes, it's not a very good idea. – Ivan Olshansky Feb 13 '19 at 17:28
  • Yes, I do, because that's what I need. But just realized it changes type so it cannot be done. – Rich_F Feb 13 '19 at 17:34
  • @Rich_F, There is a workaround. `a.fulljoin!` can make `a` an Array with only one member a[0] - a String you need. – Ivan Olshansky Feb 13 '19 at 17:41
  • Beyond the scope of this question, but thanks. Just as easy as the reassignment of the variable. – Rich_F Feb 13 '19 at 17:49
  • @Rich_F, I can publish my comments as answer (with working solution for a[0] workaround) if you think it can be useful. – Ivan Olshansky Feb 13 '19 at 17:58
  • 1
    Sure, why not. I'm always looking for efficient solutions. – Rich_F Feb 13 '19 at 18:00
  • @Rich_F, done: https://stackoverflow.com/a/54676840/10306509 – Ivan Olshansky Feb 13 '19 at 18:14
  • 1
    Excellent, will take a look. – Rich_F Feb 13 '19 at 18:31
  • If `a` is an instance of class `A`, there is no instance method `A#m` such that, after `a.m` is executed, `a` will be an instance of any class other than `A`. For example, an array cannot be converted in place (i.e., `a.object_id` is not changed) to a string. – Cary Swoveland Feb 14 '19 at 03:45

1 Answers1

1

Initially a is an Array. If you could write method a.fulljoin! with desirable result, a would become a String, but it's not possible in Ruby.

But a.fulljoin! can convert a to Array with single member a[0] - a String you need. And it will be as close to your goal as possible:

class Array
  def fulljoin!
    r = "'#{self.join("', '")}'"
    self.clear
    self[0] = r
  end
end

a = ["Alex", "Bert", "Charlie"]
a.fulljoin!
p a
=> ["'Alex', 'Bert', 'Charlie'"]

P.S.: As suggested by @engineersmnky, method fulljoin! can be simplified to:

class Array
  def fulljoin!
    self.replace(["'#{self.join("', '")}'"])
  end
end
Ivan Olshansky
  • 889
  • 2
  • 17
  • 23
  • 1
    Oh I see. OK then. Then the variable reassignment `a = fulljoin(a)` is much more simple when implementing this in PG. I'm going to actually suggest a method in the PG Class as this is strangely not present. – Rich_F Feb 13 '19 at 18:37
  • 1
    @Rich_F, you can just write `a = "'#{a.join("', '")}'"` – Ivan Olshansky Feb 13 '19 at 19:09
  • 1
    See that's still long, given the frequency I'm preparing statements. Now I have `Methods::fulljoin(a)` which is crazy easy. But I'm still confused as to why this functionality isn't in the PG gem, both for listing fields and values. – Rich_F Feb 13 '19 at 19:11
  • 1
    can be simplified to `self.replace(["'#{self.join("', '")}'"])` – engineersmnky Feb 13 '19 at 20:42
  • @engineersmnky, It should work. I've added your suggestion to post. Thank you! – Ivan Olshansky Feb 13 '19 at 22:13
  • Your first sentence is incorrect. Not that it's "not a good idea", but it is not possible to convert an array in place to a string: `a = []; a.replace('cat') #=> TypeError (no implicit conversion of String into Array)`. If that is not enough to convince you let's see a counter-example. – Cary Swoveland Feb 14 '19 at 17:14
  • @CarySwoveland, Please specify which sentence you mean? – Ivan Olshansky Feb 14 '19 at 17:18
  • Which "first sentence"? – Cary Swoveland Feb 14 '19 at 17:19
  • @CarySwoveland, sorry, I had seen early version of your comment and didn't understand what it was about. As for the essence of the question, I meant that the idea of changing the object class is very, very bad from the point of view of OOP, even if it would be possible in Ruby. And you are absolutely right: in Ruby it is impossible. And it's very good, that it's impossible. Anyway, thank you very much for your comment! – Ivan Olshansky Feb 14 '19 at 17:58
  • 1
    Consider an edit. It doesn't make sense to say that something is a bad idea if it's not possible. "Hey, guys, let's check out the bars on Mars tonight.". Bad idea. For one, nighttime temperatures on Mars can be -100 degrees Celsius (almost as cold as Minsk on Feb. 2, 2012). Also, so far as we know, there are no bars on Mars. – Cary Swoveland Feb 14 '19 at 18:19
  • @CarySwoveland, You're right again. It really was not very correctly formulated. I have edited the post. Thank you for helping to make the answer better! – Ivan Olshansky Feb 14 '19 at 18:26