velocity 0.0.1
Sign up to get free protection for your applications and to get access to all the features.
- data/.document +5 -0
- data/Gemfile +22 -0
- data/Gemfile.lock +53 -0
- data/LICENSE.txt +20 -0
- data/README.rdoc +89 -0
- data/Rakefile +39 -0
- data/VERSION +1 -0
- data/config/database.sample.yml +5 -0
- data/config/schema.rb +38 -0
- data/lib/velocity.rb +5 -0
- data/lib/velocity/data_query_map.rb +85 -0
- data/lib/velocity/mapped_row.rb +9 -0
- data/lib/velocity/query.rb +13 -0
- data/lib/velocity/results/postgresql.rb +73 -0
- data/test/helper.rb +49 -0
- data/test/models/bar.rb +5 -0
- data/test/models/foo.rb +5 -0
- data/test/test_performance.rb +47 -0
- data/test/test_velocity.rb +90 -0
- data/velocity.gemspec +99 -0
- metadata +214 -0
data/.document
ADDED
data/Gemfile
ADDED
@@ -0,0 +1,22 @@
|
|
1
|
+
source "http://rubygems.org"
|
2
|
+
# Add dependencies required to use your gem here.
|
3
|
+
# Example:
|
4
|
+
# gem "activesupport", ">= 2.3.5"
|
5
|
+
|
6
|
+
# Add dependencies to develop your gem here.
|
7
|
+
# Include everything needed to run rake, tests, features, etc.
|
8
|
+
gem "activerecord", "~> 3.0.0"
|
9
|
+
gem "activesupport", "~> 3.0.0"
|
10
|
+
|
11
|
+
group :development do
|
12
|
+
gem "jeweler"
|
13
|
+
gem "pg"
|
14
|
+
gem "mysql2"
|
15
|
+
gem "thoughtbot-shoulda"
|
16
|
+
gem "simplecov"
|
17
|
+
gem "terminal-table"
|
18
|
+
gem "term-ansicolor"
|
19
|
+
gem "turn", ">= 0.8.2"
|
20
|
+
gem "ansi"
|
21
|
+
gem "faker"
|
22
|
+
end
|
data/Gemfile.lock
ADDED
@@ -0,0 +1,53 @@
|
|
1
|
+
GEM
|
2
|
+
remote: http://rubygems.org/
|
3
|
+
specs:
|
4
|
+
activemodel (3.0.7)
|
5
|
+
activesupport (= 3.0.7)
|
6
|
+
builder (~> 2.1.2)
|
7
|
+
i18n (~> 0.5.0)
|
8
|
+
activerecord (3.0.7)
|
9
|
+
activemodel (= 3.0.7)
|
10
|
+
activesupport (= 3.0.7)
|
11
|
+
arel (~> 2.0.2)
|
12
|
+
tzinfo (~> 0.3.23)
|
13
|
+
activesupport (3.0.7)
|
14
|
+
ansi (1.2.3)
|
15
|
+
arel (2.0.9)
|
16
|
+
builder (2.1.2)
|
17
|
+
faker (0.9.5)
|
18
|
+
i18n (~> 0.4)
|
19
|
+
git (1.2.5)
|
20
|
+
i18n (0.5.0)
|
21
|
+
jeweler (1.5.2)
|
22
|
+
bundler (~> 1.0.0)
|
23
|
+
git (>= 1.2.5)
|
24
|
+
rake
|
25
|
+
mysql2 (0.2.6)
|
26
|
+
pg (0.10.1)
|
27
|
+
rake (0.8.7)
|
28
|
+
simplecov (0.4.2)
|
29
|
+
simplecov-html (~> 0.4.4)
|
30
|
+
simplecov-html (0.4.4)
|
31
|
+
term-ansicolor (1.0.5)
|
32
|
+
terminal-table (1.4.2)
|
33
|
+
thoughtbot-shoulda (2.11.1)
|
34
|
+
turn (0.8.2)
|
35
|
+
ansi (>= 1.2.2)
|
36
|
+
tzinfo (0.3.26)
|
37
|
+
|
38
|
+
PLATFORMS
|
39
|
+
ruby
|
40
|
+
|
41
|
+
DEPENDENCIES
|
42
|
+
activerecord (~> 3.0.0)
|
43
|
+
activesupport (~> 3.0.0)
|
44
|
+
ansi
|
45
|
+
faker
|
46
|
+
jeweler
|
47
|
+
mysql2
|
48
|
+
pg
|
49
|
+
simplecov
|
50
|
+
term-ansicolor
|
51
|
+
terminal-table
|
52
|
+
thoughtbot-shoulda
|
53
|
+
turn (>= 0.8.2)
|
data/LICENSE.txt
ADDED
@@ -0,0 +1,20 @@
|
|
1
|
+
Copyright (c) 2011 Pascal Houliston
|
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.rdoc
ADDED
@@ -0,0 +1,89 @@
|
|
1
|
+
= Velocity
|
2
|
+
|
3
|
+
Provides a barebones, but supremely quick way of querying your ActiveRecord database.
|
4
|
+
|
5
|
+
Velocity uses adapter-specific optimizations (only pg, and mysql2 support planned for now) to reduce memory consumption
|
6
|
+
and processing time **significantly** when working with large amounts of data. This is typically useful in situations like
|
7
|
+
reporting or cached web services where you just need quick access to the rows in your database.
|
8
|
+
|
9
|
+
For now, this is mostly achieved by avoiding the creation of any Ruby data structures (lazy column and row loading), but
|
10
|
+
there are plans to use asynchronous queries with perhaps fibres or threads to speed processing up further.
|
11
|
+
|
12
|
+
Supported adapters: PostgreSQL
|
13
|
+
|
14
|
+
== How to
|
15
|
+
|
16
|
+
Include the query module into your ActiveRecord model
|
17
|
+
|
18
|
+
class Foo < ActiveRecord::Base
|
19
|
+
include Velocity::Query
|
20
|
+
end
|
21
|
+
|
22
|
+
This adds a .data() method onto your model class. Use this pretty much like the ActiveRecord 3 query interface.
|
23
|
+
|
24
|
+
Foo.data.where(:designation => 'Superfoo').limit(1000).order('name ASC').each do |foo|
|
25
|
+
puts foo.title
|
26
|
+
end
|
27
|
+
|
28
|
+
What should immediately apparent is that rather than getting a list of ActiveRecord model instances (i.e. Foo), you get an
|
29
|
+
iterator object. This object consists of accessor methods reflecting all the fields in your query.
|
30
|
+
|
31
|
+
*N.B:* Only simple where(), limit(), order(), group(), and joins() calls are supported.
|
32
|
+
|
33
|
+
Using the joins() method essentially results in a LEFT JOIN (in contrast to normal ActiveRecord), and the primary key
|
34
|
+
of the joined association is automatically selected as foos_id (where foos is your joined table name).
|
35
|
+
|
36
|
+
== Debugging
|
37
|
+
|
38
|
+
You can inspect the attributes of a row by calling:
|
39
|
+
|
40
|
+
Foo.data.each do |row|
|
41
|
+
puts row.inspect
|
42
|
+
end
|
43
|
+
|
44
|
+
>> #Velocity::MappedRow --> {"id"=>"100759", "some_string_field"=>"Value A"}
|
45
|
+
|
46
|
+
|
47
|
+
== Velocity development
|
48
|
+
|
49
|
+
Please copy config/database.sample.yml to config/database.yml and adjust to your system settings.
|
50
|
+
|
51
|
+
> rake test
|
52
|
+
|
53
|
+
The first time you run the tests a large test database will be created (for the performance tests), so
|
54
|
+
it is likely to take a very very long time on the first run.
|
55
|
+
|
56
|
+
|
57
|
+
== Contributing to velocity
|
58
|
+
|
59
|
+
* Check out the latest master to make sure the feature hasn't been implemented or the bug hasn't been fixed yet
|
60
|
+
* Check out the issue tracker to make sure someone already hasn't requested it and/or contributed it
|
61
|
+
* Fork the project
|
62
|
+
* Start a feature/bugfix branch
|
63
|
+
* Commit and push until you are happy with your contribution
|
64
|
+
* Make sure to add tests for it. This is important so I don't break it in a future version unintentionally.
|
65
|
+
* Please try not to mess with the Rakefile, version, or history. If you want to have your own version, or is otherwise necessary, that is fine, but please isolate to its own commit so I can cherry-pick around it.
|
66
|
+
|
67
|
+
== License
|
68
|
+
|
69
|
+
Copyright (c) 2011 Pascal Houliston
|
70
|
+
|
71
|
+
Permission is hereby granted, free of charge, to any person obtaining
|
72
|
+
a copy of this software and associated documentation files (the
|
73
|
+
"Software"), to deal in the Software without restriction, including
|
74
|
+
without limitation the rights to use, copy, modify, merge, publish,
|
75
|
+
distribute, sublicense, and/or sell copies of the Software, and to
|
76
|
+
permit persons to whom the Software is furnished to do so, subject to
|
77
|
+
the following conditions:
|
78
|
+
|
79
|
+
The above copyright notice and this permission notice shall be
|
80
|
+
included in all copies or substantial portions of the Software.
|
81
|
+
|
82
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
|
83
|
+
EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
|
84
|
+
MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
|
85
|
+
NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
|
86
|
+
LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
|
87
|
+
OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
|
88
|
+
WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
89
|
+
|
data/Rakefile
ADDED
@@ -0,0 +1,39 @@
|
|
1
|
+
require 'rubygems'
|
2
|
+
require 'bundler'
|
3
|
+
begin
|
4
|
+
Bundler.setup(:default, :development)
|
5
|
+
rescue Bundler::BundlerError => e
|
6
|
+
$stderr.puts e.message
|
7
|
+
$stderr.puts "Run `bundle install` to install missing gems"
|
8
|
+
exit e.status_code
|
9
|
+
end
|
10
|
+
require 'rake'
|
11
|
+
require 'jeweler'
|
12
|
+
|
13
|
+
Jeweler::Tasks.new do |gem|
|
14
|
+
# gem is a Gem::Specification... see http://docs.rubygems.org/read/chapter/20 for more options
|
15
|
+
gem.name = "velocity"
|
16
|
+
gem.homepage = "http://github.com/pascalh1011/velocity"
|
17
|
+
gem.license = "MIT"
|
18
|
+
gem.summary = %Q{Provides a barebones, but supremely quick way of querying your ActiveRecord database.}
|
19
|
+
gem.description = %Q{Provides a barebones, but supremely quick way of querying your ActiveRecord database.}
|
20
|
+
gem.email = "101pascal@gmail.com"
|
21
|
+
gem.authors = ["Pascal Houliston"]
|
22
|
+
end
|
23
|
+
Jeweler::RubygemsDotOrgTasks.new
|
24
|
+
|
25
|
+
require 'rake/testtask'
|
26
|
+
Rake::TestTask.new(:test) do |test|
|
27
|
+
test.libs << 'lib' << 'test'
|
28
|
+
test.pattern = 'test/**/test_*.rb'
|
29
|
+
test.verbose = true
|
30
|
+
end
|
31
|
+
|
32
|
+
task :migrate do
|
33
|
+
require File.join(File.dirname(__FILE__), 'test', 'helper')
|
34
|
+
require 'active_record'
|
35
|
+
load TEST_ROOT.join('db', 'migrations.rb')
|
36
|
+
end
|
37
|
+
|
38
|
+
task :default => :test
|
39
|
+
|
data/VERSION
ADDED
@@ -0,0 +1 @@
|
|
1
|
+
0.0.1
|
data/config/schema.rb
ADDED
@@ -0,0 +1,38 @@
|
|
1
|
+
require 'faker'
|
2
|
+
|
3
|
+
ActiveRecord::Schema.define(:version => 1) do
|
4
|
+
create_table(:foos) do |t|
|
5
|
+
t.string :some_string_field
|
6
|
+
t.string :another_string_field
|
7
|
+
t.integer :some_integer_field
|
8
|
+
t.float :some_float_field
|
9
|
+
t.text :some_text_field
|
10
|
+
t.integer :some_indexed_field
|
11
|
+
end
|
12
|
+
|
13
|
+
create_table(:bars) do |t|
|
14
|
+
t.integer :foo_id
|
15
|
+
t.string :a_string_field
|
16
|
+
end
|
17
|
+
|
18
|
+
add_index :foos, :some_indexed_field
|
19
|
+
add_index :bars, :foo_id
|
20
|
+
end
|
21
|
+
|
22
|
+
puts "Setting up database, this may take a while..."
|
23
|
+
|
24
|
+
fake_sentences = []
|
25
|
+
10.times { fake_sentences << Faker::Lorem.sentence }
|
26
|
+
paragraphs = Faker::Lorem.paragraphs
|
27
|
+
|
28
|
+
Foo.transaction do
|
29
|
+
100000.times do
|
30
|
+
Foo.create!(:some_string_field => fake_sentences[rand(9)], :another_string_field => fake_sentences[rand(9)], :some_integer_field => rand(9999999), :some_float_field => rand(), :some_text_field => paragraphs, :some_indexed_field => 1)
|
31
|
+
end
|
32
|
+
end
|
33
|
+
|
34
|
+
Bar.transaction do
|
35
|
+
Foo.select('id').find_each do |foo|
|
36
|
+
Bar.create!(:foo_id => foo.id, :a_string_field => fake_sentences[rand(9)])
|
37
|
+
end
|
38
|
+
end
|
data/lib/velocity.rb
ADDED
@@ -0,0 +1,85 @@
|
|
1
|
+
require 'velocity/mapped_row'
|
2
|
+
require 'velocity/results/postgresql'
|
3
|
+
|
4
|
+
module Velocity
|
5
|
+
class DataQueryMap
|
6
|
+
def initialize(model_binding, force_adapter = nil)
|
7
|
+
begin
|
8
|
+
@handler = "Velocity::#{force_adapter || model_binding.connection.adapter_name}Result".constantize
|
9
|
+
rescue NameError
|
10
|
+
raise "Velocity does not currently support the #{model_binding.connection.adapter_name} adapter.\n Head on over to http://github.com/pascalh1011/velocity to add an issue, or if you're feeling generous, submit an appropriate handler for your database adapter."
|
11
|
+
end
|
12
|
+
|
13
|
+
@model = model_binding
|
14
|
+
@results = nil # For accepting last query (from Result)
|
15
|
+
@fields = ""
|
16
|
+
@conditions = []
|
17
|
+
@orders = []
|
18
|
+
@joins = []
|
19
|
+
|
20
|
+
# Prepare and proxy methods pertaining to results onto the Result object (after performing the query)
|
21
|
+
(@handler.instance_methods-@handler.superclass.instance_methods+[:inspect, :to_yaml]).each do |method|
|
22
|
+
define_singleton_method(method) do |*arguments, &block|
|
23
|
+
prepare_and_run_query
|
24
|
+
@results.send(method, *arguments, &block)
|
25
|
+
end
|
26
|
+
end
|
27
|
+
end
|
28
|
+
|
29
|
+
def select(fields='')
|
30
|
+
@fields = [@fields, fields].join(', ') unless fields.blank?
|
31
|
+
self
|
32
|
+
end
|
33
|
+
|
34
|
+
def limit(limit=nil)
|
35
|
+
@limit = limit.to_i
|
36
|
+
self
|
37
|
+
end
|
38
|
+
|
39
|
+
def where(conditions={})
|
40
|
+
@conditions << @model.send(:sanitize_sql_for_conditions, conditions) unless conditions.empty?
|
41
|
+
self
|
42
|
+
end
|
43
|
+
|
44
|
+
def joins(*list_of_associations)
|
45
|
+
@joins += list_of_associations.flatten unless list_of_associations.empty?
|
46
|
+
self
|
47
|
+
end
|
48
|
+
|
49
|
+
def order(field_and_direction='')
|
50
|
+
@orders << field_and_direction unless field_and_direction.blank?
|
51
|
+
self
|
52
|
+
end
|
53
|
+
|
54
|
+
def all
|
55
|
+
limit(0)
|
56
|
+
prepare_and_run_query
|
57
|
+
@results
|
58
|
+
end
|
59
|
+
|
60
|
+
private
|
61
|
+
def prepare_and_run_query
|
62
|
+
fields = "#{@model.table_name}.*" if @fields.blank?
|
63
|
+
|
64
|
+
unless @joins.blank?
|
65
|
+
joins = @joins.collect do |association|
|
66
|
+
association_reflection = @model.reflect_on_association(association)
|
67
|
+
raise "#{@model.class}: No association found named #{association}" unless association_reflection
|
68
|
+
|
69
|
+
if @fields.blank?
|
70
|
+
fields += ", #{association_reflection.klass.table_name}.*, #{association_reflection.klass.table_name}.#{association_reflection.klass.primary_key} AS #{association_reflection.klass.table_name}_#{association_reflection.klass.primary_key}"
|
71
|
+
end
|
72
|
+
foreign_key = (association_reflection.respond_to?(:foreign_key))? association_reflection.foreign_key.to_s : association_reflection.primary_key_name.to_s
|
73
|
+
"LEFT JOIN #{association_reflection.table_name} ON #{association_reflection.klass.table_name}.#{foreign_key} = #{@model.table_name}.#{association_reflection.klass.primary_key}"
|
74
|
+
end.join(" ")
|
75
|
+
end
|
76
|
+
|
77
|
+
limit = (@limit.to_i > 0)? "LIMIT #{@limit}" : ""
|
78
|
+
conditions = "WHERE #{@conditions.join(' AND ')}" unless @conditions.blank?
|
79
|
+
orders = "ORDER BY #{@orders.join(', ')}" unless @orders.blank?
|
80
|
+
|
81
|
+
sql = "SELECT #{fields} FROM #{@model.table_name} "+[joins, conditions, orders, limit].reject(&:blank?).join(' ')
|
82
|
+
@results = @handler.new(sql, @model.connection)
|
83
|
+
end
|
84
|
+
end
|
85
|
+
end
|
@@ -0,0 +1,73 @@
|
|
1
|
+
module Velocity
|
2
|
+
class PostgreSQLResult
|
3
|
+
class PostgreSQLMappedRow < MappedRow
|
4
|
+
|
5
|
+
def initialize(pg_query)
|
6
|
+
@fields = pg_query.fields
|
7
|
+
@pg_query = pg_query
|
8
|
+
|
9
|
+
# It is imperitive that the adapter return the array in the same sequence of fields provided above
|
10
|
+
# If this isn't the case, some re-ordering of the field KEYS should take place
|
11
|
+
@fields.each_with_index do |field, index|
|
12
|
+
define_singleton_method(field) do
|
13
|
+
pg_query.getvalue(@tuple, index)
|
14
|
+
end
|
15
|
+
end
|
16
|
+
end
|
17
|
+
|
18
|
+
def attributes
|
19
|
+
@pg_query[@tuple] # Try to avoid calling this, its relatively slow.
|
20
|
+
end
|
21
|
+
end
|
22
|
+
|
23
|
+
def initialize(sql, adapter)
|
24
|
+
@results = adapter.execute(sql)
|
25
|
+
@mapping = PostgreSQLMappedRow.new(@results)
|
26
|
+
@num_tuples = @results.num_tuples
|
27
|
+
end
|
28
|
+
|
29
|
+
def collect(&block)
|
30
|
+
@collected = []
|
31
|
+
@results.num_tuples.times do |tuple|
|
32
|
+
@mapping.tuple = tuple
|
33
|
+
@collected << yield(@mapping)
|
34
|
+
end
|
35
|
+
cleanup
|
36
|
+
@collected
|
37
|
+
end
|
38
|
+
|
39
|
+
def first
|
40
|
+
self.[](0)
|
41
|
+
end
|
42
|
+
|
43
|
+
def [](index)
|
44
|
+
@mapping.tap do |mapping|
|
45
|
+
mapping.tuple = index
|
46
|
+
end
|
47
|
+
end
|
48
|
+
|
49
|
+
def inspect
|
50
|
+
collect(&:attributes).inspect
|
51
|
+
end
|
52
|
+
|
53
|
+
def to_yaml
|
54
|
+
collect(&:attributes).to_yaml
|
55
|
+
end
|
56
|
+
|
57
|
+
def each(&block)
|
58
|
+
@results.num_tuples.times do |tuple|
|
59
|
+
@mapping.tuple = tuple
|
60
|
+
yield(@mapping)
|
61
|
+
end
|
62
|
+
cleanup
|
63
|
+
end
|
64
|
+
|
65
|
+
def length
|
66
|
+
@num_tuples
|
67
|
+
end
|
68
|
+
|
69
|
+
def cleanup
|
70
|
+
@results.clear
|
71
|
+
end
|
72
|
+
end
|
73
|
+
end
|
data/test/helper.rb
ADDED
@@ -0,0 +1,49 @@
|
|
1
|
+
require 'rubygems'
|
2
|
+
require 'bundler'
|
3
|
+
begin
|
4
|
+
Bundler.setup(:default, :development)
|
5
|
+
rescue Bundler::BundlerError => e
|
6
|
+
$stderr.puts e.message
|
7
|
+
$stderr.puts "Run `bundle install` to install missing gems"
|
8
|
+
exit e.status_code
|
9
|
+
end
|
10
|
+
require 'simplecov'
|
11
|
+
SimpleCov.start do
|
12
|
+
add_filter 'test'
|
13
|
+
add_filter 'config'
|
14
|
+
end
|
15
|
+
require 'test/unit'
|
16
|
+
require 'turn'
|
17
|
+
require 'shoulda'
|
18
|
+
|
19
|
+
$LOAD_PATH.unshift(File.dirname(__FILE__))
|
20
|
+
$LOAD_PATH.unshift(File.join(File.dirname(__FILE__), '..', 'lib'))
|
21
|
+
require 'velocity'
|
22
|
+
require 'terminal-table/import'
|
23
|
+
|
24
|
+
class TestRoot
|
25
|
+
def initialize
|
26
|
+
@dir = Dir.pwd
|
27
|
+
end
|
28
|
+
|
29
|
+
def to_s
|
30
|
+
@dir
|
31
|
+
end
|
32
|
+
|
33
|
+
def join(*args)
|
34
|
+
File.join(@dir, args)
|
35
|
+
end
|
36
|
+
end
|
37
|
+
|
38
|
+
TEST_ROOT = TestRoot.new
|
39
|
+
|
40
|
+
ActiveRecord::Base.establish_connection(YAML::load_file(TEST_ROOT.join('config', 'database.yml')))
|
41
|
+
Dir[TEST_ROOT.join('test', 'models', '*.rb')].each { |f| require(f) }
|
42
|
+
|
43
|
+
unless Foo.table_exists?
|
44
|
+
require TEST_ROOT.join('config', 'schema')
|
45
|
+
end
|
46
|
+
|
47
|
+
|
48
|
+
class Test::Unit::TestCase
|
49
|
+
end
|
data/test/models/bar.rb
ADDED
data/test/models/foo.rb
ADDED
@@ -0,0 +1,47 @@
|
|
1
|
+
require 'helper'
|
2
|
+
|
3
|
+
class TestPerformance < Test::Unit::TestCase
|
4
|
+
def measure(&block)
|
5
|
+
t = Time.now.to_f
|
6
|
+
yield
|
7
|
+
((Time.now.to_f-t)*1000.0).round
|
8
|
+
end
|
9
|
+
|
10
|
+
# TODO: Nice way of getting actual memory consumption
|
11
|
+
def memory
|
12
|
+
return 0 unless RUBY_PLATFORM.match(/linux/i) # Will be happy to remove this if someone can confirm it's working on other OS's :)
|
13
|
+
sleep 0.1
|
14
|
+
`ps -o rss= -p #{Process.pid}`.to_i
|
15
|
+
end
|
16
|
+
|
17
|
+
|
18
|
+
should "perform favourably compared to ActiveRecord" do
|
19
|
+
t = table
|
20
|
+
t.headings = ['Name', 'Total Rows', 'Total Processing Time (ms)', 'Memory Consumed (KB)']
|
21
|
+
count = Foo.count('id')
|
22
|
+
base = memory
|
23
|
+
private_set = nil
|
24
|
+
|
25
|
+
ar = measure do
|
26
|
+
collection = []
|
27
|
+
Foo.find_each do |result|
|
28
|
+
collection << result.some_integer_field
|
29
|
+
end
|
30
|
+
private_set=memory-base
|
31
|
+
collection
|
32
|
+
end
|
33
|
+
t << ['ActiveRecord', count, ar, private_set]
|
34
|
+
|
35
|
+
base = memory
|
36
|
+
v = measure do
|
37
|
+
collection = []
|
38
|
+
Foo.data.each do |result|
|
39
|
+
collection << result.some_integer_field
|
40
|
+
end
|
41
|
+
private_set = memory-base
|
42
|
+
collection
|
43
|
+
end
|
44
|
+
t << ['Velocity', count, v, private_set]
|
45
|
+
puts t
|
46
|
+
end
|
47
|
+
end
|
@@ -0,0 +1,90 @@
|
|
1
|
+
require 'helper'
|
2
|
+
|
3
|
+
class TestVelocity < Test::Unit::TestCase
|
4
|
+
def setup
|
5
|
+
@foo_a = Foo.create(:some_string_field => "Value A")
|
6
|
+
@foo_b = Foo.create(:some_string_field => "Value A", :another_string_field => "Value B")
|
7
|
+
@foo_a.bars.create(:a_string_field => "A")
|
8
|
+
@foo_b.bars.create(:a_string_field => "B")
|
9
|
+
end
|
10
|
+
|
11
|
+
def teardown
|
12
|
+
@foo_a.destroy
|
13
|
+
@foo_b.destroy
|
14
|
+
end
|
15
|
+
|
16
|
+
should "return a Velocity::DataQueryMap with expected API methods when included in an ActiveRecord model" do
|
17
|
+
assert_equal(Velocity::DataQueryMap, Foo.data.class)
|
18
|
+
|
19
|
+
expected_api_methods = [:select, :joins, :limit, :where, :order]
|
20
|
+
|
21
|
+
expected_api_methods.each do |method|
|
22
|
+
assert(Foo.data.respond_to?(method), "Velocity::DataQueryMap should respond to #{method}")
|
23
|
+
instance = Foo.data
|
24
|
+
assert_equal(instance, instance.send(method), "Velocity::DataQueryMap.#{method} should return an instance of itself (for chaining)")
|
25
|
+
end
|
26
|
+
end
|
27
|
+
|
28
|
+
should "return the first result matching when given a where clause" do
|
29
|
+
first_match = Foo.data.where(:some_string_field => "Value A").first
|
30
|
+
Foo.columns_hash.keys.each do |column|
|
31
|
+
assert(first_match.respond_to?(column), "Single result should respond to column #{column}")
|
32
|
+
end
|
33
|
+
assert_equal("Value A", first_match.some_string_field)
|
34
|
+
assert(first_match.inspect.include?("Value A"))
|
35
|
+
|
36
|
+
second_match = Foo.data.where(:some_string_field => "Value A").where(:another_string_field => "Value B").first
|
37
|
+
|
38
|
+
Foo.columns_hash.keys.each do |column|
|
39
|
+
assert(second_match.respond_to?(column), "Single result should respond to column #{column}")
|
40
|
+
end
|
41
|
+
assert_equal("Value A", second_match.some_string_field)
|
42
|
+
assert_equal("Value B", second_match.another_string_field)
|
43
|
+
end
|
44
|
+
|
45
|
+
should "return an iterator for all results" do
|
46
|
+
results_via_reflection = Foo.data.where(:some_string_field => "Value A")
|
47
|
+
results_via_iterator = Foo.data.where(:some_string_field => "Value A").all # Note the all()
|
48
|
+
|
49
|
+
[results_via_reflection, results_via_iterator].each do |results|
|
50
|
+
results.each do |result|
|
51
|
+
Foo.columns_hash.keys.each do |column|
|
52
|
+
assert(result.respond_to?(column), "Single result should respond to column #{column}")
|
53
|
+
assert_equal(result.some_string_field, "Value A")
|
54
|
+
end
|
55
|
+
end
|
56
|
+
end
|
57
|
+
assert_equal(2, results_via_iterator.length)
|
58
|
+
end
|
59
|
+
|
60
|
+
should "respond provide a simple inspection API for debugging" do
|
61
|
+
all_results = Foo.data.where(:some_string_field => "Value A")
|
62
|
+
|
63
|
+
assert(all_results.inspect.include?('Value B'), "inspect() method should work as per default")
|
64
|
+
assert(all_results.to_yaml.include?('Value B'), "to_yaml() method should work as per default")
|
65
|
+
end
|
66
|
+
|
67
|
+
should "return results in the correct order" do
|
68
|
+
results = Foo.data.where(:some_string_field => "Value A").order("another_string_field ASC").collect { |r| r.another_string_field }
|
69
|
+
|
70
|
+
assert_equal("Value B", results.first)
|
71
|
+
assert_nil(results.last)
|
72
|
+
end
|
73
|
+
|
74
|
+
should "join associations when requested and return appropriate columns" do
|
75
|
+
results = Foo.data.where(:some_string_field => "Value A").joins(:bars)
|
76
|
+
|
77
|
+
results.each do |result|
|
78
|
+
Foo.columns_hash.merge(Bar.columns_hash).keys.each do |column|
|
79
|
+
assert(result.respond_to?(column), "Result should respond to #{column} (may be from association)")
|
80
|
+
end
|
81
|
+
assert(result.respond_to?("bars_id"), "Should include the association's primary key")
|
82
|
+
end
|
83
|
+
end
|
84
|
+
|
85
|
+
should "inform the user if their database is unsupported" do
|
86
|
+
assert_raise RuntimeError do
|
87
|
+
Velocity::DataQueryMap.new(Foo, "HappyRainbowAdapter")
|
88
|
+
end
|
89
|
+
end
|
90
|
+
end
|
data/velocity.gemspec
ADDED
@@ -0,0 +1,99 @@
|
|
1
|
+
# Generated by jeweler
|
2
|
+
# DO NOT EDIT THIS FILE DIRECTLY
|
3
|
+
# Instead, edit Jeweler::Tasks in Rakefile, and run 'rake gemspec'
|
4
|
+
# -*- encoding: utf-8 -*-
|
5
|
+
|
6
|
+
Gem::Specification.new do |s|
|
7
|
+
s.name = %q{velocity}
|
8
|
+
s.version = "0.0.1"
|
9
|
+
|
10
|
+
s.required_rubygems_version = Gem::Requirement.new(">= 0") if s.respond_to? :required_rubygems_version=
|
11
|
+
s.authors = ["Pascal Houliston"]
|
12
|
+
s.date = %q{2011-05-03}
|
13
|
+
s.description = %q{Provides a barebones, but supremely quick way of querying your ActiveRecord database.}
|
14
|
+
s.email = %q{101pascal@gmail.com}
|
15
|
+
s.extra_rdoc_files = [
|
16
|
+
"LICENSE.txt",
|
17
|
+
"README.rdoc"
|
18
|
+
]
|
19
|
+
s.files = [
|
20
|
+
".document",
|
21
|
+
"Gemfile",
|
22
|
+
"Gemfile.lock",
|
23
|
+
"LICENSE.txt",
|
24
|
+
"README.rdoc",
|
25
|
+
"Rakefile",
|
26
|
+
"VERSION",
|
27
|
+
"config/database.sample.yml",
|
28
|
+
"config/schema.rb",
|
29
|
+
"lib/velocity.rb",
|
30
|
+
"lib/velocity/data_query_map.rb",
|
31
|
+
"lib/velocity/mapped_row.rb",
|
32
|
+
"lib/velocity/query.rb",
|
33
|
+
"lib/velocity/results/postgresql.rb",
|
34
|
+
"test/helper.rb",
|
35
|
+
"test/models/bar.rb",
|
36
|
+
"test/models/foo.rb",
|
37
|
+
"test/test_performance.rb",
|
38
|
+
"test/test_velocity.rb",
|
39
|
+
"velocity.gemspec"
|
40
|
+
]
|
41
|
+
s.homepage = %q{http://github.com/pascalh1011/velocity}
|
42
|
+
s.licenses = ["MIT"]
|
43
|
+
s.require_paths = ["lib"]
|
44
|
+
s.rubygems_version = %q{1.5.2}
|
45
|
+
s.summary = %q{Provides a barebones, but supremely quick way of querying your ActiveRecord database.}
|
46
|
+
s.test_files = [
|
47
|
+
"test/helper.rb",
|
48
|
+
"test/models/bar.rb",
|
49
|
+
"test/models/foo.rb",
|
50
|
+
"test/test_performance.rb",
|
51
|
+
"test/test_velocity.rb"
|
52
|
+
]
|
53
|
+
|
54
|
+
if s.respond_to? :specification_version then
|
55
|
+
s.specification_version = 3
|
56
|
+
|
57
|
+
if Gem::Version.new(Gem::VERSION) >= Gem::Version.new('1.2.0') then
|
58
|
+
s.add_runtime_dependency(%q<activerecord>, ["~> 3.0.0"])
|
59
|
+
s.add_runtime_dependency(%q<activesupport>, ["~> 3.0.0"])
|
60
|
+
s.add_development_dependency(%q<jeweler>, [">= 0"])
|
61
|
+
s.add_development_dependency(%q<pg>, [">= 0"])
|
62
|
+
s.add_development_dependency(%q<mysql2>, [">= 0"])
|
63
|
+
s.add_development_dependency(%q<thoughtbot-shoulda>, [">= 0"])
|
64
|
+
s.add_development_dependency(%q<simplecov>, [">= 0"])
|
65
|
+
s.add_development_dependency(%q<terminal-table>, [">= 0"])
|
66
|
+
s.add_development_dependency(%q<term-ansicolor>, [">= 0"])
|
67
|
+
s.add_development_dependency(%q<turn>, [">= 0.8.2"])
|
68
|
+
s.add_development_dependency(%q<ansi>, [">= 0"])
|
69
|
+
s.add_development_dependency(%q<faker>, [">= 0"])
|
70
|
+
else
|
71
|
+
s.add_dependency(%q<activerecord>, ["~> 3.0.0"])
|
72
|
+
s.add_dependency(%q<activesupport>, ["~> 3.0.0"])
|
73
|
+
s.add_dependency(%q<jeweler>, [">= 0"])
|
74
|
+
s.add_dependency(%q<pg>, [">= 0"])
|
75
|
+
s.add_dependency(%q<mysql2>, [">= 0"])
|
76
|
+
s.add_dependency(%q<thoughtbot-shoulda>, [">= 0"])
|
77
|
+
s.add_dependency(%q<simplecov>, [">= 0"])
|
78
|
+
s.add_dependency(%q<terminal-table>, [">= 0"])
|
79
|
+
s.add_dependency(%q<term-ansicolor>, [">= 0"])
|
80
|
+
s.add_dependency(%q<turn>, [">= 0.8.2"])
|
81
|
+
s.add_dependency(%q<ansi>, [">= 0"])
|
82
|
+
s.add_dependency(%q<faker>, [">= 0"])
|
83
|
+
end
|
84
|
+
else
|
85
|
+
s.add_dependency(%q<activerecord>, ["~> 3.0.0"])
|
86
|
+
s.add_dependency(%q<activesupport>, ["~> 3.0.0"])
|
87
|
+
s.add_dependency(%q<jeweler>, [">= 0"])
|
88
|
+
s.add_dependency(%q<pg>, [">= 0"])
|
89
|
+
s.add_dependency(%q<mysql2>, [">= 0"])
|
90
|
+
s.add_dependency(%q<thoughtbot-shoulda>, [">= 0"])
|
91
|
+
s.add_dependency(%q<simplecov>, [">= 0"])
|
92
|
+
s.add_dependency(%q<terminal-table>, [">= 0"])
|
93
|
+
s.add_dependency(%q<term-ansicolor>, [">= 0"])
|
94
|
+
s.add_dependency(%q<turn>, [">= 0.8.2"])
|
95
|
+
s.add_dependency(%q<ansi>, [">= 0"])
|
96
|
+
s.add_dependency(%q<faker>, [">= 0"])
|
97
|
+
end
|
98
|
+
end
|
99
|
+
|
metadata
ADDED
@@ -0,0 +1,214 @@
|
|
1
|
+
--- !ruby/object:Gem::Specification
|
2
|
+
name: velocity
|
3
|
+
version: !ruby/object:Gem::Version
|
4
|
+
prerelease:
|
5
|
+
version: 0.0.1
|
6
|
+
platform: ruby
|
7
|
+
authors:
|
8
|
+
- Pascal Houliston
|
9
|
+
autorequire:
|
10
|
+
bindir: bin
|
11
|
+
cert_chain: []
|
12
|
+
|
13
|
+
date: 2011-05-03 00:00:00 +02:00
|
14
|
+
default_executable:
|
15
|
+
dependencies:
|
16
|
+
- !ruby/object:Gem::Dependency
|
17
|
+
name: activerecord
|
18
|
+
requirement: &id001 !ruby/object:Gem::Requirement
|
19
|
+
none: false
|
20
|
+
requirements:
|
21
|
+
- - ~>
|
22
|
+
- !ruby/object:Gem::Version
|
23
|
+
version: 3.0.0
|
24
|
+
type: :runtime
|
25
|
+
prerelease: false
|
26
|
+
version_requirements: *id001
|
27
|
+
- !ruby/object:Gem::Dependency
|
28
|
+
name: activesupport
|
29
|
+
requirement: &id002 !ruby/object:Gem::Requirement
|
30
|
+
none: false
|
31
|
+
requirements:
|
32
|
+
- - ~>
|
33
|
+
- !ruby/object:Gem::Version
|
34
|
+
version: 3.0.0
|
35
|
+
type: :runtime
|
36
|
+
prerelease: false
|
37
|
+
version_requirements: *id002
|
38
|
+
- !ruby/object:Gem::Dependency
|
39
|
+
name: jeweler
|
40
|
+
requirement: &id003 !ruby/object:Gem::Requirement
|
41
|
+
none: false
|
42
|
+
requirements:
|
43
|
+
- - ">="
|
44
|
+
- !ruby/object:Gem::Version
|
45
|
+
version: "0"
|
46
|
+
type: :development
|
47
|
+
prerelease: false
|
48
|
+
version_requirements: *id003
|
49
|
+
- !ruby/object:Gem::Dependency
|
50
|
+
name: pg
|
51
|
+
requirement: &id004 !ruby/object:Gem::Requirement
|
52
|
+
none: false
|
53
|
+
requirements:
|
54
|
+
- - ">="
|
55
|
+
- !ruby/object:Gem::Version
|
56
|
+
version: "0"
|
57
|
+
type: :development
|
58
|
+
prerelease: false
|
59
|
+
version_requirements: *id004
|
60
|
+
- !ruby/object:Gem::Dependency
|
61
|
+
name: mysql2
|
62
|
+
requirement: &id005 !ruby/object:Gem::Requirement
|
63
|
+
none: false
|
64
|
+
requirements:
|
65
|
+
- - ">="
|
66
|
+
- !ruby/object:Gem::Version
|
67
|
+
version: "0"
|
68
|
+
type: :development
|
69
|
+
prerelease: false
|
70
|
+
version_requirements: *id005
|
71
|
+
- !ruby/object:Gem::Dependency
|
72
|
+
name: thoughtbot-shoulda
|
73
|
+
requirement: &id006 !ruby/object:Gem::Requirement
|
74
|
+
none: false
|
75
|
+
requirements:
|
76
|
+
- - ">="
|
77
|
+
- !ruby/object:Gem::Version
|
78
|
+
version: "0"
|
79
|
+
type: :development
|
80
|
+
prerelease: false
|
81
|
+
version_requirements: *id006
|
82
|
+
- !ruby/object:Gem::Dependency
|
83
|
+
name: simplecov
|
84
|
+
requirement: &id007 !ruby/object:Gem::Requirement
|
85
|
+
none: false
|
86
|
+
requirements:
|
87
|
+
- - ">="
|
88
|
+
- !ruby/object:Gem::Version
|
89
|
+
version: "0"
|
90
|
+
type: :development
|
91
|
+
prerelease: false
|
92
|
+
version_requirements: *id007
|
93
|
+
- !ruby/object:Gem::Dependency
|
94
|
+
name: terminal-table
|
95
|
+
requirement: &id008 !ruby/object:Gem::Requirement
|
96
|
+
none: false
|
97
|
+
requirements:
|
98
|
+
- - ">="
|
99
|
+
- !ruby/object:Gem::Version
|
100
|
+
version: "0"
|
101
|
+
type: :development
|
102
|
+
prerelease: false
|
103
|
+
version_requirements: *id008
|
104
|
+
- !ruby/object:Gem::Dependency
|
105
|
+
name: term-ansicolor
|
106
|
+
requirement: &id009 !ruby/object:Gem::Requirement
|
107
|
+
none: false
|
108
|
+
requirements:
|
109
|
+
- - ">="
|
110
|
+
- !ruby/object:Gem::Version
|
111
|
+
version: "0"
|
112
|
+
type: :development
|
113
|
+
prerelease: false
|
114
|
+
version_requirements: *id009
|
115
|
+
- !ruby/object:Gem::Dependency
|
116
|
+
name: turn
|
117
|
+
requirement: &id010 !ruby/object:Gem::Requirement
|
118
|
+
none: false
|
119
|
+
requirements:
|
120
|
+
- - ">="
|
121
|
+
- !ruby/object:Gem::Version
|
122
|
+
version: 0.8.2
|
123
|
+
type: :development
|
124
|
+
prerelease: false
|
125
|
+
version_requirements: *id010
|
126
|
+
- !ruby/object:Gem::Dependency
|
127
|
+
name: ansi
|
128
|
+
requirement: &id011 !ruby/object:Gem::Requirement
|
129
|
+
none: false
|
130
|
+
requirements:
|
131
|
+
- - ">="
|
132
|
+
- !ruby/object:Gem::Version
|
133
|
+
version: "0"
|
134
|
+
type: :development
|
135
|
+
prerelease: false
|
136
|
+
version_requirements: *id011
|
137
|
+
- !ruby/object:Gem::Dependency
|
138
|
+
name: faker
|
139
|
+
requirement: &id012 !ruby/object:Gem::Requirement
|
140
|
+
none: false
|
141
|
+
requirements:
|
142
|
+
- - ">="
|
143
|
+
- !ruby/object:Gem::Version
|
144
|
+
version: "0"
|
145
|
+
type: :development
|
146
|
+
prerelease: false
|
147
|
+
version_requirements: *id012
|
148
|
+
description: Provides a barebones, but supremely quick way of querying your ActiveRecord database.
|
149
|
+
email: 101pascal@gmail.com
|
150
|
+
executables: []
|
151
|
+
|
152
|
+
extensions: []
|
153
|
+
|
154
|
+
extra_rdoc_files:
|
155
|
+
- LICENSE.txt
|
156
|
+
- README.rdoc
|
157
|
+
files:
|
158
|
+
- .document
|
159
|
+
- Gemfile
|
160
|
+
- Gemfile.lock
|
161
|
+
- LICENSE.txt
|
162
|
+
- README.rdoc
|
163
|
+
- Rakefile
|
164
|
+
- VERSION
|
165
|
+
- config/database.sample.yml
|
166
|
+
- config/schema.rb
|
167
|
+
- lib/velocity.rb
|
168
|
+
- lib/velocity/data_query_map.rb
|
169
|
+
- lib/velocity/mapped_row.rb
|
170
|
+
- lib/velocity/query.rb
|
171
|
+
- lib/velocity/results/postgresql.rb
|
172
|
+
- test/helper.rb
|
173
|
+
- test/models/bar.rb
|
174
|
+
- test/models/foo.rb
|
175
|
+
- test/test_performance.rb
|
176
|
+
- test/test_velocity.rb
|
177
|
+
- velocity.gemspec
|
178
|
+
has_rdoc: true
|
179
|
+
homepage: http://github.com/pascalh1011/velocity
|
180
|
+
licenses:
|
181
|
+
- MIT
|
182
|
+
post_install_message:
|
183
|
+
rdoc_options: []
|
184
|
+
|
185
|
+
require_paths:
|
186
|
+
- lib
|
187
|
+
required_ruby_version: !ruby/object:Gem::Requirement
|
188
|
+
none: false
|
189
|
+
requirements:
|
190
|
+
- - ">="
|
191
|
+
- !ruby/object:Gem::Version
|
192
|
+
hash: 547612753
|
193
|
+
segments:
|
194
|
+
- 0
|
195
|
+
version: "0"
|
196
|
+
required_rubygems_version: !ruby/object:Gem::Requirement
|
197
|
+
none: false
|
198
|
+
requirements:
|
199
|
+
- - ">="
|
200
|
+
- !ruby/object:Gem::Version
|
201
|
+
version: "0"
|
202
|
+
requirements: []
|
203
|
+
|
204
|
+
rubyforge_project:
|
205
|
+
rubygems_version: 1.5.2
|
206
|
+
signing_key:
|
207
|
+
specification_version: 3
|
208
|
+
summary: Provides a barebones, but supremely quick way of querying your ActiveRecord database.
|
209
|
+
test_files:
|
210
|
+
- test/helper.rb
|
211
|
+
- test/models/bar.rb
|
212
|
+
- test/models/foo.rb
|
213
|
+
- test/test_performance.rb
|
214
|
+
- test/test_velocity.rb
|