Class: Epoxy

Inherits:
Object
  • Object
show all
Defined in:
lib/epoxy.rb

Overview

Epoxy - bind data to queries for any query language.

Let me hit ya with some science!

# numbered binds
ep = Epoxy.new("select * from foo where bar=?")
binds = %W[foo]
bound_query = ep.quote { |x| "'" + binds[x] + "'" }
"select * from foo where bar='foo'"

# named binds
binds = { :name => 'Lee', :age => 132 }
ep = Epoxy.new("select * from people where name=?name and age=?age")
bound_query = ep.quote(binds) { |x| "'#{binds[x]}'" }
"select * from people where name='Lee' and age='132'"

# mix them!
binds = { 0 => "Age", :name => 'Lee' }
ep = Epoxy.new("select * from people where name=?name and age=?")
bound_query = ep.quote(binds) { |x| "'#{binds[x]}'" }
"select * from people where name='Lee' and age='Age'"

Epoxy handles:

  • ?<name> for named binds

  • ? for numbered binds

  • ?? for a real question mark

  • ‘?’ for a real question mark

  • comments, weird quoting styles (look at the “holy shit” test for examples)

  • not telling you how to quote your data. This solution works for any query language and any database.

Instance Attribute Summary collapse

Class Method Summary collapse

Instance Method Summary collapse

Constructor Details

#initialize(query, comment_chars = %r{--|//}) ⇒ Epoxy

Takes a query as a string and an optional regexp defining beginning-of-line comments. The binding rules are as follows:

  • ?<name> for named binds

  • ? for numbered binds

  • ?? for a real question mark

  • ‘?’ for a real question mark

  • comments, weird quoting styles are unaffected.



76
77
78
79
80
# File 'lib/epoxy.rb', line 76

def initialize(query, comment_chars=%r{--|//})
  @comment_chars = comment_chars
  @query  = query
  @tokens = self.class.parse_tokens(query, @comment_chars)
end

Instance Attribute Details

#comment_charsObject (readonly)

leader comment characters - defaults to SQL “–”



64
65
66
# File 'lib/epoxy.rb', line 64

def comment_chars
  @comment_chars
end

#queryObject (readonly)

the original query, before quoting.



62
63
64
# File 'lib/epoxy.rb', line 62

def query
  @query
end

#tokensObject (readonly)

tokens generated by Epoxy.parse_tokens. Just use Epoxy#quote for now.



60
61
62
# File 'lib/epoxy.rb', line 60

def tokens
  @tokens
end

Class Method Details

.parse_tokens(query, comment_chars) ⇒ Object

Token parser, isolates components of the query into parts to where they can be managed indepdently.

Probably not the easiest thing to deal with by itself. Use the standard methods plox.



39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
# File 'lib/epoxy.rb', line 39

def self.parse_tokens(query, comment_chars)
  a = query.scan(%r{
    (
      #{comment_chars}.*                  (?# matches "--" style comments to the end of line or string )
      |
      ' ( [^'\\]  |  ''  |  \\. )* '      (?# match strings surrounded by apostophes )
      |
      " ( [^"\\]  |  ""  |  \\. )* "      (?# match strings surrounded by " )
      |
      ['"]                                (?# match a loose quote ) 
      |         
      \?[a-zA-Z]+                         (?# match a named bind )
      |
      \?\??                               (?# match one or two question marks )
      |
      [^-/'"?:]+                          (?# match all characters except ' " ? - : and / )
  )
  }x).collect(&:first)
end

Instance Method Details

#quote(binds = {}, &block) ⇒ Object

Processes your query for quoting. Provide a block that emulates how your data should be quoted. This method accepts a Hash to process named bindings, which when provided will yield each successive Hash key which has a match in the named binds. Keys are coerced to symbols before being yielded.

Without a Hash it will yield on each successive bound element with the index of that element passed.

You are responsible for quoting your data properly. Epoxy just makes it easier to get the places you need to quote out of the query.



94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
# File 'lib/epoxy.rb', line 94

def quote(binds = {}, &block)
  result = ""
  bind_pos = 0

  unless binds.empty?
    tokens.each do |token|
      binds.each do |key, rep|
        if token == "?#{key}"
          token.replace block.call(key.to_sym)
        end
      end
    end
  end

  tokens.each do |part|
    case part
    when '?'
      result << block.call(bind_pos)
      bind_pos += 1
    when '??'
      result << "?"
    else
      result << part
    end
  end

  return result
end