prequel 0.0.1

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/LICENSE ADDED
@@ -0,0 +1,19 @@
1
+ Copyright (C) 2011 by Nathan Sobo
2
+
3
+ Permission is hereby granted, free of charge, to any person obtaining a copy
4
+ of this software and associated documentation files (the "Software"), to deal
5
+ in the Software without restriction, including without limitation the rights
6
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
7
+ copies of the Software, and to permit persons to whom the Software is
8
+ furnished to do so, subject to the following conditions:
9
+
10
+ The above copyright notice and this permission notice shall be included in
11
+ all copies or substantial portions of the Software.
12
+
13
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
14
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
15
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
16
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
17
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
18
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
19
+ THE SOFTWARE.
File without changes
@@ -0,0 +1,38 @@
1
+ require 'active_support/all'
2
+ require 'sequel'
3
+ require 'prequel/version'
4
+
5
+ module Prequel
6
+ extend ActiveSupport::Autoload
7
+ extend self
8
+
9
+ def const_missing(name)
10
+ if name == :DB
11
+ const_set(:DB, Sequel::DATABASES.first)
12
+ else
13
+ super
14
+ end
15
+ end
16
+
17
+ def table(name, &block)
18
+ Relations::Table.new(name, &block)
19
+ end
20
+
21
+ def session
22
+ Thread.current[:prequel_session] ||= Session.new
23
+ end
24
+
25
+ def clear_session
26
+ Thread.current[:prequel_session] = nil if Thread.current[:prequel_session]
27
+ end
28
+
29
+ require 'prequel/core_extensions'
30
+ autoload :CompositeTuple
31
+ autoload :Expressions
32
+ autoload :Field
33
+ autoload :Record
34
+ autoload :Relations
35
+ autoload :Session
36
+ autoload :Sql
37
+ autoload :Tuple
38
+ end
@@ -0,0 +1,33 @@
1
+ module Prequel
2
+ class CompositeTuple
3
+ attr_reader :left, :right
4
+
5
+ def initialize(left, right)
6
+ @left, @right = left, right
7
+ end
8
+
9
+ def [](name)
10
+ get_record(name) || get_field_value(name)
11
+ end
12
+
13
+ def get_record(table_name)
14
+ left.get_record(table_name) || right.get_record(table_name)
15
+ end
16
+
17
+ def get_field_value(name)
18
+ if name =~ /(.+)__(.+)/
19
+ table_name = $1.to_sym
20
+ field_name = $2.to_sym
21
+ get_record(table_name).try(:get_field_value, field_name)
22
+ else
23
+ left.get_field_value(name) || right.get_field_value(name)
24
+ end
25
+ end
26
+
27
+ def field_values
28
+ [left.field_values, right.field_values]
29
+ end
30
+
31
+ delegate :inspect, :to => :field_values
32
+ end
33
+ end
@@ -0,0 +1,62 @@
1
+ module Prequel
2
+ module HashExtensions
3
+ def to_predicate
4
+ raise NotImplementedError unless size == 1
5
+ keys.first.eq(values.first)
6
+ end
7
+
8
+ Hash.send(:include, self)
9
+ end
10
+
11
+ module SymbolExtensions
12
+ def as(alias_name)
13
+ "#{self}___#{alias_name}".to_sym
14
+ end
15
+
16
+ def eq(other)
17
+ Expressions::Equal.new(self, other)
18
+ end
19
+
20
+ def count
21
+ Expressions::SetFunction.new(self, :count)
22
+ end
23
+
24
+ def resolve_in_relations(relations)
25
+ if self =~ /^(.+)___(.+)$/
26
+ column_name = $1.to_sym
27
+ alias_name = $2.to_sym
28
+ Expressions::AliasedExpression.new(column_name, alias_name).resolve_in_relations(relations)
29
+ else
30
+ relations.each do |relation|
31
+ if column = relation.get_column(self)
32
+ return column
33
+ end
34
+ end
35
+ nil
36
+ end
37
+ end
38
+
39
+ def to_sql
40
+ inspect
41
+ end
42
+
43
+ Symbol.send(:include, self)
44
+ end
45
+
46
+ module PrimitiveExtensions
47
+ def resolve_in_relations(relations)
48
+ self
49
+ end
50
+
51
+ def resolve_in_query(query)
52
+ query.add_literal(self)
53
+ end
54
+
55
+ Numeric.send(:include, self)
56
+ String.send(:include, self)
57
+ TrueClass.send(:include, self)
58
+ FalseClass.send(:include, self)
59
+ NilClass.send(:include, self)
60
+ Time.send(:include, self)
61
+ end
62
+ end
@@ -0,0 +1,12 @@
1
+ module Prequel
2
+ module Expressions
3
+ extend ActiveSupport::Autoload
4
+
5
+ autoload :AliasedExpression
6
+ autoload :Column
7
+ autoload :DerivedColumn
8
+ autoload :Equal
9
+ autoload :Expression
10
+ autoload :SetFunction
11
+ end
12
+ end
@@ -0,0 +1,15 @@
1
+ module Prequel
2
+ module Expressions
3
+ class AliasedExpression
4
+ attr_reader :expression, :alias_name
5
+
6
+ def initialize(expression, alias_name)
7
+ @expression, @alias_name = expression, alias_name
8
+ end
9
+
10
+ def resolve_in_relations(relations)
11
+ AliasedExpression.new(expression.resolve_in_relations(relations), alias_name)
12
+ end
13
+ end
14
+ end
15
+ end
@@ -0,0 +1,35 @@
1
+ module Prequel
2
+ module Expressions
3
+ class Column
4
+ attr_reader :table, :name, :type
5
+
6
+ def initialize(table, name, type)
7
+ @table, @name, @type = table, name, type
8
+ end
9
+
10
+ def alias_name
11
+ nil
12
+ end
13
+
14
+ def eq(other)
15
+ Equal.new(self, other)
16
+ end
17
+
18
+ def qualified_name
19
+ "#{table.name}__#{name}".to_sym
20
+ end
21
+
22
+ def expression
23
+ self
24
+ end
25
+
26
+ def origin
27
+ self
28
+ end
29
+
30
+ def resolve_in_query(query)
31
+ query.singular_table_refs[table].resolve_column(self)
32
+ end
33
+ end
34
+ end
35
+ end
@@ -0,0 +1,24 @@
1
+ module Prequel
2
+ module Expressions
3
+ class DerivedColumn
4
+ attr_reader :relation, :expression, :alias_name
5
+ delegate :origin, :to => :expression
6
+
7
+ def initialize(relation, expression, alias_name)
8
+ @relation, @expression, @alias_name = relation, expression, alias_name
9
+ end
10
+
11
+ def name
12
+ alias_name || expression.name
13
+ end
14
+
15
+ def resolve_in_query(query)
16
+ if subquery = query.singular_table_refs[relation]
17
+ subquery.resolve_derived_column(self)
18
+ else
19
+ expression.resolve_in_query(query)
20
+ end
21
+ end
22
+ end
23
+ end
24
+ end
@@ -0,0 +1,22 @@
1
+ module Prequel
2
+ module Expressions
3
+ class Equal
4
+ attr_reader :left, :right
5
+ def initialize(left, right)
6
+ @left, @right = left, right
7
+ end
8
+
9
+ def resolve_in_relations(relations)
10
+ Equal.new(left.resolve_in_relations(relations), right.resolve_in_relations(relations))
11
+ end
12
+
13
+ def resolve_in_query(query)
14
+ Equal.new(left.resolve_in_query(query), right.resolve_in_query(query))
15
+ end
16
+
17
+ def to_sql
18
+ "#{left.to_sql} = #{right.to_sql}"
19
+ end
20
+ end
21
+ end
22
+ end
@@ -0,0 +1,10 @@
1
+ module Prequel
2
+ module Expressions
3
+ class Expression
4
+ def as(alias_name)
5
+ AliasedExpression.new(self, alias_name)
6
+ end
7
+ end
8
+ end
9
+ end
10
+
@@ -0,0 +1,24 @@
1
+ module Prequel
2
+ module Expressions
3
+ class SetFunction < Expression
4
+ attr_reader :expression, :type
5
+
6
+ def initialize(expression, type)
7
+ @expression, @type = expression, type
8
+ end
9
+
10
+ def resolve_in_relations(relations)
11
+ SetFunction.new(expression.resolve_in_relations(relations), type)
12
+ end
13
+
14
+ def resolve_in_query(query)
15
+ SetFunction.new(expression.resolve_in_query(query), type)
16
+ end
17
+
18
+ def to_sql
19
+ "#{type}(#{expression.to_sql})"
20
+ end
21
+ end
22
+ end
23
+ end
24
+
@@ -0,0 +1,10 @@
1
+ module Prequel
2
+ class Field
3
+ attr_reader :tuple, :column
4
+ attr_accessor :value
5
+
6
+ def initialize(tuple, column)
7
+ @tuple, @column = tuple, column
8
+ end
9
+ end
10
+ end
@@ -0,0 +1,50 @@
1
+ module Prequel
2
+ class Record < Tuple
3
+ class << self
4
+ delegate :all, :result_set, :[], :to_sql, :get_column, :first, :find, :where, :join, :project, :to => :relation
5
+
6
+ def table
7
+ relation
8
+ end
9
+
10
+ def to_relation
11
+ relation
12
+ end
13
+
14
+ def inherited(klass)
15
+ table_name = klass.name.demodulize.underscore.pluralize.to_sym
16
+ klass.relation = Relations::Table.new(table_name, klass)
17
+ end
18
+
19
+ def def_field_accessor(name)
20
+ def_field_reader(name)
21
+ def_field_writer(name)
22
+ end
23
+
24
+ def def_field_writer(name)
25
+ define_method("#{name}=") do |value|
26
+ set_field_value(name, value)
27
+ end
28
+ end
29
+
30
+ def column(name, type)
31
+ relation.def_column(name, type)
32
+ def_field_accessor(name)
33
+ end
34
+
35
+ def new(field_values={})
36
+ Prequel.session[table.name][field_values[:id]] ||= super
37
+ end
38
+ end
39
+
40
+ def table
41
+ relation
42
+ end
43
+
44
+ public :set_field_value
45
+
46
+ def get_record(table_name)
47
+ self if table_name == table.name
48
+ end
49
+ end
50
+ end
@@ -0,0 +1,11 @@
1
+ module Prequel
2
+ module Relations
3
+ extend ActiveSupport::Autoload
4
+
5
+ autoload :InnerJoin
6
+ autoload :Projection
7
+ autoload :Relation
8
+ autoload :Selection
9
+ autoload :Table
10
+ end
11
+ end
@@ -0,0 +1,39 @@
1
+ module Prequel
2
+ module Relations
3
+ class InnerJoin < Relation
4
+ attr_reader :left, :right, :predicate
5
+
6
+ def initialize(left_operand, right_operand, predicate)
7
+ @left, @right = left_operand.to_relation, right_operand.to_relation
8
+ @predicate = resolve(predicate.to_predicate)
9
+ end
10
+
11
+ def get_table(name)
12
+ left.get_table(name) || right.get_table(name)
13
+ end
14
+
15
+ def columns
16
+ (left.columns + right.columns).map do |column|
17
+ derive(column)
18
+ end
19
+ end
20
+
21
+ def visit(query)
22
+ query.table_ref = table_ref(query)
23
+ query.select_list = columns.map do |derived_column|
24
+ query.resolve_derived_column(derived_column, :qualified)
25
+ end
26
+ end
27
+
28
+ def table_ref(query)
29
+ Sql::InnerJoinedTableRef.new(left.table_ref(query), right.singular_table_ref(query), predicate.resolve_in_query(query))
30
+ end
31
+
32
+ protected
33
+
34
+ def operands
35
+ [left, right]
36
+ end
37
+ end
38
+ end
39
+ end
@@ -0,0 +1,86 @@
1
+ module Prequel
2
+ module Relations
3
+ class Projection < Relation
4
+ attr_reader :operand
5
+
6
+ def initialize(operand, *symbols)
7
+ @operand = operand
8
+ assign_derived_columns(symbols)
9
+ end
10
+
11
+ def get_column(name)
12
+ if name.to_s.include?("__")
13
+ super
14
+ else
15
+ derived_columns_by_name[name]
16
+ end
17
+ end
18
+
19
+ def columns
20
+ derived_columns.values
21
+ end
22
+
23
+ def visit(query)
24
+ operand.visit(query)
25
+ query.select_list = columns.map do |derived_column|
26
+ query.resolve_derived_column(derived_column)
27
+ end
28
+
29
+ if projected_table
30
+ query.tuple_builder = query.singular_table_refs[projected_table]
31
+ else
32
+ query.tuple_builder = self
33
+ end
34
+ end
35
+
36
+ def build_tuple(field_values)
37
+ tuple_class.new(field_values)
38
+ end
39
+
40
+ def tuple_class
41
+ @tuple_class ||= Class.new(Tuple).tap do |tuple_class|
42
+ tuple_class.relation = self
43
+ columns.each do |column|
44
+ tuple_class.def_field_reader(column.name)
45
+ end
46
+ end
47
+ end
48
+
49
+ protected
50
+ attr_reader :projected_table
51
+
52
+ def assign_derived_columns(expressions)
53
+ if @projected_table = detect_projected_table(expressions)
54
+ projected_table.columns.map do |column|
55
+ derive(resolve(column.qualified_name.as(column.name)))
56
+ end
57
+ else
58
+ expressions.each do |column_name|
59
+ derive(resolve(column_name))
60
+ end
61
+ end
62
+ end
63
+
64
+ def detect_projected_table(args)
65
+ return false unless args.size == 1
66
+ arg = args.first
67
+ if arg.instance_of?(Table)
68
+ table_name = arg.name
69
+ elsif arg.instance_of?(Class) && arg.respond_to?(:table)
70
+ table_name = arg.table.name
71
+ elsif arg.instance_of?(Symbol)
72
+ return false if arg =~ /__/
73
+ table_name = arg
74
+ else
75
+ return false
76
+ end
77
+
78
+ operand.get_table(table_name)
79
+ end
80
+
81
+ def operands
82
+ [operand]
83
+ end
84
+ end
85
+ end
86
+ end
@@ -0,0 +1,71 @@
1
+ module Prequel
2
+ module Relations
3
+ class Relation
4
+ delegate :to_sql, :result_set, :all, :first, :to => :query
5
+
6
+ def query
7
+ Sql::Query.new(self).build
8
+ end
9
+
10
+ def find(id)
11
+ where(:id => id).first
12
+ end
13
+
14
+ def where(predicate)
15
+ Selection.new(self, predicate)
16
+ end
17
+
18
+ def join(right, predicate)
19
+ InnerJoin.new(self, right, predicate)
20
+ end
21
+
22
+ def project(*symbols)
23
+ Projection.new(self, *symbols)
24
+ end
25
+
26
+ def table_ref(query)
27
+ singular_table_ref(query)
28
+ end
29
+
30
+ def singular_table_ref(query)
31
+ query.add_subquery(self)
32
+ end
33
+
34
+ def to_relation
35
+ self
36
+ end
37
+
38
+ def get_column(name)
39
+ resolved = resolve(name)
40
+ derive(resolved) if resolved
41
+ end
42
+
43
+ protected
44
+
45
+ def resolve(expression)
46
+ expression.resolve_in_relations(operands)
47
+ end
48
+
49
+ def derive(resolved_expression)
50
+ if resolved_expression.instance_of?(Expressions::AliasedExpression)
51
+ alias_name = resolved_expression.alias_name
52
+ resolved_expression = resolved_expression.expression
53
+ end
54
+
55
+ derived_columns[resolved_expression] ||=
56
+ Expressions::DerivedColumn.new(self, resolved_expression, alias_name).tap do |derived_column|
57
+ derived_columns[resolved_expression] = derived_column
58
+ derived_columns_by_name[derived_column.name] = derived_column
59
+ end
60
+ end
61
+
62
+ def derived_columns
63
+ @derived_columns ||= {}
64
+ end
65
+
66
+ def derived_columns_by_name
67
+ @derived_columns_by_name ||= {}
68
+ end
69
+ end
70
+ end
71
+ end
@@ -0,0 +1,31 @@
1
+ module Prequel
2
+ module Relations
3
+ class Selection < Relation
4
+ attr_reader :operand, :predicate
5
+
6
+ def initialize(operand, predicate)
7
+ @operand = operand
8
+ @predicate = resolve(predicate.to_predicate)
9
+ end
10
+
11
+ delegate :get_table, :to => :operand
12
+
13
+ def columns
14
+ operand.columns.map do |column|
15
+ derive(column)
16
+ end
17
+ end
18
+
19
+ def visit(query)
20
+ operand.visit(query)
21
+ query.add_condition(predicate.resolve_in_query(query))
22
+ end
23
+
24
+ protected
25
+
26
+ def operands
27
+ [operand]
28
+ end
29
+ end
30
+ end
31
+ end
@@ -0,0 +1,56 @@
1
+ module Prequel
2
+ module Relations
3
+ class Table < Relation
4
+ attr_reader :name, :columns_by_name, :tuple_class
5
+
6
+ def initialize(name, tuple_class=nil, &block)
7
+ @name, @tuple_class = name, tuple_class
8
+ @columns_by_name = {}
9
+ TableDefinitionContext.new(self).instance_eval(&block) if block
10
+ end
11
+
12
+ def def_column(name, type)
13
+ columns_by_name[name] = Expressions::Column.new(self, name, type)
14
+ end
15
+
16
+ def [](col_name)
17
+ "#{name}__#{col_name}".to_sym
18
+ end
19
+
20
+ def get_column(column_name)
21
+ if column_name.match(/(.+)__(.+)/)
22
+ qualifier, column_name = $1.to_sym, $2.to_sym
23
+ return nil unless qualifier == name
24
+ end
25
+ columns_by_name[column_name]
26
+ end
27
+
28
+ def get_table(table_name)
29
+ self if name == table_name
30
+ end
31
+
32
+ def columns
33
+ columns_by_name.values
34
+ end
35
+
36
+ def visit(query)
37
+ query.table_ref = table_ref(query)
38
+ end
39
+
40
+ def singular_table_ref(query)
41
+ query.add_singular_table_ref(self, Sql::TableRef.new(self))
42
+ end
43
+
44
+ class TableDefinitionContext
45
+ attr_reader :table
46
+ def initialize(table)
47
+ @table = table
48
+ end
49
+
50
+ def column(name, type)
51
+ table.def_column(name, type)
52
+ end
53
+ end
54
+ end
55
+ end
56
+ end
@@ -0,0 +1,12 @@
1
+ module Prequel
2
+ class Session
3
+ def initialize
4
+ @identity_map = Hash.new {|h,k| h[k] = {}}
5
+ end
6
+
7
+ delegate :[], :[]=, :to => :identity_map
8
+
9
+ protected
10
+ attr_reader :identity_map
11
+ end
12
+ end
@@ -0,0 +1,13 @@
1
+ module Prequel
2
+ module Sql
3
+ extend ActiveSupport::Autoload
4
+
5
+ autoload :DerivedQueryColumn
6
+ autoload :InnerJoinedTableRef
7
+ autoload :NamedTableRef
8
+ autoload :Query
9
+ autoload :QueryColumn
10
+ autoload :Subquery
11
+ autoload :TableRef
12
+ end
13
+ end
@@ -0,0 +1,23 @@
1
+ module Prequel
2
+ module Sql
3
+ class DerivedQueryColumn
4
+ attr_reader :subquery, :name, :expression
5
+
6
+ def initialize(subquery, name, expression)
7
+ @subquery, @name, @expression = subquery, name, expression
8
+ end
9
+
10
+ def to_sql
11
+ "#{subquery.name}.#{name}"
12
+ end
13
+
14
+ def to_select_clause_sql
15
+ "#{expression.to_sql} as #{name}"
16
+ end
17
+
18
+ def qualified_name
19
+ "#{subquery.name}__#{name}"
20
+ end
21
+ end
22
+ end
23
+ end
@@ -0,0 +1,23 @@
1
+ module Prequel
2
+ module Sql
3
+ class InnerJoinedTableRef
4
+ attr_reader :left, :right, :predicate
5
+ def initialize(left, right, predicate)
6
+ @left, @right, @predicate = left, right, predicate
7
+ end
8
+
9
+ def to_sql
10
+ [left.to_sql,
11
+ 'inner join',
12
+ right.to_sql,
13
+ 'on',
14
+ predicate.to_sql
15
+ ].join(' ')
16
+ end
17
+
18
+ def build_tuple(field_values)
19
+ CompositeTuple.new(left.build_tuple(field_values), right.build_tuple(field_values))
20
+ end
21
+ end
22
+ end
23
+ end
@@ -0,0 +1,19 @@
1
+ module Prequel
2
+ module Sql
3
+ module NamedTableRef
4
+ protected
5
+
6
+ def extract_field_values(field_values)
7
+ {}.tap do |specific_field_values|
8
+ field_values.each do |field_name, value|
9
+ if field_name =~ /(.+?)__(.+)/
10
+ qualifier, field_name = $1.to_sym, $2.to_sym
11
+ next unless qualifier == name
12
+ end
13
+ specific_field_values[field_name] = value
14
+ end
15
+ end
16
+ end
17
+ end
18
+ end
19
+ end
@@ -0,0 +1,110 @@
1
+ module Prequel
2
+ module Sql
3
+ class Query
4
+ attr_accessor :select_list
5
+ attr_reader :relation, :table_ref, :conditions, :literals, :singular_table_refs, :subquery_count, :query_columns
6
+ attr_writer :tuple_builder
7
+
8
+ def initialize(relation)
9
+ @relation = relation
10
+ @conditions = []
11
+ @literals = {}
12
+ @singular_table_refs = { relation => self }
13
+ @subquery_count = 0
14
+ @query_columns = {}
15
+ end
16
+
17
+ def all
18
+ result_set.map do |field_values|
19
+ tuple_builder.build_tuple(field_values)
20
+ end
21
+ end
22
+
23
+ def first
24
+ r = result_set
25
+ r.empty?? nil : tuple_builder.build_tuple(r.first)
26
+ end
27
+
28
+ def result_set
29
+ DB[*to_sql]
30
+ end
31
+
32
+ def to_sql
33
+ [sql_string, literals]
34
+ end
35
+
36
+ def build
37
+ relation.visit(self)
38
+ self
39
+ end
40
+
41
+ def table_ref=(table_ref)
42
+ raise "A table ref has already been assigned" if @table_ref
43
+ @table_ref = table_ref
44
+ end
45
+
46
+ def add_condition(predicate)
47
+ conditions.push(predicate)
48
+ end
49
+
50
+ def add_literal(literal)
51
+ "v#{literals.size + 1}".to_sym.tap do |placeholder|
52
+ literals[placeholder] = literal
53
+ end
54
+ end
55
+
56
+ def add_singular_table_ref(relation, table_ref)
57
+ singular_table_refs[relation] = table_ref
58
+ end
59
+
60
+ def add_subquery(relation)
61
+ @subquery_count += 1
62
+ subquery = Subquery.new(self, relation, "t#{subquery_count}".to_sym)
63
+ add_singular_table_ref(relation, subquery)
64
+ subquery.build
65
+ end
66
+
67
+ def resolve_derived_column(column, qualified=false)
68
+ query_columns[column] ||= begin
69
+ resolved_expression = column.expression.resolve_in_query(self)
70
+ resolved_name = qualified ? resolved_expression.qualified_name : column.name
71
+ Sql::DerivedQueryColumn.new(self, resolved_name, resolved_expression)
72
+ end
73
+ end
74
+
75
+ def tuple_builder
76
+ @tuple_builder || table_ref
77
+ end
78
+
79
+ protected
80
+
81
+ def sql_string
82
+ ["select",
83
+ select_clause_sql,
84
+ "from",
85
+ from_clause_sql,
86
+ where_clause_sql,
87
+ ].compact.join(" ")
88
+ end
89
+
90
+ def select_clause_sql
91
+ if select_list
92
+ select_list.map {|column| column.to_select_clause_sql}.join(', ')
93
+ else
94
+ '*'
95
+ end
96
+ end
97
+
98
+ def from_clause_sql
99
+ table_ref.to_sql
100
+ end
101
+
102
+ def where_clause_sql
103
+ return nil if conditions.empty?
104
+ 'where ' + conditions.map do |condition|
105
+ condition.to_sql
106
+ end.join(' and ')
107
+ end
108
+ end
109
+ end
110
+ end
@@ -0,0 +1,22 @@
1
+ module Prequel
2
+ module Sql
3
+ class QueryColumn
4
+ attr_reader :table_ref, :name
5
+ def initialize(table_ref, name)
6
+ @table_ref, @name = table_ref, name
7
+ end
8
+
9
+ def to_sql
10
+ "#{table_ref.name}.#{name}"
11
+ end
12
+
13
+ def to_select_clause_sql
14
+ to_sql
15
+ end
16
+
17
+ def qualified_name
18
+ "#{table_ref.name}__#{name}"
19
+ end
20
+ end
21
+ end
22
+ end
@@ -0,0 +1,25 @@
1
+ module Prequel
2
+ module Sql
3
+ class Subquery < Query
4
+ include NamedTableRef
5
+
6
+ attr_reader :parent, :relation, :name
7
+ delegate :columns, :to => :relation
8
+
9
+ def initialize(parent, relation, name)
10
+ @parent, @name = parent, name
11
+ super(relation)
12
+ end
13
+
14
+ delegate :add_literal, :add_singular_table_ref, :add_subquery, :singular_table_refs, :to => :parent
15
+
16
+ def to_sql
17
+ ['(', sql_string, ') as ', name].join
18
+ end
19
+
20
+ def build_tuple(field_values)
21
+ tuple_builder.build_tuple(extract_field_values(field_values))
22
+ end
23
+ end
24
+ end
25
+ end
@@ -0,0 +1,26 @@
1
+ module Prequel
2
+ module Sql
3
+ class TableRef
4
+ include NamedTableRef
5
+ attr_reader :relation, :query_columns
6
+ delegate :name, :columns, :tuple_class, :to => :relation
7
+
8
+ def initialize(relation)
9
+ @relation = relation
10
+ @query_columns = {}
11
+ end
12
+
13
+ def to_sql
14
+ name
15
+ end
16
+
17
+ def resolve_column(column)
18
+ query_columns[column] ||= Sql::QueryColumn.new(self, column.name)
19
+ end
20
+
21
+ def build_tuple(field_values)
22
+ tuple_class.new(extract_field_values(field_values))
23
+ end
24
+ end
25
+ end
26
+ end
@@ -0,0 +1,64 @@
1
+ module Prequel
2
+ class Tuple
3
+ class_attribute :relation
4
+
5
+ class << self
6
+ delegate :columns, :to => :relation
7
+
8
+ def def_field_reader(name)
9
+ define_method(name) do
10
+ get_field_value(name)
11
+ end
12
+ end
13
+ end
14
+
15
+ def initialize(values = {})
16
+ initialize_fields
17
+ soft_update_fields(values)
18
+ end
19
+
20
+ delegate :columns, :to => :relation
21
+
22
+ def soft_update_fields(values)
23
+ values.each do |name, value|
24
+ set_field_value(name, value)
25
+ end
26
+ end
27
+
28
+ def get_field_value(name)
29
+ fields_by_name[name].try(:value)
30
+ end
31
+
32
+ def field_values
33
+ fields_by_name.inject({}) do |h, (name, field)|
34
+ h[name] = field.value
35
+ h
36
+ end
37
+ end
38
+
39
+ def get_record(name)
40
+ nil
41
+ end
42
+
43
+ delegate :inspect, :to => :field_values
44
+
45
+ protected
46
+ attr_reader :fields_by_name
47
+
48
+ def initialize_fields
49
+ @fields_by_name = {}
50
+ columns.each do |column|
51
+ fields_by_name[column.name] = Field.new(self, column)
52
+ end
53
+ end
54
+
55
+ def set_field_value(name, value)
56
+ field = fields_by_name[name]
57
+ unless field
58
+ raise "No field found #{name.inspect}"
59
+ end
60
+ field.value = value
61
+ end
62
+ end
63
+ end
64
+
@@ -0,0 +1,3 @@
1
+ module Prequel
2
+ VERSION = "0.0.1" unless defined?(::Prequel::VERSION)
3
+ end
metadata ADDED
@@ -0,0 +1,119 @@
1
+ --- !ruby/object:Gem::Specification
2
+ name: prequel
3
+ version: !ruby/object:Gem::Version
4
+ prerelease:
5
+ version: 0.0.1
6
+ platform: ruby
7
+ authors:
8
+ - Nathan Sobo
9
+ autorequire:
10
+ bindir: bin
11
+ cert_chain: []
12
+
13
+ date: 2011-03-03 00:00:00 -08:00
14
+ default_executable:
15
+ dependencies:
16
+ - !ruby/object:Gem::Dependency
17
+ name: activesupport
18
+ prerelease: false
19
+ requirement: &id001 !ruby/object:Gem::Requirement
20
+ none: false
21
+ requirements:
22
+ - - ">="
23
+ - !ruby/object:Gem::Version
24
+ version: 3.0.4
25
+ type: :runtime
26
+ version_requirements: *id001
27
+ - !ruby/object:Gem::Dependency
28
+ name: sequel
29
+ prerelease: false
30
+ requirement: &id002 !ruby/object:Gem::Requirement
31
+ none: false
32
+ requirements:
33
+ - - ">="
34
+ - !ruby/object:Gem::Version
35
+ version: 3.20.0
36
+ type: :runtime
37
+ version_requirements: *id002
38
+ - !ruby/object:Gem::Dependency
39
+ name: rspec
40
+ prerelease: false
41
+ requirement: &id003 !ruby/object:Gem::Requirement
42
+ none: false
43
+ requirements:
44
+ - - ">="
45
+ - !ruby/object:Gem::Version
46
+ version: "0"
47
+ type: :development
48
+ version_requirements: *id003
49
+ description: Prequel is the database library I've always wanted.
50
+ email:
51
+ - nathansobo@gmail.com
52
+ executables: []
53
+
54
+ extensions: []
55
+
56
+ extra_rdoc_files: []
57
+
58
+ files:
59
+ - lib/prequel/composite_tuple.rb
60
+ - lib/prequel/core_extensions.rb
61
+ - lib/prequel/expressions/aliased_expression.rb
62
+ - lib/prequel/expressions/column.rb
63
+ - lib/prequel/expressions/derived_column.rb
64
+ - lib/prequel/expressions/equal.rb
65
+ - lib/prequel/expressions/expression.rb
66
+ - lib/prequel/expressions/set_function.rb
67
+ - lib/prequel/expressions.rb
68
+ - lib/prequel/field.rb
69
+ - lib/prequel/record.rb
70
+ - lib/prequel/relations/inner_join.rb
71
+ - lib/prequel/relations/projection.rb
72
+ - lib/prequel/relations/relation.rb
73
+ - lib/prequel/relations/selection.rb
74
+ - lib/prequel/relations/table.rb
75
+ - lib/prequel/relations.rb
76
+ - lib/prequel/session.rb
77
+ - lib/prequel/sql/derived_query_column.rb
78
+ - lib/prequel/sql/inner_joined_table_ref.rb
79
+ - lib/prequel/sql/named_table_ref.rb
80
+ - lib/prequel/sql/query.rb
81
+ - lib/prequel/sql/query_column.rb
82
+ - lib/prequel/sql/subquery.rb
83
+ - lib/prequel/sql/table_ref.rb
84
+ - lib/prequel/sql.rb
85
+ - lib/prequel/tuple.rb
86
+ - lib/prequel/version.rb
87
+ - lib/prequel.rb
88
+ - LICENSE
89
+ - README.md
90
+ has_rdoc: true
91
+ homepage: http://github.com/nathansobo/prequel
92
+ licenses: []
93
+
94
+ post_install_message:
95
+ rdoc_options: []
96
+
97
+ require_paths:
98
+ - lib
99
+ required_ruby_version: !ruby/object:Gem::Requirement
100
+ none: false
101
+ requirements:
102
+ - - ">="
103
+ - !ruby/object:Gem::Version
104
+ version: "0"
105
+ required_rubygems_version: !ruby/object:Gem::Requirement
106
+ none: false
107
+ requirements:
108
+ - - ">="
109
+ - !ruby/object:Gem::Version
110
+ version: "0"
111
+ requirements: []
112
+
113
+ rubyforge_project:
114
+ rubygems_version: 1.5.2
115
+ signing_key:
116
+ specification_version: 3
117
+ summary: A ground-up relational algebraic ORM.
118
+ test_files: []
119
+