From 42c42bfa50876b4221c024cebc47fc34fc6530a9 Mon Sep 17 00:00:00 2001 From: Nick Kallen Date: Fri, 11 Apr 2008 13:47:06 -0700 Subject: moved bind to factory of select --- CONVENTIONS | 17 ------ Rakefile | 1 - TODO | 68 --------------------- doc/CONVENTIONS | 17 ++++++ doc/TODO | 69 ++++++++++++++++++++++ lib/active_relation/predicates.rb | 20 ++----- lib/active_relation/relations/relation.rb | 2 +- lib/active_relation/relations/selection.rb | 3 +- .../unit/relations/relation_spec.rb | 2 +- .../unit/relations/selection_spec.rb | 10 +++- 10 files changed, 102 insertions(+), 107 deletions(-) delete mode 100644 CONVENTIONS delete mode 100644 TODO create mode 100644 doc/CONVENTIONS create mode 100644 doc/TODO diff --git a/CONVENTIONS b/CONVENTIONS deleted file mode 100644 index 0d7c1b4ef0..0000000000 --- a/CONVENTIONS +++ /dev/null @@ -1,17 +0,0 @@ -This file should ultimately be replaced by a series of tests, something like a lint tool. - -- all describes and its should use single quotes unless they have nested quotes. -- object instantiation in tests for all objects that are not the SUT should be manufactured in a before -- 'should' and other counterfactuals/subjunctive forms should not be used in tests -- no doubles should be used except for behaviorist testing - - behaviorist testing is desirable only when interacting with the database or the session -- when unit testing, always demonstrate behavior by using a real world example (as in, a public use of the API), so as to provide documentation. -- use collect rather than map -- jargon: - - 'obtains' is preferred to 'returns true' - - 'manufactures' -- in tests - - when manufacturing expected values (right-hand-side of should), avoid convenience methods -- construct it by initializing the object directly (Foo.new(...)). This ensures equality expectations in tests is rigorous. - - the SUT should be manufactured inline inside the test, not in a before - - dependencies for the SUT should be manufactured using convenience methods (or whatever is most terse). -- group conceptually related methods in a class within an inline module; immediately include that module. \ No newline at end of file diff --git a/Rakefile b/Rakefile index e45ade4f7d..b1fc74a13c 100644 --- a/Rakefile +++ b/Rakefile @@ -1,5 +1,4 @@ require 'rubygems' -require 'spec' require 'spec/rake/spectask' Spec::Rake::SpecTask.new do |t| diff --git a/TODO b/TODO deleted file mode 100644 index d7b57da4e2..0000000000 --- a/TODO +++ /dev/null @@ -1,68 +0,0 @@ -todo: -- string passthrough: - :joins=>"INNER JOIN posts ON comments.post_id = posts.id" - - shit this one is hard at the moment. - -- need adapters for this form: - {:conditions=>["approved = ?", false]} - {:conditions=>{:approved=>false}} - {:conditions=>{"topics.approved"=>false}} - {:conditions=>{:address=>#, "customers.name"=>"David1"}} - -- re-evaluate bind -- does bind belong inside the relation / predicate classes or in the factory methods? -- #bind in Attribute and Expression should be doing a descend? -- try to make aggration testing in join spec to be a bit more unit-like -- finish pending tests -- test relation, table reset -- cache expiry on write - - rewrite of querycache test in light of this -- standardize quoting - - use strings everywhere, not symbols ? -- "unit" test sql strategies - - use real world examples, so they should be like a tutorial. -- rename the tion (Selection) classes so that words that don't end in tion don't seem inconsistent -- mock out database - -done: -. Relation <=> Relation -> InnerJoinOperation -. Relation << Relation -> LeftOuterJoinOperation -. InnerJoinOperation.on(*Predicate) -> InnerJoinRelation -. LeftOuterJoinOperation.on(*Predicate) -> LeftOuterJoinRelation -. Relation[Symbol] -> Attribute -. Relation[Range] -> Relation -. Attribute == Attribute -> EqualityPredicate -. Attribute >= Attribute -> GreaterThanOrEqualToPredicate -. Relation.include?(Column) -> Predicate -. Relation.project(*Column) -> ProjectionRelation -. Relation.select(*Predicate) -> SelectionRelation -. Relation.order(*Column) -> OrderRelation -. #to_sql -. Remove Builder -. Namespace -. Audit SqlAlchemy for missing features -- Generalized denormalizations on any aggregation (count, yes, but also max, min, average) -- Remove operator overloading of << and <=> for joins. Make it just foo.join(bar) and foo.outer_join(bar). -- Remove operator overloading of == for predicates. make it a.eq(b) (note lack of question mark). -- hookup more predicates (=, <=, =>) -- get some basic aggregations working: users.project(user[:points].max) -- Alias Table Names -- When joining with any sort of aggregation, it needs to be a nested select -- get a value select working: users.project(users[:name], addresses.select(addresses[:user_id] == users[:id]).project(addresses[:id].count)) -- Session -- sublimate values to deal with the fact that they must be quoted per engine -- clean-up singleton monstrosity -- extract hashing module -- hash custom matcher -- make session engine stuff follow laws of demeter - currently doing some odd method chaining? rethink who is responsible for what - - session just calls execute, passing in a connection; by default it gets a connection from the relation. -- #formatter is now on value, attribute and relation; you must admit it's name is confusing given that e.g., relation already has a formatter (Sql::Relation) ... should it be called predicate formatter? operand1.to_sql(operand2.predicate) maybe prefer operand1.cast(operand2) or project or in light of - - renamed to #format: operand1.format(operand2) -- rename sql strategies -- need to_sql for ranges - - {:conditions=>{:id=>2..3}} -- nested orderings -- string passthrough - - conditions - - orderings -- relation inclusion when given an array (1,2,3,4) should quote the elements using the appropriate quoting formatter taken from the attribute - - descend on array, along with bind written in terms of it diff --git a/doc/CONVENTIONS b/doc/CONVENTIONS new file mode 100644 index 0000000000..0d7c1b4ef0 --- /dev/null +++ b/doc/CONVENTIONS @@ -0,0 +1,17 @@ +This file should ultimately be replaced by a series of tests, something like a lint tool. + +- all describes and its should use single quotes unless they have nested quotes. +- object instantiation in tests for all objects that are not the SUT should be manufactured in a before +- 'should' and other counterfactuals/subjunctive forms should not be used in tests +- no doubles should be used except for behaviorist testing + - behaviorist testing is desirable only when interacting with the database or the session +- when unit testing, always demonstrate behavior by using a real world example (as in, a public use of the API), so as to provide documentation. +- use collect rather than map +- jargon: + - 'obtains' is preferred to 'returns true' + - 'manufactures' +- in tests + - when manufacturing expected values (right-hand-side of should), avoid convenience methods -- construct it by initializing the object directly (Foo.new(...)). This ensures equality expectations in tests is rigorous. + - the SUT should be manufactured inline inside the test, not in a before + - dependencies for the SUT should be manufactured using convenience methods (or whatever is most terse). +- group conceptually related methods in a class within an inline module; immediately include that module. \ No newline at end of file diff --git a/doc/TODO b/doc/TODO new file mode 100644 index 0000000000..6d6b82082a --- /dev/null +++ b/doc/TODO @@ -0,0 +1,69 @@ +todo: +- re-evaluate bind -- does bind belong inside the relation / predicate classes or in the factory methods? +- - mock out database +- #bind in Attribute and Expression should be doing a descend? +- try to make aggegration testing in join spec to be a bit more unit-like +- finish pending tests +- test relation, table reset +- standardize quoting + - use strings everywhere, not symbols ? +- "unit" test sql strategies + - use real world examples, so they should be like a tutorial. + +- string passthrough: + :joins=>"INNER JOIN posts ON comments.post_id = posts.id" + - shit this one is hard at the moment. + +- need adapters for this form: + {:conditions=>["approved = ?", false]} + {:conditions=>{:approved=>false}} + {:conditions=>{"topics.approved"=>false}} + {:conditions=>{:address=>#, "customers.name"=>"David1"}} + +- cache expiry on write + - rewrite of querycache test in light of this +- rename the tion (Selection) classes so that words that don't end in tion don't seem inconsistent + +done: +. Relation <=> Relation -> InnerJoinOperation +. Relation << Relation -> LeftOuterJoinOperation +. InnerJoinOperation.on(*Predicate) -> InnerJoinRelation +. LeftOuterJoinOperation.on(*Predicate) -> LeftOuterJoinRelation +. Relation[Symbol] -> Attribute +. Relation[Range] -> Relation +. Attribute == Attribute -> EqualityPredicate +. Attribute >= Attribute -> GreaterThanOrEqualToPredicate +. Relation.include?(Column) -> Predicate +. Relation.project(*Column) -> ProjectionRelation +. Relation.select(*Predicate) -> SelectionRelation +. Relation.order(*Column) -> OrderRelation +. #to_sql +. Remove Builder +. Namespace +. Audit SqlAlchemy for missing features +- Generalized denormalizations on any aggregation (count, yes, but also max, min, average) +- Remove operator overloading of << and <=> for joins. Make it just foo.join(bar) and foo.outer_join(bar). +- Remove operator overloading of == for predicates. make it a.eq(b) (note lack of question mark). +- hookup more predicates (=, <=, =>) +- get some basic aggregations working: users.project(user[:points].max) +- Alias Table Names +- When joining with any sort of aggregation, it needs to be a nested select +- get a value select working: users.project(users[:name], addresses.select(addresses[:user_id] == users[:id]).project(addresses[:id].count)) +- Session +- sublimate values to deal with the fact that they must be quoted per engine +- clean-up singleton monstrosity +- extract hashing module +- hash custom matcher +- make session engine stuff follow laws of demeter - currently doing some odd method chaining? rethink who is responsible for what + - session just calls execute, passing in a connection; by default it gets a connection from the relation. +- #formatter is now on value, attribute and relation; you must admit it's name is confusing given that e.g., relation already has a formatter (Sql::Relation) ... should it be called predicate formatter? operand1.to_sql(operand2.predicate) maybe prefer operand1.cast(operand2) or project or in light of + - renamed to #format: operand1.format(operand2) +- rename sql strategies +- need to_sql for ranges + - {:conditions=>{:id=>2..3}} +- nested orderings +- string passthrough + - conditions + - orderings +- relation inclusion when given an array (1,2,3,4) should quote the elements using the appropriate quoting formatter taken from the attribute + - descend on array, along with bind written in terms of it diff --git a/lib/active_relation/predicates.rb b/lib/active_relation/predicates.rb index 22fbcd9f0b..e17a9f82fe 100644 --- a/lib/active_relation/predicates.rb +++ b/lib/active_relation/predicates.rb @@ -46,27 +46,19 @@ module ActiveRelation end class GreaterThanOrEqualTo < Binary - def predicate_sql - '>=' - end + def predicate_sql; '>=' end end class GreaterThan < Binary - def predicate_sql - '>' - end + def predicate_sql; '>' end end class LessThanOrEqualTo < Binary - def predicate_sql - '<=' - end + def predicate_sql; '<=' end end class LessThan < Binary - def predicate_sql - '<' - end + def predicate_sql; '<' end end class Match < Binary @@ -74,8 +66,6 @@ module ActiveRelation end class In < Binary - def predicate_sql - operand2.inclusion_predicate_sql - end + def predicate_sql; operand2.inclusion_predicate_sql end end end \ No newline at end of file diff --git a/lib/active_relation/relations/relation.rb b/lib/active_relation/relations/relation.rb index db61fce3de..f7e47c2e50 100644 --- a/lib/active_relation/relations/relation.rb +++ b/lib/active_relation/relations/relation.rb @@ -38,7 +38,7 @@ module ActiveRelation end def select(*predicates) - Selection.new(self, *predicates.collect {|p| p.bind(self)}) + Selection.new(self, *predicates) end def project(*attributes) diff --git a/lib/active_relation/relations/selection.rb b/lib/active_relation/relations/selection.rb index fe28908cc2..032de63d04 100644 --- a/lib/active_relation/relations/selection.rb +++ b/lib/active_relation/relations/selection.rb @@ -3,8 +3,9 @@ module ActiveRelation attr_reader :predicate def initialize(relation, *predicates) - @predicate = predicates.shift + predicate = predicates.shift @relation = predicates.empty?? relation : Selection.new(relation, *predicates) + @predicate = predicate.bind(@relation) end def ==(other) diff --git a/spec/active_relation/unit/relations/relation_spec.rb b/spec/active_relation/unit/relations/relation_spec.rb index d434a1e317..7bb85d7270 100644 --- a/spec/active_relation/unit/relations/relation_spec.rb +++ b/spec/active_relation/unit/relations/relation_spec.rb @@ -91,7 +91,7 @@ module ActiveRelation end it "accepts arbitrary strings" do - @relation.select("arbitrary").should == Selection.new(@relation, Value.new("arbitrary", @relation)) + @relation.select("arbitrary").should == Selection.new(@relation, "arbitrary") end end diff --git a/spec/active_relation/unit/relations/selection_spec.rb b/spec/active_relation/unit/relations/selection_spec.rb index 001c38c370..1919d3007e 100644 --- a/spec/active_relation/unit/relations/selection_spec.rb +++ b/spec/active_relation/unit/relations/selection_spec.rb @@ -26,9 +26,13 @@ module ActiveRelation end describe '#descend' do + before do + @selection = Selection.new(@relation, @predicate) + end + it "distributes a block over the relation and predicates" do - Selection.new(@relation, @predicate).descend(&:qualify). \ - should == Selection.new(@relation.descend(&:qualify), @predicate.descend(&:qualify)) + @selection.descend(&:qualify). \ + should == Selection.new(@selection.relation.descend(&:qualify), @selection.predicate.qualify) end end @@ -45,7 +49,7 @@ module ActiveRelation describe 'when given a string' do before do - @string = "asdf".bind(@relation) + @string = "asdf" end it "passes the string through to the where clause" do -- cgit v1.2.3