smart_tuple 0.1.1 → 0.1.2
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.
- data/README.html +108 -82
- data/README.md +32 -9
- data/Rakefile +18 -8
- data/VERSION.yml +1 -1
- data/init.rb +1 -0
- data/lib/smart_tuple.rb +67 -45
- data/smart_tuple.gemspec +16 -19
- data/spec/smart_tuple_spec.rb +9 -0
- metadata +9 -21
    
        data/README.html
    CHANGED
    
    | @@ -7,43 +7,61 @@ | |
| 7 7 |  | 
| 8 8 | 
             
            <h2 id="introduction">Introduction</h2>
         | 
| 9 9 |  | 
| 10 | 
            -
            <p>Sometimes we need to build SQL WHERE statements which are compound or conditional by nature. SmartTuple simplifies this task by letting us build statements of virtually unlimited complexity out of smaller ones.</p>
         | 
| 10 | 
            +
            <p>Sometimes we need to build SQL <code>WHERE</code> statements which are compound or conditional by nature. <strong>SmartTuple</strong> simplifies this task by letting us build statements of virtually unlimited complexity out of smaller ones.</p>
         | 
| 11 11 |  | 
| 12 12 | 
             
            <p>SmartTuple is suitable for use with Ruby on Rails (ActiveRecord) and other Ruby frameworks and ORMs.</p>
         | 
| 13 13 |  | 
| 14 | 
            -
            <h2 id="setup">Setup</h2>
         | 
| 14 | 
            +
            <h2 id="setup-rails-3">Setup (Rails 3)</h2>
         | 
| 15 15 |  | 
| 16 | 
            -
            < | 
| 17 | 
            -
             | 
| 16 | 
            +
            <p>In your app’s <code>Gemfile</code>, add:</p>
         | 
| 17 | 
            +
             | 
| 18 | 
            +
            <pre><code>gem "smart_tuple"
         | 
| 19 | 
            +
            </code></pre>
         | 
| 20 | 
            +
             | 
| 21 | 
            +
            <p>To install the gem with RDoc/ri documentation, do a:</p>
         | 
| 22 | 
            +
             | 
| 23 | 
            +
            <pre><code>$ gem install smart_tuple
         | 
| 18 24 | 
             
            </code></pre>
         | 
| 19 25 |  | 
| 20 | 
            -
            <p> | 
| 26 | 
            +
            <p>Otherwise, do a <code>bundle install</code>.</p>
         | 
| 27 | 
            +
             | 
| 28 | 
            +
            <h2 id="setup-rails-2">Setup (Rails 2)</h2>
         | 
| 29 | 
            +
             | 
| 30 | 
            +
            <p>In your app’s <code>config/environment.rb</code> do a:</p>
         | 
| 21 31 |  | 
| 22 | 
            -
            <pre><code>config.gem  | 
| 32 | 
            +
            <pre><code>config.gem "smart_tuple"
         | 
| 23 33 | 
             
            </code></pre>
         | 
| 24 34 |  | 
| 35 | 
            +
            <p>To install the gem, do a:</p>
         | 
| 36 | 
            +
             | 
| 37 | 
            +
            <pre><code>$ gem sources --add http://rubygems.org
         | 
| 38 | 
            +
            $ gem install smart_tuple
         | 
| 39 | 
            +
            </code></pre>
         | 
| 40 | 
            +
             | 
| 41 | 
            +
            <p>, or use <code>rake gems:install</code>.</p>
         | 
| 42 | 
            +
             | 
| 25 43 | 
             
            <h2 id="kickstart-demo">Kickstart Demo</h2>
         | 
