native-query 0.9.0
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/.document +5 -0
- data/Gemfile +21 -0
- data/Gemfile.lock +25 -0
- data/LICENSE.txt +20 -0
- data/README.md +462 -0
- data/Rakefile +31 -0
- data/VERSION +1 -0
- data/lib/native-query/join.rb +318 -0
- data/lib/native-query/model.rb +76 -0
- data/lib/native-query/query.rb +430 -0
- data/lib/native-query/result.rb +113 -0
- data/lib/native-query/row.rb +75 -0
- data/native-query.gemspec +60 -0
- metadata +113 -0
data/.document
ADDED
data/Gemfile
ADDED
@@ -0,0 +1,21 @@
|
|
1
|
+
source "http://rubygems.org"
|
2
|
+
# Add dependencies required to use your gem here.
|
3
|
+
# Example:
|
4
|
+
gem "fluent-query", ">= 0.9.0"
|
5
|
+
gem "hash-utils", ">= 0.18.0"
|
6
|
+
|
7
|
+
# Add dependencies to develop your gem here.
|
8
|
+
# Include everything needed to run rake, tests, features, etc.
|
9
|
+
group :development do
|
10
|
+
gem "bundler", "~> 1.0.13"
|
11
|
+
gem "jeweler", "~> 1.6.0"
|
12
|
+
end
|
13
|
+
|
14
|
+
|
15
|
+
|
16
|
+
# fluent-query-sql
|
17
|
+
# fluent-query-dbh
|
18
|
+
# fluent-query-mysql
|
19
|
+
# fluent-query-sqlite
|
20
|
+
# fluent-query-postgresql
|
21
|
+
# native-query
|
data/Gemfile.lock
ADDED
@@ -0,0 +1,25 @@
|
|
1
|
+
GEM
|
2
|
+
remote: http://rubygems.org/
|
3
|
+
specs:
|
4
|
+
abstract (1.0.0)
|
5
|
+
fluent-query (0.9.0)
|
6
|
+
abstract (>= 1.0.0)
|
7
|
+
hash-utils (>= 0.18.0)
|
8
|
+
hashie (>= 1.0.0)
|
9
|
+
git (1.2.5)
|
10
|
+
hash-utils (0.18.0)
|
11
|
+
hashie (1.0.0)
|
12
|
+
jeweler (1.6.3)
|
13
|
+
bundler (~> 1.0)
|
14
|
+
git (>= 1.2.5)
|
15
|
+
rake
|
16
|
+
rake (0.9.2)
|
17
|
+
|
18
|
+
PLATFORMS
|
19
|
+
ruby
|
20
|
+
|
21
|
+
DEPENDENCIES
|
22
|
+
bundler (~> 1.0.13)
|
23
|
+
fluent-query (>= 0.9.0)
|
24
|
+
hash-utils (>= 0.18.0)
|
25
|
+
jeweler (~> 1.6.0)
|
data/LICENSE.txt
ADDED
@@ -0,0 +1,20 @@
|
|
1
|
+
Copyright (c) 2010 - 2011 Martin Kozák (martinkozak@martinkozak.net)
|
2
|
+
|
3
|
+
Permission is hereby granted, free of charge, to any person obtaining
|
4
|
+
a copy of this software and associated documentation files (the
|
5
|
+
"Software"), to deal in the Software without restriction, including
|
6
|
+
without limitation the rights to use, copy, modify, merge, publish,
|
7
|
+
distribute, sublicense, and/or sell copies of the Software, and to
|
8
|
+
permit persons to whom the Software is furnished to do so, subject to
|
9
|
+
the following conditions:
|
10
|
+
|
11
|
+
The above copyright notice and this permission notice shall be
|
12
|
+
included in all copies or substantial portions of the Software.
|
13
|
+
|
14
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
|
15
|
+
EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
|
16
|
+
MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
|
17
|
+
NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
|
18
|
+
LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
|
19
|
+
OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
|
20
|
+
WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
data/README.md
ADDED
@@ -0,0 +1,462 @@
|
|
1
|
+
Native Query
|
2
|
+
============
|
3
|
+
|
4
|
+
**Native Query** is cool way how to speak with database server. It's
|
5
|
+
ellegant and very ruby SQL query helper which works by similar way as
|
6
|
+
Arel or another ORM selecting logic. It's derived from [Dibi][1]
|
7
|
+
database layer in its ideas, so is much more simple and (of sure) much
|
8
|
+
more KISS, readable and straightforward.
|
9
|
+
|
10
|
+
It's build on top of the general [Fluent Query][2] library which servers as
|
11
|
+
underlying layer, so can be extended to almost whatever – and not-only
|
12
|
+
database – platform.
|
13
|
+
|
14
|
+
### Connecting
|
15
|
+
|
16
|
+
# Include it!
|
17
|
+
require "fluent-query/mysql"
|
18
|
+
require "native-query"
|
19
|
+
|
20
|
+
# Setup it!
|
21
|
+
driver = FluentQuery::Drivers::MySQL
|
22
|
+
settings = {
|
23
|
+
:username => "wikistatistics.net",
|
24
|
+
:password => "alfabeta",
|
25
|
+
:server => "localhost",
|
26
|
+
:port => 5432,
|
27
|
+
:database => "wikistatistics.net",
|
28
|
+
:schema => "public"
|
29
|
+
}
|
30
|
+
|
31
|
+
# Create it!
|
32
|
+
model = NativeQuery::Model::new(driver, settings)
|
33
|
+
|
34
|
+
Now we have model prepared for use.
|
35
|
+
|
36
|
+
### Selecting
|
37
|
+
|
38
|
+
Simply call method accroding to table name above the model. Its
|
39
|
+
arguments will be fields which you would like to select:
|
40
|
+
|
41
|
+
records = model.maintainers :name, :code do
|
42
|
+
...
|
43
|
+
get.all
|
44
|
+
end
|
45
|
+
|
46
|
+
The last command in the block is getter. You can take `all` records,
|
47
|
+
`one` record only or `single` (first) value of first row. `assoc` method
|
48
|
+
is described below.
|
49
|
+
|
50
|
+
Traversing through returned records is simple of sure:
|
51
|
+
|
52
|
+
records.each do |row|
|
53
|
+
p row.code, row.name
|
54
|
+
end
|
55
|
+
|
56
|
+
#### Associative Fetching
|
57
|
+
|
58
|
+
Special associative method is the `assoc` one which is directly inspired
|
59
|
+
by appropriate feature of the [Dibi][1] layer. It's aim is automatic
|
60
|
+
aggregation of returned rows to multidimensional Hashes.
|
61
|
+
|
62
|
+
Simply give it key names from your dataset. Be warn, only one or two
|
63
|
+
levels (e.g. dimesions in resultant Hash) are supported:
|
64
|
+
|
65
|
+
records = model.sites :maintainer_id, :language, :name do
|
66
|
+
# ...
|
67
|
+
get.assoc :maintainer_id, :language
|
68
|
+
end
|
69
|
+
|
70
|
+
Will transform the dataset:
|
71
|
+
|
72
|
+
# maintainer_id, language, name
|
73
|
+
[1, "en", "English Wikipedia"],
|
74
|
+
[1, "es", "Spain Wikipedia"],
|
75
|
+
[2, "cs", "Czech Wikihow"],
|
76
|
+
[2, "ja", "Japan Wikihow"],
|
77
|
+
|
78
|
+
To the following structure:
|
79
|
+
|
80
|
+
1 => {
|
81
|
+
"en" => "English Wikipedia",
|
82
|
+
"es" => "Spain Wikipedia"
|
83
|
+
},
|
84
|
+
|
85
|
+
2 => {
|
86
|
+
"cs" => "Czech Wikihow",
|
87
|
+
"ja" => "Japan Wikihow"
|
88
|
+
}
|
89
|
+
|
90
|
+
### Conditions, ordering and limits
|
91
|
+
|
92
|
+
Limits and offsets are simple too:
|
93
|
+
|
94
|
+
records = model.maintainers :name, :code do
|
95
|
+
# ...
|
96
|
+
offset 5
|
97
|
+
limit 3
|
98
|
+
# ...
|
99
|
+
end
|
100
|
+
|
101
|
+
Will select sixth, seventh and eighth record.
|
102
|
+
|
103
|
+
#### Conditions
|
104
|
+
|
105
|
+
Conditions (`WHERE` equivalent) receives Ruby's native data types. So
|
106
|
+
simply call:
|
107
|
+
|
108
|
+
records = model.maintainers :name, :code do
|
109
|
+
where :active => true
|
110
|
+
where :id => 5
|
111
|
+
# ...
|
112
|
+
end
|
113
|
+
|
114
|
+
These confitions are simple and `AND` equivalency of sure. Because aim
|
115
|
+
is to be simple and to don't complicate rather nice interface by giant
|
116
|
+
stuff of sophisticated and complicated calls, you can provide whatever
|
117
|
+
condition using FluentQuery strings:
|
118
|
+
|
119
|
+
records = model.maintainers :name, :code do
|
120
|
+
# ...
|
121
|
+
where "[id] > 5"
|
122
|
+
where "[name] IN %%l", names
|
123
|
+
where "%%or", :id => 10, :name => "Wikia, Inc."
|
124
|
+
# ...
|
125
|
+
end
|
126
|
+
|
127
|
+
Brackets always means "this identifer is a field name". See description
|
128
|
+
of the [Fluent Query][2] below.
|
129
|
+
|
130
|
+
#### Ordering
|
131
|
+
|
132
|
+
Orders work by very predictable way. For example:
|
133
|
+
|
134
|
+
records = model.maintainers :name, :code do
|
135
|
+
# ...
|
136
|
+
order :name, :desc
|
137
|
+
order :date, :asc, :id, :asc
|
138
|
+
# ...
|
139
|
+
end
|
140
|
+
|
141
|
+
Means "order by `name DESC` and then by `date, id ASC`". You can combine
|
142
|
+
both of styles mentioned above. If you need order by joined fields,
|
143
|
+
simply replace symbol by array with table name and field name as you can
|
144
|
+
see in advanced example below.
|
145
|
+
|
146
|
+
### Joining
|
147
|
+
|
148
|
+
Two kinds of joining are available: *automatic* and *manual*. They have
|
149
|
+
the same syntax principially, for manual joining is necessary to provide
|
150
|
+
more informations of sure.
|
151
|
+
|
152
|
+
#### Manual Joining
|
153
|
+
|
154
|
+
For manual joining simply type:
|
155
|
+
|
156
|
+
records = model.maintainers :name, :code, :sites_code, :sites_name do
|
157
|
+
# ...
|
158
|
+
sites :code, :name, :language_name do
|
159
|
+
direct :site_id => :id
|
160
|
+
# ...
|
161
|
+
end
|
162
|
+
# ...
|
163
|
+
end
|
164
|
+
|
165
|
+
Which means select from table `maintainers` and join it with
|
166
|
+
table `sites` by N:1 (direct) relation. Yes, you can join directly by
|
167
|
+
"calling the table" and treating its block as your primary table. It's
|
168
|
+
ellegant and very readable. For next level of joining simply do the same
|
169
|
+
in the inner block.
|
170
|
+
|
171
|
+
All fields selected from the joined table are prefixed by its name and
|
172
|
+
it's necessary of sure to tell interpret you want return them, as you
|
173
|
+
can see above. It's practical because you know about orgination of the
|
174
|
+
field whenever further in your source code.
|
175
|
+
|
176
|
+
Slightly more complicated is M:N relation type which works in
|
177
|
+
semiautomatic way only:
|
178
|
+
|
179
|
+
records = model.maintainers :name, :code, :sites_code, :sites_name do
|
180
|
+
# ...
|
181
|
+
sites :code, :name, :language_name do
|
182
|
+
indirect :sites_maintainers, :id => :id
|
183
|
+
# ...
|
184
|
+
end
|
185
|
+
# ...
|
186
|
+
end
|
187
|
+
|
188
|
+
Which means the same as:
|
189
|
+
|
190
|
+
SELECT ... FROM `maintainers`
|
191
|
+
JOIN `sites_maintainers` ON `maintainers`.`id` = `sites_maintainers`.`maintainers_id`
|
192
|
+
JOIN `sites` ON `sites_maintainers`.`sites_id` = `site`.`id`
|
193
|
+
...
|
194
|
+
|
195
|
+
Only `LEFT JOIN` is supported. For other joining types, use direct
|
196
|
+
*Fluent Query* interface (see below). Special conditions in `ON` clausule
|
197
|
+
is possible to achieve simply by giving the *Fluent Query* string:
|
198
|
+
|
199
|
+
records = model.maintainers :name, :code, :sites_code, :sites_name do
|
200
|
+
# ...
|
201
|
+
sites :code, :name, :language_name do
|
202
|
+
indirect :sites_maintainers, "[maintainers.id] = [sites_maintainers.strange_1]", "[sites_maintainers.strange_2] = [site.id]"
|
203
|
+
# ...
|
204
|
+
end
|
205
|
+
# ...
|
206
|
+
end
|
207
|
+
|
208
|
+
And the same for direct joining of sure.
|
209
|
+
|
210
|
+
#### Automatic joining
|
211
|
+
|
212
|
+
Automatic joining is recommended joining way although it has some strict
|
213
|
+
requirements for table and field names:
|
214
|
+
|
215
|
+
* primary keys are expected to be named `id`,
|
216
|
+
* foreign key fields are expected to be named `<target-table>_id`,
|
217
|
+
* M:N linking tables are expected to be named `<source-table>_<target-table>`.
|
218
|
+
|
219
|
+
But then you can use the following nice syntax for both *direct*:
|
220
|
+
|
221
|
+
records = model.maintainers :name, :code, :sites_code, :sites_name do
|
222
|
+
# ...
|
223
|
+
sites :code, :name, :language_name do
|
224
|
+
direct
|
225
|
+
# ...
|
226
|
+
end
|
227
|
+
# ...
|
228
|
+
end
|
229
|
+
|
230
|
+
Which will be transformed approximately (it's driver dependent) into:
|
231
|
+
|
232
|
+
SELECT `name`, `code`, `sites`.`code`, `sites`.`name`
|
233
|
+
FROM `maintainers`
|
234
|
+
JOIN `sites` ON `maintainers`.`id` = `sites`.`maintainer_id`
|
235
|
+
...
|
236
|
+
|
237
|
+
Or *indirect*:
|
238
|
+
|
239
|
+
records = model.maintainers :name, :code, :sites_code, :sites_name do
|
240
|
+
# ...
|
241
|
+
sites :code, :name, :language_name do
|
242
|
+
indirect
|
243
|
+
# ...
|
244
|
+
end
|
245
|
+
# ...
|
246
|
+
end
|
247
|
+
|
248
|
+
Which will be transformed approximately (it's driver dependent) into:
|
249
|
+
|
250
|
+
SELECT `name`, `code`, `sites`.`code` AS `sites_code`, `sites`.`name` AS `sites_name`
|
251
|
+
FROM `maintainers`
|
252
|
+
JOIN `maintainers_sites` ON `maintainers`.`id` = `maintainers_sites`.`maintainer_id`
|
253
|
+
JOIN `sites` ON `maintainers_sites`.`site_id` = `site`.`id`
|
254
|
+
...
|
255
|
+
|
256
|
+
Should be noted, if you need *backward indirect* joining (so in opposite
|
257
|
+
direction than in examples above), simply call `direct backward` or
|
258
|
+
`indirect backward`.
|
259
|
+
|
260
|
+
### Inserts, Updates and Deletes
|
261
|
+
|
262
|
+
Native Query doesn't support native inserting, updating and deleting,
|
263
|
+
but provides bridge to appropriate Fluent Query methods. Some examples:
|
264
|
+
|
265
|
+
model.insert(:maintainers, :name => "Wikimedia", :country => "United States")
|
266
|
+
|
267
|
+
# Will be:
|
268
|
+
# INSERT INTO `maintainers` (`name`, `country`) VALUES ("Wikimedia", "United States")
|
269
|
+
|
270
|
+
model.update(:maintainers).set(:country => "Czech Republic").where(:id => 10).limit(1)
|
271
|
+
|
272
|
+
# Will be:
|
273
|
+
# UPDATE `maintainers` SET `country` = "Czech Republic" WHERE `id` = 10 LIMIT 1
|
274
|
+
|
275
|
+
model.delete(:maintainers).where(:id => 10).limit(1)
|
276
|
+
|
277
|
+
# Will be:
|
278
|
+
# DELETE FROM `maintainers` WHERE `id` = 10 LIMIT 1
|
279
|
+
|
280
|
+
#### Transactions
|
281
|
+
|
282
|
+
Transactions support is available manual:
|
283
|
+
|
284
|
+
* `model.begin`
|
285
|
+
* `model.commit`
|
286
|
+
* `model.rollback`
|
287
|
+
|
288
|
+
Or by automatic way:
|
289
|
+
|
290
|
+
model.transaction do
|
291
|
+
#...
|
292
|
+
end
|
293
|
+
|
294
|
+
### Fluent Queries
|
295
|
+
|
296
|
+
The *Native Query* library is built on top of the [Fluent Query][2]
|
297
|
+
library which provides way how to fluently translate series of method
|
298
|
+
calls to some query language (but typically SQL). Some example:
|
299
|
+
|
300
|
+
model.select("[id], [name]").from("[maintainers]").orderBy("[code] ASC")
|
301
|
+
|
302
|
+
Will be rendered to:
|
303
|
+
|
304
|
+
SELECT `id`, `name` FROM `maintainers` ORDER BY `code` ASC
|
305
|
+
|
306
|
+
It looks trivial, but for example call `model.heyReturnMeSomething("[yeah]")`
|
307
|
+
will be transformed to:
|
308
|
+
|
309
|
+
HEY RETURN ME SOMETHING `yeah`
|
310
|
+
|
311
|
+
Which gives big potential. Of sure, escaping, aggregation and chaining
|
312
|
+
of chunks for example for `WHERE` directive or another is necessary.
|
313
|
+
It's ensured by appropriate *language* (e.g. database) *driver*.
|
314
|
+
|
315
|
+
And what a more: order of tokens isn't mandatory, so with exception
|
316
|
+
of initial world (`SELECT`, `INSERT` etc.) you can add them according to
|
317
|
+
your needs.
|
318
|
+
|
319
|
+
#### Placeholders
|
320
|
+
|
321
|
+
Simple translation calls to queries isn't the only functionality. Very
|
322
|
+
helpful are also *placeholders*. They works principially by the same way
|
323
|
+
as `#printf` method, but are more suitable for use in queries and
|
324
|
+
supports automatic quoting. Available are:
|
325
|
+
|
326
|
+
* `%%s` which quotes string,
|
327
|
+
* `%%i` which quotes integer,
|
328
|
+
* `%%b` which quotes boolean,
|
329
|
+
* `%%f` which quotes float,
|
330
|
+
* `%%d` which quotes date,
|
331
|
+
* `%%t` which quotes date-time,
|
332
|
+
|
333
|
+
And also three special:
|
334
|
+
|
335
|
+
* `%%sql` which quotes subquery (expects query object),
|
336
|
+
* `%%and` which joins input by `AND` operator (expects hash),
|
337
|
+
* `%%or` which joins input by `OR` operator (expects hash).
|
338
|
+
|
339
|
+
An example:
|
340
|
+
|
341
|
+
model.select("[id], [name]") \
|
342
|
+
.from("[maintainers]") \
|
343
|
+
.where("[id] = %%i AND company = %%s", 5, "Wikia") \
|
344
|
+
.where("[language] IN %%l", ["cz", "en"]) \
|
345
|
+
.or \
|
346
|
+
.where("[active] IS %%b", true)
|
347
|
+
|
348
|
+
Will be transformed to:
|
349
|
+
|
350
|
+
SELECT `id`, `name` FROM `maintainers`
|
351
|
+
WHERE `id` = 5
|
352
|
+
AND `company` = "Wikia"
|
353
|
+
AND `language` IN ("cz", "en")
|
354
|
+
OR `active` IS TRUE
|
355
|
+
|
356
|
+
It's way how to write complex or special queries. But **direct values
|
357
|
+
assigning is supported**, so for example:
|
358
|
+
|
359
|
+
model.select(:id, :name) \
|
360
|
+
.from(:maintainers) \
|
361
|
+
.where(:id => 5, :company => "Wikia") \
|
362
|
+
.where("[language] IN %%l", ["cz", "en"]) # %l will join items by commas
|
363
|
+
.or \
|
364
|
+
.where(:active => true)
|
365
|
+
|
366
|
+
Will give you expected result too and as you can see, it's much more
|
367
|
+
readable, flexible, so preferred.
|
368
|
+
|
369
|
+
#### Relation to Native Query
|
370
|
+
|
371
|
+
You can take Fluent Query object from the Native Query by:
|
372
|
+
|
373
|
+
# Query it!
|
374
|
+
query = model.maintainers :name, :code do
|
375
|
+
where :active => true
|
376
|
+
order :name, :asc
|
377
|
+
limit 1
|
378
|
+
get.query # takes the Fluent Query object
|
379
|
+
end
|
380
|
+
|
381
|
+
query.execute!
|
382
|
+
|
383
|
+
And if necessary build it by `#build` method to string. Build method is
|
384
|
+
also available above Native Query object directly. To execute query or
|
385
|
+
fetch data is possible through `#do(*args)` or `#execute(*args)`. Result
|
386
|
+
will be result object similar to Native Query's one.
|
387
|
+
|
388
|
+
### Examples
|
389
|
+
|
390
|
+
Simple example:
|
391
|
+
|
392
|
+
# Query it!
|
393
|
+
records = model.maintainers :name, :code do
|
394
|
+
where :active => true
|
395
|
+
order :name, :asc
|
396
|
+
limit 1
|
397
|
+
get.all
|
398
|
+
end
|
399
|
+
|
400
|
+
Will be transformed to:
|
401
|
+
|
402
|
+
SELECT `name`, `code` FROM `maintainers`
|
403
|
+
WHERE `active` IS TRUE
|
404
|
+
ORDER BY `name` ASC
|
405
|
+
LIMIT 1
|
406
|
+
|
407
|
+
Advanced automatic joining (advanced example):
|
408
|
+
|
409
|
+
# here selects two fields from 'projects' table and two other fields from joined 'sites' table
|
410
|
+
projects = model.projects :name, :code, :sites_code, :sites_name do
|
411
|
+
sites :code, :name, :language_name do
|
412
|
+
where :active => true
|
413
|
+
end
|
414
|
+
|
415
|
+
maintainers do # joins 'projects' table with table 'maintainers'
|
416
|
+
indirect backward # ...by indirect way, so M:N
|
417
|
+
where :active => true
|
418
|
+
where :id => 10
|
419
|
+
end
|
420
|
+
|
421
|
+
where :active => true
|
422
|
+
order :code, [:sites, :code]
|
423
|
+
|
424
|
+
get.assoc(:code, :sites_code)
|
425
|
+
end
|
426
|
+
|
427
|
+
Will be transformed to:
|
428
|
+
|
429
|
+
SELECT `name`, `code`, `sites`.`code` AS `sites_code`, `sites`.`name` AS `sites_name`
|
430
|
+
FROM `projects`
|
431
|
+
JOIN `sites` ON `projects`.`id` = `sites`.`project_id`
|
432
|
+
JOIN `maintainers_projects`
|
433
|
+
ON `projects`.`id` = `maintainers_projects`.`project_id`
|
434
|
+
JOIN `maintainers`
|
435
|
+
ON `maintainers`.`id` = `maintainers_projects`.`maintainer_id`
|
436
|
+
WHERE `sites`.`active` IS TRUE
|
437
|
+
AND `maintainers`.`active` IS TRUE
|
438
|
+
AND `maintainers`.`id` = 10
|
439
|
+
AND `active` IS TRUE
|
440
|
+
ORDER BY `code`, `sites`.`code` ASC
|
441
|
+
|
442
|
+
|
443
|
+
Contributing
|
444
|
+
------------
|
445
|
+
|
446
|
+
1. Fork it.
|
447
|
+
2. Create a branch (`git checkout -b 20101220-my-change`).
|
448
|
+
3. Commit your changes (`git commit -am "Added something"`).
|
449
|
+
4. Push to the branch (`git push origin 20101220-my-change`).
|
450
|
+
5. Create an [Issue][3] with a link to your branch.
|
451
|
+
6. Enjoy a refreshing Diet Coke and wait.
|
452
|
+
|
453
|
+
Copyright
|
454
|
+
---------
|
455
|
+
|
456
|
+
Copyright © 2010-2011 [Martin Kozák][4]. See `LICENSE.txt` for
|
457
|
+
further details.
|
458
|
+
|
459
|
+
[1]: http://dibiphp.com/
|
460
|
+
[2]: https://github.com/martinkozak/fluent-query
|
461
|
+
[3]: http://github.com/martinkozak/native-query/issues
|
462
|
+
[4]: http://www.martinkozak.net/
|