friendly 0.3.5 → 0.4.0

Sign up to get free protection for your applications and to get access to all the features.
@@ -0,0 +1,22 @@
1
+ require File.expand_path("../../spec_helper", __FILE__)
2
+ require "active_support/core_ext"
3
+
4
+ describe "Chaining scopes together" do
5
+ describe "then calling #all" do
6
+ before do
7
+ User.all(:name => "Quagmire").each { |q| q.destroy }
8
+ @users = (0...10).map do |i|
9
+ User.create :name => "Quagmire",
10
+ :created_at => i.hours.ago
11
+ end
12
+ end
13
+
14
+ it "queries using a combination of both scopes" do
15
+ User.named_quagmire.recent.all.should == @users.slice(0, 3)
16
+ end
17
+
18
+ it "gives scopes on the right priority" do
19
+ User.named_joe.named_quagmire.first.name.should == "Quagmire"
20
+ end
21
+ end
22
+ end
@@ -7,16 +7,20 @@ describe "Creating the tables for a model" do
7
7
 
8
8
  def self.table_name; "stuffs"; end
9
9
 
10
- attribute :name, String
10
+ attribute :name, String
11
+ attribute :user_id, Friendly::UUID
11
12
 
12
13
  indexes [:name, :created_at]
14
+ indexes :user_id
13
15
  end
14
16
 
15
17
  @klass.create_tables!
16
- @schema = Friendly.db.schema(:stuffs)
17
- @table = Hash[*@schema.map { |s| [s.first, s.last] }.flatten]
18
- @index_schema = Friendly.db.schema(:index_stuffs_on_name_and_created_at)
19
- @index_table = Hash[*@index_schema.map { |s| [s.first, s.last] }.flatten]
18
+ @schema = Friendly.db.schema(:stuffs)
19
+ @table = Hash[*@schema.map { |s| [s.first, s.last] }.flatten]
20
+ @index_schema = Friendly.db.schema(:index_stuffs_on_name_and_created_at)
21
+ @index_table = Hash[*@index_schema.map { |s| [s.first, s.last] }.flatten]
22
+ @id_idx_schema = Friendly.db.schema(:index_stuffs_on_user_id)
23
+ @id_index_table = Hash[*@id_idx_schema.map { |s| [s.first, s.last] }.flatten]
20
24
  end
21
25
 
22
26
  after do
@@ -44,6 +48,14 @@ describe "Creating the tables for a model" do
44
48
  @index_table[:id][:primary_key].should be_true
45
49
  end
46
50
 
51
+ it "knows how to create an index for a field of a custom type" do
52
+ @id_index_table.keys.length.should == 2
53
+ @id_index_table[:user_id][:db_type].should == "binary(16)"
54
+ @id_index_table[:user_id][:primary_key].should be_true
55
+ @id_index_table[:id][:db_type].should == "binary(16)"
56
+ @id_index_table[:id][:primary_key].should be_true
57
+ end
58
+
47
59
  it "doesn't raise if the tables already exist" do