| 26 44 |  | 
| 27 | 
            -
            <pre><code>tup = SmartTuple.new( | 
| 45 | 
            +
            <pre><code>tup = SmartTuple.new(" AND ")
         | 
| 28 46 | 
             
            tup << {:brand => params[:brand]} if params[:brand].present?
         | 
| 29 | 
            -
            tup << [ | 
| 30 | 
            -
            tup << [ | 
| 47 | 
            +
            tup << ["min_price >= ?", params[:min_price]] if params[:min_price].present?
         | 
| 48 | 
            +
            tup << ["max_price <= ?", params[:max_price]] if params[:max_price].present?
         | 
| 31 49 |  | 
| 32 50 | 
             
            @phones = Phone.find(:all, :conditions => tup.compile)
         | 
| 33 51 | 
             
            </code></pre>
         | 
| 34 52 |  | 
| 35 | 
            -
            <p>There | 
| 53 | 
            +
            <p>There’s a number of ways you can use SmartTuple. Some of them is covered in the tutorial below.</p>
         | 
| 36 54 |  | 
| 37 55 | 
             
            <h2 id="tutorial">Tutorial</h2>
         | 
| 38 56 |  | 
| 39 | 
            -
            <p>Suppose we | 
| 57 | 
            +
            <p>Suppose we’ve got a mobile phone catalog with a search form. We are starting with a price filter of two values: <code>min_price</code> and <code>max_price</code>, both optional.</p>
         | 
| 40 58 |  | 
| 41 59 | 
             
            <p>Filter logic:</p>
         | 
| 42 60 |  | 
| 43 61 | 
             
            <ul>
         | 
| 44 | 
            -
              <li>If the user hasn | 
| 45 | 
            -
              <li>If the user has input <code>min_price</code>, it | 
| 46 | 
            -
              <li>If the user has input <code>max_price</code>, it | 
| 62 | 
            +
              <li>If the user hasn’t input anything, the filter has no conditions (allows any record).</li>
         | 
| 63 | 
            +
              <li>If the user has input <code>min_price</code>, it’s used in filter condition.</li>
         | 
| 64 | 
            +
              <li>If the user has input <code>max_price</code>, it’s used in filter condition.</li>
         | 
| 47 65 | 
             
              <li>If the user has input <code>min_price</code> and <code>max_price</code>, they both are used in filter condition.</li>
         | 
| 48 66 | 
             
            </ul>
         | 
| 49 67 |  | 
| @@ -53,155 +71,162 @@ tup << ["max_price <= ?", params[:max_price]] if params[:max_ | |
| 53 71 | 
             
            params[:max_price] = 300    # Can be blank.
         | 
| 54 72 | 
             
            </code></pre>
         | 
| 55 73 |  | 
| 56 | 
            -
            <p>Now let | 
| 74 | 
            +
            <p>Now let’s write condition-building code:</p>
         | 
| 57 75 |  | 
| 58 | 
            -
            <pre><code># Start by creating a tuple whose statements are glued with  | 
| 59 | 
            -
            tup = SmartTuple.new( | 
| 76 | 
            +
            <pre><code># Start by creating a tuple whose statements are glued with " AND ".
         | 
| 77 | 
            +
            tup = SmartTuple.new(" AND ")
         | 
| 60 78 |  | 
| 61 79 | 
             
            # If min_price is not blank, append its statement.
         | 
| 62 80 | 
             
            if params[:min_price].present?
         | 
| 63 | 
            -
              tup << [ | 
| 81 | 
            +
              tup << ["min_price >= ?", params[:min_price]]
         | 
| 64 82 | 
             
            end
         | 
| 65 83 |  | 
| 66 84 | 
             
            # Same for max_price.
         | 
| 67 85 | 
             
            if params[:max_price].present?
         | 
| 68 | 
            -
              tup << [ | 
| 86 | 
            +
              tup << ["max_price <= ?", params[:max_price]]
         | 
| 69 87 | 
             
            end
         | 
| 70 88 |  | 
| 71 89 | 
             
            # Finally, fire up the query.
         | 
| 72 90 | 
             
            @phones = Phone.find(:all, {:conditions => tup.compile})
         | 
| 73 91 | 
             
            </code></pre>
         | 
| 74 92 |  | 
| 75 | 
            -
            <p>That | 
| 93 | 
            +
            <p>That’s basically it. Now let’s see how different <code>params</code> values affect the resulting <code>:conditions</code> value. Labelled <strong>p</strong> and <strong>c</strong> in this and following listings:</p>
         | 
| 76 94 |  | 
| 77 95 | 
             
            <pre><code>p: {}
         | 
| 78 96 | 
             
            c: []
         | 
| 79 97 |  | 
| 80 98 | 
             
            p: {:max_price=>300}
         | 
| 81 | 
            -
            c: [ | 
| 99 | 
            +
            c: ["max_price <= ?", 300]
         | 
| 82 100 |  | 
| 83 101 | 
             
            p: {:min_price=>100, :max_price=>300}
         | 
| 84 | 
            -
            c: [ | 
| 102 | 
            +
            c: ["min_price >= ? AND max_price <= ?", 100, 300]
         | 
| 85 103 | 
             
            </code></pre>
         | 
| 86 104 |  | 
| 87 105 | 
             
            <h3 id="plus-another-condition">Plus Another Condition</h3>
         | 
| 88 106 |  | 
| 89 | 
            -
            <p>Let | 
| 107 | 
            +
            <p>Let’s make things a bit more user-friendly. Let user filter phones by brand. We do it by adding another field, let’s call it <code>brand</code>, bearing a straight string value (that’s just a simple tutorial, remember?).</p>
         | 
| 90 108 |  | 
| 91 109 | 
             
            <p>Our <code>params</code> now becomes something like:</p>
         | 
| 92 110 |  | 
| 93 | 
            -
            <pre><code>params[:brand] =  | 
| 111 | 
            +
            <pre><code>params[:brand] = "Nokia"    # Can be blank.
         | 
| 94 112 | 
             
            params[:min_price] = 100    # Can be blank.
         | 
| 95 113 | 
             
            params[:max_price] = 300    # Can be blank.
         | 
| 96 114 | 
             
            </code></pre>
         | 
| 97 115 |  | 
| 98 | 
            -
            <p>Let | 
| 116 | 
            +
            <p>Let’s build a tuple:</p>
         | 
| 99 117 |  | 
| 100 | 
            -
            <pre><code>tup = SmartTuple.new( | 
| 118 | 
            +
            <pre><code>tup = SmartTuple.new(" AND ") +
         | 
| 101 119 | 
             
              ({:brand => params[:brand]} if params[:brand].present?) +
         | 
| 102 | 
            -
              ([ | 
| 103 | 
            -
              ([ | 
| 120 | 
            +
              (["min_price >= ?", params[:min_price]] if params[:min_price].present?) +
         | 
| 121 | 
            +
              (["max_price <= ?", params[:max_price]] if params[:max_price].present?)
         | 
| 104 122 | 
             
            </code></pre>
         | 
| 105 123 |  | 
| 106 124 | 
             
            <p>The above code shows that we can construct ready-made tuples with a single expression, using <code>+</code> operator. Also, if a condition is an equality test, we can use Hash notation: <code>{:brand => params[:brand]}</code>.</p>
         | 
| 107 125 |  | 
| 108 126 | 
             
            <p>A quick look at <code>params</code> and <code>:conditions</code>, again:</p>
         | 
| 109 127 |  | 
| 110 | 
            -
            <pre><code>p: {:brand=> | 
| 111 | 
            -
            c: [ | 
| 128 | 
            +
            <pre><code>p: {:brand=>"Nokia"}
         | 
| 129 | 
            +
            c: ["brand = ?", "Nokia"]
         | 
| 112 130 |  | 
| 113 | 
            -
            p: {:brand=> | 
| 114 | 
            -
            c: [ | 
| 131 | 
            +
            p: {:brand=>"Nokia", :max_price=>300}
         | 
| 132 | 
            +
            c: ["brand = ? AND max_price <= ?", "Nokia", 300]
         | 
| 115 133 |  | 
| 116 | 
            -
            p: {:brand=> | 
| 117 | 
            -
            c: [ | 
| 134 | 
            +
            p: {:brand=>"Nokia", :min_price=>100, :max_price=>300}
         | 
| 135 | 
            +
            c: ["brand = ? AND min_price >= ? AND max_price <= ?", "Nokia", 100, 300]
         | 
| 118 136 | 
             
            </code></pre>
         | 
| 119 137 |  | 
| 120 138 | 
             
            <h3 id="we-want-more">We Want More!</h3>
         | 
| 121 139 |  | 
| 122 | 
            -
            <p>Since we now see how easy it | 
| 140 | 
            +
            <p>Since we now see how easy it’s to build compound conditions, we decide to further extend our search form. Now we want to:</p>
         | 
| 123 141 |  | 
| 124 142 | 
             
            <ul>
         | 
| 125 143 | 
             
              <li>Let user specify more than 1 brand.</li>
         | 
| 126 144 | 
             
              <li>Let user specify a selection of colors.</li>
         | 
| 127 145 | 
             
            </ul>
         | 
| 128 146 |  | 
| 129 | 
            -
            <p>From <code>params</code> perspective that | 
| 147 | 
            +
            <p>From <code>params</code> perspective that’s something like:</p>
         | 
| 130 148 |  | 
| 131 | 
            -
            <pre><code>params[:brands] = [ | 
| 149 | 
            +
            <pre><code>params[:brands] = ["Nokia", "Motorola"]         # Can be blank.
         | 
| 132 150 | 
             
            params[:min_price] = 100                        # Can be blank.
         | 
| 133 151 | 
             
            params[:max_price] = 300                        # Can be blank.
         | 
| 134 | 
            -
            params[:colors] = [ | 
| 152 | 
            +
            params[:colors] = ["Black", "Silver", "Pink"]   # Can be blank.
         | 
| 135 153 | 
             
            </code></pre>
         | 
| 136 154 |  | 
| 137 | 
            -
            <p>Quite obvious is that supplied values for brands and colors should be OR | 
| 155 | 
            +
            <p>Quite obvious is that supplied values for brands and colors should be OR’ed. We’re now facing the task of creating a “sub-tuple”, e.g. to match brand, and then merging this sub-tuple into main tuple. Doing it straight is something like:</p>
         | 
| 138 156 |  | 
| 139 | 
            -
            <pre><code>tup = SmartTuple.new( | 
| 157 | 
            +
            <pre><code>tup = SmartTuple.new(" AND ")
         | 
| 140 158 |  | 
| 141 159 | 
             
            if params[:brands].present?
         | 
| 142 | 
            -
              subtup = SmartTuple.new( | 
| 143 | 
            -
              params[:brands].each {|brand| subtup << [ | 
| 160 | 
            +
              subtup = SmartTuple.new(" OR ")
         | 
| 161 | 
            +
              params[:brands].each {|brand| subtup << ["brand = ?", brand]}
         | 
| 144 162 | 
             
              tup << subtup
         | 
| 145 163 | 
             
            end
         | 
| 146 164 | 
             
            </code></pre>
         | 
| 147 165 |  | 
| 148 166 | 
             
            <p>Or, in a smarter way by utilizing <code>#add_each</code> method:</p>
         | 
| 149 167 |  | 
| 150 | 
            -
            <pre><code>tup = SmartTuple.new( | 
| 151 | 
            -
            tup << SmartTuple.new( | 
| 168 | 
            +
            <pre><code>tup = SmartTuple.new(" AND ")
         | 
| 169 | 
            +
            tup << SmartTuple.new(" OR ").add_each(params[:brands]) {|v| ["brand = ?", v]} if params[:brands].present?
         | 
| 152 170 | 
             
            </code></pre>
         | 
| 153 171 |  | 
| 154 172 | 
             
            <p>The final query:</p>
         | 
| 155 173 |  | 
| 156 | 
            -
            <pre><code>Phone.find(:all, {:conditions => [SmartTuple.new( | 
| 157 | 
            -
              (SmartTuple.new( | 
| 158 | 
            -
              ([ | 
| 159 | 
            -
              ([ | 
| 160 | 
            -
              (SmartTuple.new( | 
| 174 | 
            +
            <pre><code>Phone.find(:all, {:conditions => [SmartTuple.new(" AND "),
         | 
| 175 | 
            +
              (SmartTuple.new(" OR ").add_each(params[:brands]) {|v| ["brand = ?", v]} if params[:brands].present?),
         | 
| 176 | 
            +
              (["min_price >= ?", params[:min_price]] if params[:min_price].present?),
         | 
| 177 | 
            +
              (["max_price <= ?", params[:max_price]] if params[:max_price].present?),
         | 
| 178 | 
            +
              (SmartTuple.new(" OR ").add_each(params[:colors]) {|v| ["color = ?", v]} if params[:colors].present?),
         | 
| 161 179 | 
             
            ].sum.compile})
         | 
| 162 180 | 
             
            </code></pre>
         | 
| 163 181 |  | 
| 164 182 | 
             
            <blockquote>
         | 
| 165 | 
            -
              <p>NOTE: In the above sample I | 
| 183 | 
            +
              <p>NOTE: In the above sample I’ve used <code>Array#sum</code> (available in ActiveSupport) instead of <code>+</code> to add statements to the tuple. I prefer to write it like this since it allows to comment and swap lines without breaking the syntax.</p>
         | 
| 184 | 
            +
            </blockquote>
         | 
| 185 | 
            +
             | 
| 186 | 
            +
            <blockquote>
         | 
| 187 | 
            +
              <p>NOTE: Recommended Rails 3 usage is:</p>
         | 
| 188 | 
            +
             | 
| 189 | 
            +
              <pre><code>Phone.where(...)      # Pass a compiled SmartTuple object in place of `...`.
         | 
| 190 | 
            +
            </code></pre>
         | 
| 166 191 | 
             
            </blockquote>
         | 
| 167 192 |  | 
| 168 193 | 
             
            <p>Checking out <code>params</code> and <code>:conditions</code>:</p>
         | 
| 169 194 |  | 
| 170 | 
            -
            <pre><code>p: {:brands=>[ | 
| 171 | 
            -
            c: [ | 
| 195 | 
            +
            <pre><code>p: {:brands=>["Nokia"], :max_price=>300}
         | 
| 196 | 
            +
            c: ["brand = ? AND max_price <= ?", "Nokia", 300]
         | 
| 172 197 |  | 
| 173 | 
            -
            p: {:brands=>[ | 
| 174 | 
            -
            c: [ | 
| 198 | 
            +
            p: {:brands=>["Nokia", "Motorola"], :max_price=>300}
         | 
| 199 | 
            +
            c: ["(brand = ? OR brand = ?) AND max_price <= ?", "Nokia", "Motorola", 300]
         | 
| 175 200 | 
             
                 ^--                    ^-- note the auto brackets
         | 
| 176 201 |  | 
| 177 | 
            -
            p: {:brands=>[ | 
| 178 | 
            -
            c: [ | 
| 202 | 
            +
            p: {:brands=>["Nokia", "Motorola"], :max_price=>300, :colors=>["Black"]}
         | 
| 203 | 
            +
            c: ["(brand = ? OR brand = ?) AND max_price <= ? AND color = ?", "Nokia", "Motorola", 300, "Black"]
         | 
| 179 204 |  | 
| 180 | 
            -
            p: {:brands=>[ | 
| 181 | 
            -
            c: [ | 
| 205 | 
            +
            p: {:brands=>["Nokia", "Motorola"], :colors=>["Black", "Silver", "Pink"]}
         | 
| 206 | 
            +
            c: ["(brand = ? OR brand = ?) AND (color = ? OR color = ? OR color = ?)", "Nokia", "Motorola", "Black", "Silver", "Pink"]
         | 
| 182 207 | 
             
            </code></pre>
         | 
| 183 208 |  | 
| 184 | 
            -
            <p>That | 
| 209 | 
            +
            <p>That’s the end of our tutorial. Hope now you’ve got an idea of what SmartTuple is.</p>
         | 
| 185 210 |  | 
| 186 211 | 
             
            <h2 id="api-summary">API Summary</h2>
         | 
| 187 212 |  | 
| 188 | 
            -
            <p>Here | 
| 213 | 
            +
            <p>Here’s a brief cheatsheet, which outlines the main SmartTuple features.</p>
         | 
| 189 214 |  | 
| 190 215 | 
             
            <h3 id="appending-statements">Appending Statements</h3>
         | 
| 191 216 |  | 
| 192 217 | 
             
            <pre><code># Array.
         | 
| 193 | 
            -
            tup << [ | 
| 194 | 
            -
            tup << [ | 
| 218 | 
            +
            tup << ["brand = ?", "Nokia"]
         | 
| 219 | 
            +
            tup << ["brand = ? AND color = ?", "Nokia", "Black"]
         | 
| 195 220 |  | 
| 196 221 | 
             
            # Hash.
         | 
| 197 | 
            -
            tup << {:brand =>  | 
| 198 | 
            -
            tup << {:brand =>  | 
| 222 | 
            +
            tup << {:brand => "Nokia"}
         | 
| 223 | 
            +
            tup << {:brand => "Nokia", :color => "Black"}
         | 
| 199 224 |  | 
| 200 225 | 
             
            # Another SmartTuple.
         | 
| 201 226 | 
             
            tup << other_tuple
         | 
| 202 227 |  | 
| 203 | 
            -
            # String.
         | 
| 204 | 
            -
            tup << & | 
| 228 | 
            +
            # String. Generally NOT recommended.
         | 
| 229 | 
            +
            tup << "min_price >= 75"
         | 
| 205 230 | 
             
            </code></pre>
         | 
| 206 231 |  | 
| 207 232 | 
             
            <p>Appending empty or blank (where appropriate) statements has no effect on the receiver:</p>
         | 
| @@ -210,24 +235,24 @@ tup << "brand IS NULL" | |
| 210 235 | 
             
            tup << []
         | 
| 211 236 | 
             
            tup << {}
         | 
| 212 237 | 
             
            tup << an_empty_tuple
         | 
| 213 | 
            -
            tup <<  | 
| 214 | 
            -
            tup <<  | 
| 238 | 
            +
            tup << ""
         | 
| 239 | 
            +
            tup << "  "     # Will be treated as blank if ActiveSupport is on.
         | 
| 215 240 | 
             
            </code></pre>
         | 
| 216 241 |  | 
| 217 242 | 
             
            <p>Another way to append something is to use <code>+</code>.</p>
         | 
| 218 243 |  | 
| 219 | 
            -
            <pre><code>tup = SmartTuple.new( | 
| 244 | 
            +
            <pre><code>tup = SmartTuple.new(" AND ") + {:brand => "Nokia"} + ["max_price <= ?", 300]
         | 
| 220 245 | 
             
            </code></pre>
         | 
| 221 246 |  | 
| 222 247 | 
             
            <p>Appending one statement per each collection item is easy through <code>#add_each</code>:</p>
         | 
| 223 248 |  | 
| 224 | 
            -
            <pre><code>tup.add_each([ | 
| 249 | 
            +
            <pre><code>tup.add_each(["Nokia", "Motorola"]) {|v| ["brand = ?", v]}
         | 
| 225 250 | 
             
            </code></pre>
         | 
| 226 251 |  | 
| 227 252 | 
             
            <p>The latter can be made conditional. Remember, appending <code>nil</code> has no effect on the receiving tuple, which gives us freedom to use conditions whenever we want to:</p>
         | 
| 228 253 |  | 
| 229 | 
            -
            <pre><code>tup.add_each([ | 
| 230 | 
            -
              [ | 
| 254 | 
            +
            <pre><code>tup.add_each(["Nokia", "Motorola"]) do |v|
         | 
| 255 | 
            +
              ["brand = ?", v] if v =~ /^Moto/
         | 
| 231 256 | 
             
            end
         | 
| 232 257 | 
             
            </code></pre>
         | 
| 233 258 |  | 
| @@ -235,7 +260,7 @@ end | |
| 235 260 |  | 
| 236 261 | 
             
            <p><em>This chapter still has to be written.</em></p>
         | 
| 237 262 |  | 
| 238 | 
            -
            <pre><code>tup = SmartTuple.new( | 
| 263 | 
            +
            <pre><code>tup = SmartTuple.new(" AND ")
         | 
| 239 264 | 
             
            tup.brackets
         | 
| 240 265 | 
             
            => :auto
         | 
| 241 266 |  | 
| @@ -255,43 +280,44 @@ tup.brackets = :auto | |
| 255 280 |  | 
| 256 281 | 
             
            <p>Compiling is converting the tuple into something suitable for use as <code>:conditions</code> of an ActiveRecord call.</p>
         | 
| 257 282 |  | 
| 258 | 
            -
            <p>It | 
| 283 | 
            +
            <p>It’s as straight as:</p>
         | 
| 259 284 |  | 
| 260 285 | 
             
            <pre><code>tup.compile
         | 
| 261 286 | 
             
            tup.to_a        # An alias, does the same.
         | 
| 262 287 |  | 
| 263 288 | 
             
            # Go fetch!
         | 
| 264 | 
            -
            Phone.find(:all, :conditions => tup.compile)
         | 
| 289 | 
            +
            Phone.find(:all, :conditions => tup.compile)    # Rails 2
         | 
| 290 | 
            +
            Phone.where(tup.compile)                        # Rails 3
         | 
| 265 291 | 
             
            </code></pre>
         | 
| 266 292 |  | 
| 267 293 | 
             
            <h3 id="contents-and-size">Contents and Size</h3>
         | 
| 268 294 |  | 
| 269 | 
            -
            <p>You can examine tuple | 
| 295 | 
            +
            <p>You can examine tuple’s state with methods often found in other Ruby classes: <code>#empty?</code>, <code>#size</code>, and attribute accessors <code>#statements</code> and <code>#args</code>.</p>
         | 
| 270 296 |  | 
| 271 | 
            -
            <pre><code>tup = SmartTuple.new( | 
| 297 | 
            +
            <pre><code>tup = SmartTuple.new(" AND ")
         | 
| 272 298 | 
             
            tup.empty?
         | 
| 273 299 | 
             
            => true
         | 
| 274 300 | 
             
            tup.size
         | 
| 275 301 | 
             
            => 0
         | 
| 276 302 |  | 
| 277 | 
            -
            tup << [ | 
| 303 | 
            +
            tup << ["brand = ?", "Nokia"]
         | 
| 278 304 | 
             
            tup.empty?
         | 
| 279 305 | 
             
            => false
         | 
| 280 306 | 
             
            tup.size
         | 
| 281 307 | 
             
            => 1
         | 
| 282 308 |  | 
| 283 | 
            -
            tup << [ | 
| 309 | 
            +
            tup << ["max_price >= ?", 300]
         | 
| 284 310 | 
             
            tup.size
         | 
| 285 311 | 
             
            => 2
         | 
| 286 312 |  | 
| 287 313 | 
             
            tup.statements
         | 
| 288 | 
            -
            => [ | 
| 314 | 
            +
            => ["brand = ?", "max_price >= ?"]
         | 
| 289 315 | 
             
            tup.args
         | 
| 290 | 
            -
            => [ | 
| 316 | 
            +
            => ["Nokia", 300]
         | 
| 291 317 | 
             
            </code></pre>
         | 
| 292 318 |  | 
| 293 319 | 
             
            <h2 id="feedback">Feedback</h2>
         | 
| 294 320 |  | 
| 295 | 
            -
            <p>Send bug reports, suggestions and criticisms through <a href="http://github.com/dadooda/smart_tuple">project | 
| 321 | 
            +
            <p>Send bug reports, suggestions and criticisms through <a href="http://github.com/dadooda/smart_tuple">project’s page on GitHub</a>.</p>
         | 
| 296 322 |  | 
| 297 323 | 
             
            <p>Licensed under the MIT License.</p>
         | 
    
        data/README.md
    CHANGED
    
    | @@ -6,21 +6,39 @@ SmartTuple: A Simple Yet Smart SQL Conditions Builder | |
| 6 6 | 
             
            Introduction
         | 
| 7 7 | 
             
            ------------
         | 
| 8 8 |  | 
| 9 | 
            -
            Sometimes we need to build SQL WHERE statements which are compound or conditional by nature. SmartTuple simplifies this task by letting us build statements of virtually unlimited complexity out of smaller ones.
         | 
| 9 | 
            +
            Sometimes we need to build SQL `WHERE` statements which are compound or conditional by nature. **SmartTuple** simplifies this task by letting us build statements of virtually unlimited complexity out of smaller ones.
         | 
| 10 10 |  | 
| 11 11 | 
             
            SmartTuple is suitable for use with Ruby on Rails (ActiveRecord) and other Ruby frameworks and ORMs.
         | 
| 12 12 |  | 
| 13 13 |  | 
| 14 | 
            -
            Setup
         | 
| 15 | 
            -
             | 
| 14 | 
            +
            Setup (Rails 3)
         | 
| 15 | 
            +
            ---------------
         | 
| 16 | 
            +
             | 
| 17 | 
            +
            In your app's `Gemfile`, add:
         | 
| 18 | 
            +
             | 
| 19 | 
            +
                gem "smart_tuple"
         | 
| 20 | 
            +
             | 
| 21 | 
            +
            To install the gem with RDoc/ri documentation, do a:
         | 
| 16 22 |  | 
| 17 | 
            -
                $ gem sources --add http://rubygems.org
         | 
| 18 23 | 
             
                $ gem install smart_tuple
         | 
| 19 24 |  | 
| 25 | 
            +
            Otherwise, do a `bundle install`.
         | 
| 26 | 
            +
             | 
| 27 | 
            +
             | 
| 28 | 
            +
            Setup (Rails 2)
         | 
| 29 | 
            +
            ---------------
         | 
| 30 | 
            +
             | 
| 20 31 | 
             
            In your app's `config/environment.rb` do a:
         | 
| 21 32 |  | 
| 22 33 | 
             
                config.gem "smart_tuple"
         | 
| 23 34 |  | 
| 35 | 
            +
            To install the gem, do a:
         | 
| 36 | 
            +
             | 
| 37 | 
            +
                $ gem sources --add http://rubygems.org
         | 
| 38 | 
            +
                $ gem install smart_tuple
         | 
| 39 | 
            +
             | 
| 40 | 
            +
            , or use `rake gems:install`.
         | 
| 41 | 
            +
             | 
| 24 42 |  | 
| 25 43 | 
             
            Kickstart Demo
         | 
| 26 44 | 
             
            --------------
         | 
| @@ -32,7 +50,7 @@ Kickstart Demo | |
| 32 50 |  | 
| 33 51 | 
             
                @phones = Phone.find(:all, :conditions => tup.compile)
         | 
| 34 52 |  | 
| 35 | 
            -
            There's a number of ways you can use SmartTuple  | 
| 53 | 
            +
            There's a number of ways you can use SmartTuple. Some of them is covered in the tutorial below.
         | 
| 36 54 |  | 
| 37 55 |  | 
| 38 56 | 
             
            Tutorial
         | 
| @@ -151,6 +169,10 @@ The final query: | |
| 151 169 |  | 
| 152 170 | 
             
            > NOTE: In the above sample I've used `Array#sum` (available in ActiveSupport) instead of `+` to add statements to the tuple. I prefer to write it like this since it allows to comment and swap lines without breaking the syntax.
         | 
| 153 171 |  | 
| 172 | 
            +
            > NOTE: Recommended Rails 3 usage is:
         | 
| 173 | 
            +
            >
         | 
| 174 | 
            +
            >     Phone.where(...)      # Pass a compiled SmartTuple object in place of `...`.
         | 
| 175 | 
            +
             | 
| 154 176 | 
             
            Checking out `params` and `:conditions`:
         | 
| 155 177 |  | 
| 156 178 | 
             
                p: {:brands=>["Nokia"], :max_price=>300}
         | 
| @@ -172,7 +194,7 @@ That's the end of our tutorial. Hope now you've got an idea of what SmartTuple i | |
| 172 194 | 
             
            API Summary
         | 
| 173 195 | 
             
            -----------
         | 
| 174 196 |  | 
| 175 | 
            -
            Here's a brief cheatsheet, which outlines main SmartTuple features.
         | 
| 197 | 
            +
            Here's a brief cheatsheet, which outlines the main SmartTuple features.
         | 
| 176 198 |  | 
| 177 199 | 
             
            ### Appending Statements ###
         | 
| 178 200 |  | 
| @@ -187,8 +209,8 @@ Here's a brief cheatsheet, which outlines main SmartTuple features. | |
| 187 209 | 
             
                # Another SmartTuple.
         | 
| 188 210 | 
             
                tup << other_tuple
         | 
| 189 211 |  | 
| 190 | 
            -
                # String.
         | 
| 191 | 
            -
                tup << " | 
| 212 | 
            +
                # String. Generally NOT recommended.
         | 
| 213 | 
            +
                tup << "min_price >= 75"
         | 
| 192 214 |  | 
| 193 215 | 
             
            Appending empty or blank (where appropriate) statements has no effect on the receiver:
         | 
| 194 216 |  | 
| @@ -244,7 +266,8 @@ It's as straight as: | |
| 244 266 | 
             
                tup.to_a        # An alias, does the same.
         | 
| 245 267 |  | 
| 246 268 | 
             
                # Go fetch!
         | 
| 247 | 
            -
                Phone.find(:all, :conditions => tup.compile)
         | 
| 269 | 
            +
                Phone.find(:all, :conditions => tup.compile)    # Rails 2
         | 
| 270 | 
            +
                Phone.where(tup.compile)                        # Rails 3
         | 
| 248 271 |  | 
| 249 272 |  | 
| 250 273 | 
             
            ### Contents and Size ###
         | 
    
        data/Rakefile
    CHANGED
    
    | @@ -1,4 +1,5 @@ | |
| 1 1 | 
             
            require "rake/rdoctask"
         | 
| 2 | 
            +
            require "yaml"
         | 
| 2 3 |  | 
| 3 4 | 
             
            GEM_NAME = "smart_tuple"
         | 
| 4 5 |  | 
| @@ -14,9 +15,9 @@ begin | |
| 14 15 | 
             
                gem.files = FileList[
         | 
| 15 16 | 
             
                  "[A-Z]*",
         | 
| 16 17 | 
             
                  "*.gemspec",
         | 
| 17 | 
            -
                  "generators/**/*",
         | 
| 18 | 
            -
                  "lib/**/*.rb",
         | 
| 19 18 | 
             
                  "init.rb",
         | 
| 19 | 
            +
                  "lib/**/*.rb",
         | 
| 20 | 
            +
                  "spec/**/*.rb",
         | 
| 20 21 | 
             
                ]
         | 
| 21 22 | 
             
              end
         | 
| 22 23 | 
             
            rescue LoadError
         | 
| @@ -26,13 +27,22 @@ end | |
| 26 27 | 
             
            desc "Rebuild gemspec and package"
         | 
| 27 28 | 
             
            task :rebuild => [:gemspec, :build]
         | 
| 28 29 |  | 
| 29 | 
            -
            desc "Push (publish) gem to RubyGems | 
| 30 | 
            -
            task :push  | 
| 31 | 
            -
              # Yet found no way to ask Jeweler forge a complete version string for us.
         | 
| 30 | 
            +
            desc "Push (publish) gem to RubyGems.org"
         | 
| 31 | 
            +
            task :push do
         | 
| 32 | 
            +
              # NOTE: Yet found no way to ask Jeweler forge a complete version string for us.
         | 
| 32 33 | 
             
              vh = YAML.load(File.read("VERSION.yml"))
         | 
| 33 | 
            -
              version = [vh[:major], vh[:minor], vh[:patch]].join(".")
         | 
| 34 | 
            -
              pkgfile = File.join("pkg",  | 
| 35 | 
            -
              system("gem", "push", pkgfile)
         | 
| 34 | 
            +
              version = [vh[:major], vh[:minor], vh[:patch], vh[:build]].compact.join(".")
         | 
| 35 | 
            +
              pkgfile = File.join("pkg", "#{GEM_NAME}-#{version}.gem")
         | 
| 36 | 
            +
              Kernel.system("gem", "push", pkgfile)
         | 
| 37 | 
            +
            end
         | 
| 38 | 
            +
             | 
| 39 | 
            +
            desc "Generate RDoc documentation"
         | 
| 40 | 
            +
            Rake::RDocTask.new(:rdoc) do |rdoc|
         | 
| 41 | 
            +
              rdoc.rdoc_dir = "doc"
         | 
| 42 | 
            +
              rdoc.title    = "SmartTuple"
         | 
| 43 | 
            +
              #rdoc.options << "--line-numbers"
         | 
| 44 | 
            +
              #rdoc.options << "--inline-source"
         | 
| 45 | 
            +
              rdoc.rdoc_files.include("lib/**/*.rb")
         | 
| 36 46 | 
             
            end
         | 
| 37 47 |  | 
| 38 48 | 
             
            desc "Compile README preview"
         | 
    
        data/VERSION.yml
    CHANGED
    
    
    
        data/init.rb
    CHANGED
    
    
    
        data/lib/smart_tuple.rb
    CHANGED
    
    | @@ -1,34 +1,50 @@ | |
| 1 | 
            +
            # SQL condition builder.
         | 
| 2 | 
            +
            #
         | 
| 3 | 
            +
            #   tup = SmartTuple.new(" AND ")
         | 
| 4 | 
            +
            #   tup << {:brand => "Nokia"}
         | 
| 5 | 
            +
            #   tup << ["min_price >= ?", 75]
         | 
| 6 | 
            +
            #   tup.compile   # => ["brand = ? AND min_price >= ?", "Nokia", 75]
         | 
| 1 7 | 
             
            class SmartTuple
         | 
| 8 | 
            +
              # Array of SQL argument parts.
         | 
| 2 9 | 
             
              attr_reader :args
         | 
| 10 | 
            +
             | 
| 11 | 
            +
              # Put brackets around statements. <tt>true</tt>, <tt>false</tt> or <tt>:auto</tt>. Default:
         | 
| 12 | 
            +
              #
         | 
| 13 | 
            +
              #   :auto
         | 
| 3 14 | 
             
              attr_reader :brackets
         | 
| 15 | 
            +
             | 
| 16 | 
            +
              # String to glue statements together.
         | 
| 4 17 | 
             
              attr_accessor :glue
         | 
| 18 | 
            +
             | 
| 19 | 
            +
              # Array of SQL statement parts.
         | 
| 5 20 | 
             
              attr_reader :statements
         | 
| 6 21 |  | 
| 22 | 
            +
              # Initializer.
         | 
| 23 | 
            +
              #
         | 
| 7 24 | 
             
              #   new(" AND ")
         | 
| 8 | 
            -
              #   new(" OR ")
         | 
| 9 | 
            -
              #   new(", ")       # E.g. for a SET or UPDATE statement.
         | 
| 25 | 
            +
              #   new(" OR ", :brackets => true)
         | 
| 10 26 | 
             
              def initialize(glue, attrs = {})
         | 
| 11 27 | 
             
                @glue = glue
         | 
| 12 28 | 
             
                clear
         | 
| 13 29 | 
             
                attrs.each {|k, v| send("#{k}=", v)}
         | 
| 14 30 | 
             
              end
         | 
| 15 31 |  | 
| 16 | 
            -
              #  | 
| 17 | 
            -
              def initialize_copy(src)
         | 
| 32 | 
            +
              # Service initializer for <tt>dup</tt>.
         | 
| 33 | 
            +
              def initialize_copy(src)    #:nodoc:
         | 
| 18 34 | 
             
                @statements = src.statements.dup
         | 
| 19 35 | 
             
                @args = src.args.dup
         | 
| 20 36 | 
             
              end
         | 
| 21 37 |  | 
| 22 | 
            -
              #  | 
| 23 | 
            -
             | 
| 24 | 
            -
              # Add a sub-statement, return new object. See <tt>#<<</tt>.
         | 
| 38 | 
            +
              # Add a statement, return new object. See #<<.
         | 
| 39 | 
            +
              #
         | 
| 25 40 | 
             
              #   SmartTuple.new(" AND ") + {:brand => "Nokia"} + ["max_price <= ?", 300]
         | 
| 26 | 
            -
              def +( | 
| 41 | 
            +
              def +(arg)
         | 
| 27 42 | 
             
                # Since #<< supports chaining, it boils down to this.
         | 
| 28 | 
            -
                dup <<  | 
| 43 | 
            +
                dup << arg
         | 
| 29 44 | 
             
              end
         | 
| 30 45 |  | 
| 31 | 
            -
              #  | 
| 46 | 
            +
              # Add a statement, return self.
         | 
| 47 | 
            +
              #
         | 
| 32 48 | 
             
              #   # Array.
         | 
| 33 49 | 
             
              #   tup << ["brand = ?", "Nokia"]
         | 
| 34 50 | 
             
              #   tup << ["brand = ? AND color = ?", "Nokia", "Black"]
         | 
| @@ -41,70 +57,71 @@ class SmartTuple | |
| 41 57 | 
             
              #   tup << other_tuple
         | 
| 42 58 | 
             
              #
         | 
| 43 59 | 
             
              #   # String. Generally NOT recommended.
         | 
| 44 | 
            -
              #   tup << " | 
| 60 | 
            +
              #   tup << "min_price >= 75"
         | 
| 61 | 
            +
              #
         | 
| 62 | 
            +
              # Adding anything empty or blank (where appropriate) has no effect on the receiver:
         | 
| 45 63 | 
             
              #
         | 
| 46 | 
            -
              # Appending empty or blank (where appropriate) statements has no effect on the receiver:
         | 
| 47 64 | 
             
              #   tup << nil
         | 
| 48 65 | 
             
              #   tup << []
         | 
| 49 66 | 
             
              #   tup << {}
         | 
| 50 67 | 
             
              #   tup << another_empty_tuple
         | 
| 51 68 | 
             
              #   tup << ""
         | 
| 52 69 | 
             
              #   tup << "  "     # Will be treated as blank if ActiveSupport is on.
         | 
| 53 | 
            -
              def <<( | 
| 54 | 
            -
                 | 
| 55 | 
            -
             | 
| 56 | 
            -
                # NOTE: Autobracketing help is placing [value] instead of (value) into @statements. #compile will take it into account.
         | 
| 70 | 
            +
              def <<(arg)
         | 
| 71 | 
            +
                # NOTE: Autobracketing is placing `[value]` instead of `value` into `@statements`. #compile understands it.
         | 
| 57 72 |  | 
| 58 73 | 
             
                # Chop off everything empty first time.
         | 
| 59 | 
            -
                if  | 
| 60 | 
            -
             | 
| 61 | 
            -
             | 
| 62 | 
            -
             | 
| 63 | 
            -
                   | 
| 64 | 
            -
                elsif sub.is_a? Array
         | 
| 65 | 
            -
                  # NOTE: If sub == [], the execution won't get here.
         | 
| 74 | 
            +
                if arg.nil? or (arg.empty? rescue false) or (arg.blank? rescue false)
         | 
| 75 | 
            +
                elsif arg.is_a? String or (arg.acts_like? :string rescue false)
         | 
| 76 | 
            +
                  @statements << arg.to_s
         | 
| 77 | 
            +
                elsif arg.is_a? Array
         | 
| 78 | 
            +
                  # NOTE: If arg == [], the execution won't get here.
         | 
| 66 79 | 
             
                  #       So, we've got at least one element. Therefore stmt will be scalar, and args -- DEFINITELY an array.
         | 
| 67 | 
            -
                  stmt, args =  | 
| 68 | 
            -
                  ##p "stmt", stmt
         | 
| 69 | 
            -
                  ##p "args", args
         | 
| 80 | 
            +
                  stmt, args = arg[0], arg[1..-1]
         | 
| 70 81 | 
             
                  if not (stmt.nil? or (stmt.empty? rescue false) or (stmt.blank? rescue false))
         | 
| 71 | 
            -
                    ##puts "-- stmt nempty"
         | 
| 72 82 | 
             
                    # Help do autobracketing later. Here we can ONLY judge by number of passed arguments.
         | 
| 73 83 | 
             
                    @statements << (args.size > 1 ? [stmt] : stmt)
         | 
| 74 84 | 
             
                    @args += args
         | 
| 75 85 | 
             
                  end
         | 
| 76 | 
            -
                elsif  | 
| 77 | 
            -
                   | 
| 78 | 
            -
                     | 
| 79 | 
            -
             | 
| 86 | 
            +
                elsif arg.is_a? Hash
         | 
| 87 | 
            +
                  arg.each do |k, v|
         | 
| 88 | 
            +
                    if v.nil?
         | 
| 89 | 
            +
                      # NOTE: AR supports it for Hashes only. ["kk = ?", nil] will not be converted.
         | 
| 90 | 
            +
                      @statements << "#{k} IS NULL"
         | 
| 91 | 
            +
                    else
         | 
| 92 | 
            +
                      @statements << "#{k} = ?"
         | 
| 93 | 
            +
                      @args << v
         | 
| 94 | 
            +
                    end
         | 
| 80 95 | 
             
                  end
         | 
| 81 | 
            -
                elsif  | 
| 82 | 
            -
                  # NOTE: If  | 
| 96 | 
            +
                elsif arg.is_a? self.class
         | 
| 97 | 
            +
                  # NOTE: If arg is empty, the execution won't get here.
         | 
| 83 98 |  | 
| 84 99 | 
             
                  # Autobrackets here are smarter, than in Array processing case.
         | 
| 85 | 
            -
                  stmt =  | 
| 86 | 
            -
                  @statements << (( | 
| 87 | 
            -
                  @args +=  | 
| 100 | 
            +
                  stmt = arg.compile[0]
         | 
| 101 | 
            +
                  @statements << ((arg.size > 1 or arg.args.size > 1) ? [stmt] : stmt)
         | 
| 102 | 
            +
                  @args += arg.args
         | 
| 88 103 | 
             
                else
         | 
| 89 | 
            -
                  raise ArgumentError, "Invalid  | 
| 104 | 
            +
                  raise ArgumentError, "Invalid statement #{arg.inspect}"
         | 
| 90 105 | 
             
                end
         | 
| 91 106 |  | 
| 92 107 | 
             
                # Return self, it's IMPORTANT to make chaining possible.
         | 
| 93 108 | 
             
                self
         | 
| 94 109 | 
             
              end
         | 
| 95 110 |  | 
| 96 | 
            -
              # Iterate over collection and add block's result per each record.
         | 
| 111 | 
            +
              # Iterate over collection and add block's result to self once per each record.
         | 
| 112 | 
            +
              #
         | 
| 97 113 | 
             
              #   add_each(brands) do |v|
         | 
| 98 114 | 
             
              #     ["brand = ?", v]
         | 
| 99 115 | 
             
              #   end
         | 
| 100 116 | 
             
              #
         | 
| 101 117 | 
             
              # Can be conditional:
         | 
| 118 | 
            +
              #
         | 
| 102 119 | 
             
              #   tup.add_each(["Nokia", "Motorola"]) do |v|
         | 
| 103 120 | 
             
              #     ["brand = ?", v] if v =~ /^Moto/
         | 
| 104 121 | 
             
              #   end
         | 
| 105 122 | 
             
              def add_each(collection, &block)
         | 
| 106 123 | 
             
                raise ArgumentError, "Code block expected" if not block
         | 
| 107 | 
            -
             | 
| 124 | 
            +
             | 
| 108 125 | 
             
                collection.each do |v|
         | 
| 109 126 | 
             
                  self << yield(v)
         | 
| 110 127 | 
             
                end
         | 
| @@ -114,6 +131,7 @@ class SmartTuple | |
| 114 131 | 
             
              end
         | 
| 115 132 |  | 
| 116 133 | 
             
              # Set bracketing mode.
         | 
| 134 | 
            +
              #
         | 
| 117 135 | 
             
              #   brackets = true         # Put brackets around each sub-statement.
         | 
| 118 136 | 
             
              #   brackets = false        # Don't put brackets.
         | 
| 119 137 | 
             
              #   brackets = :auto        # Automatically put brackets around compound sub-statements.
         | 
| @@ -122,23 +140,23 @@ class SmartTuple | |
| 122 140 | 
             
                @brackets = value
         | 
| 123 141 | 
             
              end
         | 
| 124 142 |  | 
| 125 | 
            -
              #  | 
| 143 | 
            +
              # Clear self.
         | 
| 126 144 | 
             
              def clear
         | 
| 127 145 | 
             
                @statements = []
         | 
| 128 146 | 
             
                @args = []
         | 
| 129 147 | 
             
                @brackets = :auto
         | 
| 130 148 |  | 
| 131 | 
            -
                # Array does it like this. We do either.
         | 
| 149 | 
            +
                # `Array` does it like this. We do either.
         | 
| 132 150 | 
             
                self
         | 
| 133 151 | 
             
              end
         | 
| 134 152 |  | 
| 135 | 
            -
              # Compile self into an array.
         | 
| 153 | 
            +
              # Compile self into an array. Empty self yields empty array.
         | 
| 154 | 
            +
              #
         | 
| 155 | 
            +
              #   compile   # => []
         | 
| 156 | 
            +
              #   compile   # => ["brand = ? AND min_price >= ?", "Nokia", 75]
         | 
| 136 157 | 
             
              def compile
         | 
| 137 158 | 
             
                return [] if empty?
         | 
| 138 159 |  | 
| 139 | 
            -
                ##p "@statements", @statements
         | 
| 140 | 
            -
                ##p "@args", @args
         | 
| 141 | 
            -
             | 
| 142 160 | 
             
                # Build "bracketed" statements.
         | 
| 143 161 | 
             
                bsta = @statements.map do |s|
         | 
| 144 162 | 
             
                  auto_brackets, scalar_s = s.is_a?(Array) ? [true, s[0]] : [false, s]
         | 
| @@ -173,6 +191,10 @@ class SmartTuple | |
| 173 191 | 
             
              end
         | 
| 174 192 | 
             
              alias_method :to_a, :compile
         | 
| 175 193 |  | 
| 194 | 
            +
              # Return <tt>true</tt> if self is empty.
         | 
| 195 | 
            +
              #
         | 
| 196 | 
            +
              #   tup = SmartTuple.new(" AND ")
         | 
| 197 | 
            +
              #   tup.empty?    # => true
         | 
| 176 198 | 
             
              def empty?
         | 
| 177 199 | 
             
                @statements.empty?
         | 
| 178 200 | 
             
              end
         | 
    
        data/smart_tuple.gemspec
    CHANGED
    
    | @@ -1,43 +1,39 @@ | |
| 1 1 | 
             
            # Generated by jeweler
         | 
| 2 | 
            -
            # DO NOT EDIT THIS FILE
         | 
| 3 | 
            -
            # Instead, edit Jeweler::Tasks in Rakefile, and run  | 
| 2 | 
            +
            # DO NOT EDIT THIS FILE DIRECTLY
         | 
| 3 | 
            +
            # Instead, edit Jeweler::Tasks in Rakefile, and run 'rake gemspec'
         | 
| 4 4 | 
             
            # -*- encoding: utf-8 -*-
         | 
| 5 5 |  | 
| 6 6 | 
             
            Gem::Specification.new do |s|
         | 
| 7 7 | 
             
              s.name = %q{smart_tuple}
         | 
| 8 | 
            -
              s.version = "0.1. | 
| 8 | 
            +
              s.version = "0.1.2"
         | 
| 9 9 |  | 
| 10 10 | 
             
              s.required_rubygems_version = Gem::Requirement.new(">= 0") if s.respond_to? :required_rubygems_version=
         | 
| 11 11 | 
             
              s.authors = ["Alex Fortuna"]
         | 
| 12 | 
            -
              s.date = %q{ | 
| 12 | 
            +
              s.date = %q{2011-07-14}
         | 
| 13 13 | 
             
              s.description = %q{A Simple Yet Smart SQL Conditions Builder}
         | 
| 14 14 | 
             
              s.email = %q{alex.r@askit.org}
         | 
| 15 15 | 
             
              s.extra_rdoc_files = [
         | 
| 16 16 | 
             
                "README.html",
         | 
| 17 | 
            -
             | 
| 17 | 
            +
                "README.md"
         | 
| 18 18 | 
             
              ]
         | 
| 19 19 | 
             
              s.files = [
         | 
| 20 20 | 
             
                "MIT-LICENSE",
         | 
| 21 | 
            -
             | 
| 22 | 
            -
             | 
| 23 | 
            -
             | 
| 24 | 
            -
             | 
| 25 | 
            -
             | 
| 26 | 
            -
             | 
| 27 | 
            -
             | 
| 21 | 
            +
                "README.html",
         | 
| 22 | 
            +
                "README.md",
         | 
| 23 | 
            +
                "Rakefile",
         | 
| 24 | 
            +
                "VERSION.yml",
         | 
| 25 | 
            +
                "init.rb",
         | 
| 26 | 
            +
                "lib/smart_tuple.rb",
         | 
| 27 | 
            +
                "smart_tuple.gemspec",
         | 
| 28 | 
            +
                "spec/smart_tuple_spec.rb",
         | 
| 29 | 
            +
                "spec/spec_helper.rb"
         | 
| 28 30 | 
             
              ]
         | 
| 29 31 | 
             
              s.homepage = %q{http://github.com/dadooda/smart_tuple}
         | 
| 30 | 
            -
              s.rdoc_options = ["--charset=UTF-8"]
         | 
| 31 32 | 
             
              s.require_paths = ["lib"]
         | 
| 32 | 
            -
              s.rubygems_version = %q{1. | 
| 33 | 
            +
              s.rubygems_version = %q{1.6.2}
         | 
| 33 34 | 
             
              s.summary = %q{A Simple Yet Smart SQL Conditions Builder}
         | 
| 34 | 
            -
              s.test_files = [
         | 
| 35 | 
            -
                "spec/spec_helper.rb",
         | 
| 36 | 
            -
                 "spec/smart_tuple_spec.rb"
         | 
| 37 | 
            -
              ]
         | 
| 38 35 |  | 
| 39 36 | 
             
              if s.respond_to? :specification_version then
         | 
| 40 | 
            -
                current_version = Gem::Specification::CURRENT_SPECIFICATION_VERSION
         | 
| 41 37 | 
             
                s.specification_version = 3
         | 
| 42 38 |  | 
| 43 39 | 
             
                if Gem::Version.new(Gem::VERSION) >= Gem::Version.new('1.2.0') then
         | 
| @@ -46,3 +42,4 @@ Gem::Specification.new do |s| | |
| 46 42 | 
             
              else
         | 
| 47 43 | 
             
              end
         | 
| 48 44 | 
             
            end
         | 
| 45 | 
            +
             | 
    
        data/spec/smart_tuple_spec.rb
    CHANGED
    
    | @@ -188,6 +188,7 @@ describe (klass = SmartTuple) do | |
| 188 188 | 
             
                end
         | 
| 189 189 | 
             
              end # #+
         | 
| 190 190 |  | 
| 191 | 
            +
              # Most tests are here, since arg conversion is performed right in `<<`.
         | 
| 191 192 | 
             
              describe "#<<" do
         | 
| 192 193 | 
             
                it "ignores nil/empty/blank objects" do
         | 
| 193 194 | 
             
                  objs = []
         | 
| @@ -210,6 +211,14 @@ describe (klass = SmartTuple) do | |
| 210 211 | 
             
                  (r << ["is_male = ?", true]).should eql r
         | 
| 211 212 | 
             
                end
         | 
| 212 213 |  | 
| 214 | 
            +
                it "supports IS NULL for Hash" do
         | 
| 215 | 
            +
                  r << {:kk => nil}
         | 
| 216 | 
            +
                  r.compile.should == ["kk IS NULL"]
         | 
| 217 | 
            +
             | 
| 218 | 
            +
                  r << {:mkk => 10}
         | 
| 219 | 
            +
                  r.compile.should == ["kk IS NULL AND mkk = ?", 10]
         | 
| 220 | 
            +
                end
         | 
| 221 | 
            +
             | 
| 213 222 | 
             
                it "supports chaining" do
         | 
| 214 223 | 
             
                  r << ["age >= ?", 18] << "created_at IS NULL"
         | 
| 215 224 | 
             
                  r.compile.should == ["age >= ? AND created_at IS NULL", 18]
         | 
    
        metadata
    CHANGED
    
    | @@ -1,13 +1,8 @@ | |
| 1 1 | 
             
            --- !ruby/object:Gem::Specification 
         | 
| 2 2 | 
             
            name: smart_tuple
         | 
| 3 3 | 
             
            version: !ruby/object:Gem::Version 
         | 
| 4 | 
            -
               | 
| 5 | 
            -
               | 
| 6 | 
            -
              segments: 
         | 
| 7 | 
            -
              - 0
         | 
| 8 | 
            -
              - 1
         | 
| 9 | 
            -
              - 1
         | 
| 10 | 
            -
              version: 0.1.1
         | 
| 4 | 
            +
              prerelease: 
         | 
| 5 | 
            +
              version: 0.1.2
         | 
| 11 6 | 
             
            platform: ruby
         | 
| 12 7 | 
             
            authors: 
         | 
| 13 8 | 
             
            - Alex Fortuna
         | 
| @@ -15,7 +10,7 @@ autorequire: | |
| 15 10 | 
             
            bindir: bin
         | 
| 16 11 | 
             
            cert_chain: []
         | 
| 17 12 |  | 
| 18 | 
            -
            date:  | 
| 13 | 
            +
            date: 2011-07-14 00:00:00 +04:00
         | 
| 19 14 | 
             
            default_executable: 
         | 
| 20 15 | 
             
            dependencies: []
         | 
| 21 16 |  | 
| @@ -37,15 +32,15 @@ files: | |
| 37 32 | 
             
            - init.rb
         | 
| 38 33 | 
             
            - lib/smart_tuple.rb
         | 
| 39 34 | 
             
            - smart_tuple.gemspec
         | 
| 40 | 
            -
            - spec/spec_helper.rb
         | 
| 41 35 | 
             
            - spec/smart_tuple_spec.rb
         | 
| 36 | 
            +
            - spec/spec_helper.rb
         | 
| 42 37 | 
             
            has_rdoc: true
         | 
| 43 38 | 
             
            homepage: http://github.com/dadooda/smart_tuple
         | 
| 44 39 | 
             
            licenses: []
         | 
| 45 40 |  | 
| 46 41 | 
             
            post_install_message: 
         | 
| 47 | 
            -
            rdoc_options: 
         | 
| 48 | 
            -
             | 
| 42 | 
            +
            rdoc_options: []
         | 
| 43 | 
            +
             | 
| 49 44 | 
             
            require_paths: 
         | 
| 50 45 | 
             
            - lib
         | 
| 51 46 | 
             
            required_ruby_version: !ruby/object:Gem::Requirement 
         | 
| @@ -53,26 +48,19 @@ required_ruby_version: !ruby/object:Gem::Requirement | |
| 53 48 | 
             
              requirements: 
         | 
| 54 49 | 
             
              - - ">="
         | 
| 55 50 | 
             
                - !ruby/object:Gem::Version 
         | 
| 56 | 
            -
                  hash: 3
         | 
| 57 | 
            -
                  segments: 
         | 
| 58 | 
            -
                  - 0
         | 
| 59 51 | 
             
                  version: "0"
         | 
| 60 52 | 
             
            required_rubygems_version: !ruby/object:Gem::Requirement 
         | 
| 61 53 | 
             
              none: false
         | 
| 62 54 | 
             
              requirements: 
         | 
| 63 55 | 
             
              - - ">="
         | 
| 64 56 | 
             
                - !ruby/object:Gem::Version 
         | 
| 65 | 
            -
                  hash: 3
         | 
| 66 | 
            -
                  segments: 
         | 
| 67 | 
            -
                  - 0
         | 
| 68 57 | 
             
                  version: "0"
         | 
| 69 58 | 
             
            requirements: []
         | 
| 70 59 |  | 
| 71 60 | 
             
            rubyforge_project: 
         | 
| 72 | 
            -
            rubygems_version: 1. | 
| 61 | 
            +
            rubygems_version: 1.6.2
         | 
| 73 62 | 
             
            signing_key: 
         | 
| 74 63 | 
             
            specification_version: 3
         | 
| 75 64 | 
             
            summary: A Simple Yet Smart SQL Conditions Builder
         | 
| 76 | 
            -
            test_files: 
         | 
| 77 | 
            -
             | 
| 78 | 
            -
            - spec/smart_tuple_spec.rb
         | 
| 65 | 
            +
            test_files: []
         | 
| 66 | 
            +
             |