friendly 0.3.5 → 0.4.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.
@@ -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