48
60
  lambda {
49
61
  @klass.create_tables!
data/spec/spec_helper.rb CHANGED
@@ -7,13 +7,14 @@ require 'rubygems'
7
7
  require 'spec'
8
8
  require 'spec/autorun'
9
9
  require 'sequel'
10
- require 'json'
10
+ require 'json/pure'
11
11
  gem 'jferris-mocha'
12
12
  require 'mocha'
13
13
  require 'memcached'
14
14
  require 'friendly'
15
15
 
16
- Friendly.configure "mysql://root@localhost/friendly_test"
16
+ config = YAML.load(File.read(File.dirname(__FILE__) + "/config.yml"))['test']
17
+ Friendly.configure config
17
18
  $db = Friendly.db
18
19
  Sequel::MySQL.default_engine = "InnoDB"
19
20
 
@@ -32,13 +33,25 @@ Friendly.cache = Friendly::Memcached.new($cache)
32
33
  class User
33
34
  include Friendly::Document
34
35
 
35
- attribute :name, String
36
- attribute :age, Integer
37
- attribute :happy, Friendly::Boolean, :default => true
38
- attribute :sad, Friendly::Boolean, :default => false
36
+ attribute :name, String
37
+ attribute :age, Integer
38
+ attribute :happy, Friendly::Boolean, :default => true
39
+ attribute :sad, Friendly::Boolean, :default => false
40
+ attribute :friend, Friendly::UUID
39
41
 
42
+ indexes :happy
43
+ indexes :friend
40
44
  indexes :name
41
45
  indexes :name, :created_at
46
+
47
+ named_scope :named_joe, :name => "Joe"
48
+ named_scope :named_quagmire, :name => "Quagmire"
49
+ named_scope :recent, :order! => :created_at.desc,
50
+ :limit! => 3
51
+
52
+ has_many :addresses
53
+ has_many :addresses_override, :class_name => "Address",
54
+ :foreign_key => :user_id
42
55
  end
43
56
 
44
57
  User.create_tables!
@@ -0,0 +1,57 @@
1
+ require File.expand_path("../../../spec_helper", __FILE__)
2
+
3
+ describe "Friendly::Associations::Association" do
4
+ before do
5
+ @owner_klass = stub(:name => "User")
6
+ @klass = stub
7
+ # FIXME: ugh.
8
+ String.any_instance.stubs(:constantize).returns(@klass)
9
+ @assoc_klass = Friendly::Associations::Association
10
+ end
11
+
12
+ describe "with defaults" do
13
+ before do
14
+ @association = @assoc_klass.new(@owner_klass, :addresses)
15
+ end
16
+
17
+ it "has a default klass of name.classify.constantize" do
18
+ @association.klass.should == @klass
19
+ end
20
+
21
+ it "has a foreign_key of owner_klass.name.singularize + '_id'" do
22
+ @association.foreign_key.should == :user_id
23
+ end
24
+
25
+ it "returns a scope on klass of {:foreign_key => document.id}" do
26
+ @scope = stub
27
+ @klass.stubs(:scope).with(:user_id => 42).returns(@scope)
28
+
29
+ @association.scope(stub(:id => 42)).should == @scope
30
+ end
31
+ end
32
+
33
+ describe "with overridden attributes" do
34
+ before do
35
+ @klass = stub
36
+ @class_name = "SomeOtherClass"
37
+ @class_name.stubs(:constantize).returns(@klass)
38
+ @association = @assoc_klass.new @owner_klass, :whatever,
39
+ :class_name => @class_name,
40
+ :foreign_key => :other_id
41
+ end
42
+
43
+ it "uses the overridden class_name to get the class" do
44
+ @association.klass.should == @klass
45
+ end
46
+
47
+ it "uses the overridden foreign_key" do
48
+ @association.foreign_key.should == :other_id
49
+ end
50
+
51
+ it "uses the override foreign_key in the scope" do
52
+ @scope = stub
53
+ @klass.stubs(:scope).with(:other_id => 42).returns(@scope)
54
+ @association.scope(stub(:id => 42)).should == @scope
55
+ end
56
+ end
57
+ end
@@ -0,0 +1,43 @@
1
+ require File.expand_path("../../../spec_helper", __FILE__)
2
+
3
+ describe "Friendly::Associations::Set" do
4
+ before do
5
+ @klass = Class.new
6
+ @association_klass = stub
7
+ @set = Friendly::Associations::Set.new(@klass, @association_klass)
8
+ @assoc = stub
9
+ @association_klass.stubs(:new).
10
+ with(@klass, :my_awesome_association, {}).returns(@assoc)
11
+ end
12
+
13
+ describe "adding an association" do
14
+ before do
15
+ @set.add(:my_awesome_association)
16
+ end
17
+
18
+ it "creates an association and adds it to its hash by name" do
19
+ @set.associations[:my_awesome_association].should == @assoc
20
+ end
21
+
22
+ it "adds an instance method to the klass through which to acces the assoc" do
23
+ @doc = @klass.new
24
+ @scope = stub
25
+ @klass.stubs(:association_set).returns(@set)
26
+ @assoc.stubs(:scope).with(@doc).returns(@scope)
27
+ @doc.my_awesome_association.should == @scope
28
+ end
29
+ end
30
+
31
+ it "can return the association by name" do
32
+ @set.add(:my_awesome_association)
33
+ @set.get(:my_awesome_association).should == @assoc
34
+ end
35
+
36
+ it "provides the scope for an association by name" do
37
+ @doc = stub
38
+ @scope = stub
39
+ @assoc.stubs(:scope).with(@doc).returns(@scope)
40
+ @set.add(:my_awesome_association)
41
+ @set.get_scope(:my_awesome_association, @doc).should == @scope
42
+ end
43
+ end
@@ -61,4 +61,45 @@ describe "Friendly::Attribute" do
61
61
  it "has a default value even if it's false" do
62
62
  @false.default.should be_false
63
63
  end
64
+
65
+ describe "registering a type" do
66
+ before do
67
+ @klass = Class.new
68
+ Friendly::Attribute.register_type(@klass, "binary(16)") { |t| t.to_i }
69
+ end
70
+
71
+ after { Friendly::Attribute.deregister_type(@klass) }
72
+
73
+ it "tells Attribute about the sql_type" do
74
+ Friendly::Attribute.sql_type(@klass).should == "binary(16)"
75
+ end
76
+
77
+ it "registers the conversion method" do
78
+ Friendly::Attribute.new(@klass, :something, @klass).convert("1").should == 1
79
+ end
80
+
81
+ it "is custom_type?(@klass)" do
82
+ Friendly::Attribute.should be_custom_type(@klass)
83
+ end
84
+ end
85
+
86
+ describe "deregistering a type" do
87
+ before do
88
+ @klass = Class.new
89
+ Friendly::Attribute.register_type(@klass, "whatever") {}
90
+ Friendly::Attribute.deregister_type(@klass)
91
+ end
92
+
93
+ it "removes the converter method" do
94
+ Friendly::Attribute.converters.should_not be_has_key(@klass)
95
+ end
96
+
97
+ it "removes the sql_type" do
98
+ Friendly::Attribute.sql_types.should_not be_has_key(@klass)
99
+ end
100
+
101
+ it "is not custom_type?(@klass)" do
102
+ Friendly::Attribute.should_not be_custom_type(@klass)
103
+ end
104
+ end
64
105
  end
@@ -307,5 +307,52 @@ describe "Friendly::Document" do
307
307
  @collection.should have_received(:replace).with(@docs)
308
308
  end
309
309
  end
310
+
311
+ describe "creating a named_scope" do
312
+ before do
313
+ @scope_proxy = stub(:add_named => nil)
314
+ @klass.scope_proxy = @scope_proxy
315
+ @klass.named_scope(:by_name, :order => :name)
316
+ end
317
+
318
+ it "asks the named_scope_set to add it" do
319
+ @klass.scope_proxy.should have_received(:add_named).
320
+ with(:by_name, :order => :name)
321
+ end
322
+ end
323
+
324
+ describe "Document.has_named_scope?" do
325
+ it "delegates to the scope_proxy" do
326
+ @scope_proxy = stub
327
+ @scope_proxy.stubs(:has_named_scope?).with(:whatever).returns(true)
328
+ @klass.scope_proxy = @scope_proxy
329
+ @klass.has_named_scope?(:whatever).should be_true
330
+ end
331
+ end
332
+
333
+ describe "accessing an ad-hoc scope" do
334
+ before do
335
+ @scope = stub
336
+ @scope_proxy = stub
337
+ @scope_proxy.stubs(:ad_hoc).with(:order! => :name).returns(@scope)
338
+ @klass.scope_proxy = @scope_proxy
339
+ end
340
+
341
+ it "asks the named_scope_set to add it" do
342
+ @klass.scope(:order! => :name).should == @scope
343
+ end
344
+ end
345
+
346
+ describe "adding an association" do
347
+ before do
348
+ @assoc_set = stub(:add => nil)
349
+ @klass.association_set = @assoc_set
350
+ @klass.has_many :addresses
351
+ end
352
+
353
+ it "asks the association set to add it" do
354
+ @assoc_set.should have_received(:add).with(:addresses, {})
355
+ end
356
+ end
310
357
  end
311
358
 
@@ -0,0 +1,16 @@
1
+ require File.expand_path("../../spec_helper", __FILE__)
2
+
3
+ describe "Friendly::NamedScope" do
4
+ before do
5
+ @klass = stub
6
+ @scope = stub
7
+ @scope_klass = stub
8
+ @parameters = {:name => "James"}
9
+ @scope_klass.stubs(:new).with(@klass, @parameters).returns(@scope)
10
+ @named_scope = Friendly::NamedScope.new(@klass, @parameters, @scope_klass)
11
+ end
12
+
13
+ it "provides scope instances with the given parameters" do
14
+ @named_scope.scope.should == @scope
15
+ end
16
+ end
@@ -0,0 +1,44 @@
1
+ require File.expand_path("../../spec_helper", __FILE__)
2
+
3
+ describe "Friendly::ScopeProxy" do
4
+ before do
5
+ @klass = Class.new
6
+ @scope = stub
7
+ @scope_klass = stub
8
+ @scope_proxy = Friendly::ScopeProxy.new(@klass, @scope_klass)
9
+ @params = {:order! => :created_at.desc}
10
+ @scope_klass.stubs(:new).with(@klass, @params).returns(@scope)
11
+ @klass.stubs(:scope_proxy).returns(@scope_proxy)
12
+ @scope_proxy.add_named(:recent, @params)
13
+ end
14
+
15
+ it "returns false for has_named_scope if it doesnt have one by that name" do
16
+ @scope_proxy.should_not be_has_named_scope(:whatever)
17
+ end
18
+
19
+ describe "adding a scope" do
20
+ it "adds a scope by that name to the set" do
21
+ @scope_proxy.get(:recent).should == @params
22
+ end
23
+
24
+ it "adds a method to the klass that returns an instance of the scope" do
25
+ @klass.recent.should == @scope
26
+ end
27
+
28
+ it "returns true for #has_named_scope?(name)" do
29
+ @scope_proxy.should be_has_named_scope(:recent)
30
+ end
31
+ end
32
+
33
+ describe "getting an instance of a scope" do
34
+ it "instantiates Scope" do
35
+ @scope_proxy.get_instance(:recent).should == @scope
36
+ end
37
+ end
38
+
39
+ describe "accessing an ad_hoc scope" do
40
+ it "instantiates Scope" do
41
+ @scope_proxy.ad_hoc(@params).should == @scope
42
+ end
43
+ end
44
+ end
@@ -0,0 +1,113 @@
1
+ require File.expand_path("../../spec_helper", __FILE__)
2
+
3
+ describe "Friendly::Scope" do
4
+ before do
5
+ @klass = stub
6
+ @scope_parameters = {:name => "Quagmire", :order! => :created_at.desc}
7
+ @scope = Friendly::Scope.new(@klass, @scope_parameters)
8
+ end
9
+
10
+ describe "#all" do
11
+ before do
12
+ @documents = stub
13
+ end
14
+
15
+ it "delegates to klass with the scope parameters" do
16
+ @klass.stubs(:all).with(@scope_parameters).returns(@documents)
17
+ @scope.all.should == @documents
18
+ end
19
+
20
+ it "merges additional parameters" do
21
+ merged = @scope_parameters.merge(:name => "Joe")
22
+ @klass.stubs(:all).with(merged).returns(@documents)
23
+ @scope.all(:name => "Joe").should == @documents
24
+ end
25
+ end
26
+
27
+ describe "#first" do
28
+ before do
29
+ @document = stub
30
+ end
31
+
32
+ it "delegates to klass with the scope parameters" do
33
+ @klass.stubs(:first).with(@scope_parameters).returns(@document)
34
+ @scope.first.should == @document
35
+ end
36
+
37
+ it "merges additional parameters" do
38
+ merged = @scope_parameters.merge(:name => "Joe")
39
+ @klass.stubs(:first).with(merged).returns(@document)
40
+ @scope.first(:name => "Joe").should == @document
41
+ end
42
+ end
43
+
44
+ describe "#paginate" do
45
+ before do
46
+ @documents = stub
47
+ end
48
+
49
+ it "delegates to klass with the scope parameters" do
50
+ @klass.stubs(:paginate).with(@scope_parameters).returns(@documents)
51
+ @scope.paginate.should == @documents
52
+ end
53
+
54
+ it "merges additional parameters" do
55
+ merged = @scope_parameters.merge(:name => "Joe")
56
+ @klass.stubs(:paginate).with(merged).returns(@documents)
57
+ @scope.paginate(:name => "Joe").should == @documents
58
+ end
59
+ end
60
+
61
+ describe "#build" do
62
+ it "instantiates klass with the scope parameters (minus modifiers)" do
63
+ @doc = stub
64
+ @klass.stubs(:new).with(:name => "Quagmire").returns(@doc)
65
+ @scope.build.should == @doc
66
+ end
67
+
68
+ it "merges additional parameters" do
69
+ @doc = stub
70
+ @klass.stubs(:new).with(:name => "Fred").returns(@doc)
71
+ @scope.build(:name => "Fred").should == @doc
72
+ end
73
+ end
74
+
75
+ describe "#create" do
76
+ it "delegates to klass#create with the scope parameters (minus modifiers)" do
77
+ @doc = stub
78
+ @klass.stubs(:create).with(:name => "Quagmire").returns(@doc)
79
+ @scope.create.should == @doc
80
+ end
81
+
82
+ it "merges additional parameters" do
83
+ @doc = stub
84
+ @klass.stubs(:create).with(:name => "Fred").returns(@doc)
85
+ @scope.create(:name => "Fred").should == @doc
86
+ end
87
+ end
88
+
89
+ describe "chaining another named scope" do
90
+ before do
91
+ @recent_params = {:order! => :created_at.desc}
92
+ @recent = Friendly::Scope.new(@klass, @recent_params)
93
+ @klass.stubs(:has_named_scope?).with(:recent).returns(true)
94
+ @klass.stubs(:recent).returns(@recent)
95
+ @scope_params = {:name => "Joe"}
96
+ @scope = Friendly::Scope.new(@klass, @scope_params)
97
+ end
98
+
99
+ it "responds to the other scope method" do
100
+ @scope.should be_respond_to(:recent)
101
+ end
102
+
103
+ it "creates a new scope that merges the two scopes together" do
104
+ merged_parameters = @scope_params.merge(@recent_params)
105
+ @scope.recent.parameters.should == merged_parameters
106
+ end
107
+
108
+ it "gives precedence to scopes on the right" do
109
+ @quagmire = Friendly::Scope.new(@klass, :name => "Quagmire")
110
+ (@scope + @quagmire).parameters[:name].should == "Quagmire"
111
+ end
112
+ end
113
+ end