arunthampi-friendly 0.5.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.
Files changed (137) hide show
  1. data/.document +2 -0
  2. data/.gitignore +26 -0
  3. data/APACHE-LICENSE +202 -0
  4. data/CHANGELOG.md +28 -0
  5. data/CONTRIBUTORS.md +7 -0
  6. data/LICENSE +20 -0
  7. data/README.md +288 -0
  8. data/Rakefile +68 -0
  9. data/TODO.md +5 -0
  10. data/VERSION +1 -0
  11. data/arunthampi-friendly.gemspec +241 -0
  12. data/examples/friendly.yml +7 -0
  13. data/friendly.gemspec +240 -0
  14. data/lib/friendly.rb +53 -0
  15. data/lib/friendly/associations.rb +7 -0
  16. data/lib/friendly/associations/association.rb +34 -0
  17. data/lib/friendly/associations/set.rb +37 -0
  18. data/lib/friendly/attribute.rb +98 -0
  19. data/lib/friendly/boolean.rb +10 -0
  20. data/lib/friendly/cache.rb +24 -0
  21. data/lib/friendly/cache/by_id.rb +33 -0
  22. data/lib/friendly/data_store.rb +73 -0
  23. data/lib/friendly/document.rb +70 -0
  24. data/lib/friendly/document/associations.rb +50 -0
  25. data/lib/friendly/document/attributes.rb +114 -0
  26. data/lib/friendly/document/convenience.rb +41 -0
  27. data/lib/friendly/document/mixin.rb +15 -0
  28. data/lib/friendly/document/scoping.rb +66 -0
  29. data/lib/friendly/document/storage.rb +63 -0
  30. data/lib/friendly/document_table.rb +56 -0
  31. data/lib/friendly/index.rb +73 -0
  32. data/lib/friendly/indexer.rb +50 -0
  33. data/lib/friendly/memcached.rb +48 -0
  34. data/lib/friendly/newrelic.rb +6 -0
  35. data/lib/friendly/query.rb +42 -0
  36. data/lib/friendly/scope.rb +100 -0
  37. data/lib/friendly/scope_proxy.rb +43 -0
  38. data/lib/friendly/sequel_monkey_patches.rb +34 -0
  39. data/lib/friendly/storage.rb +31 -0
  40. data/lib/friendly/storage_factory.rb +24 -0
  41. data/lib/friendly/storage_proxy.rb +111 -0
  42. data/lib/friendly/table.rb +15 -0
  43. data/lib/friendly/table_creator.rb +50 -0
  44. data/lib/friendly/time.rb +14 -0
  45. data/lib/friendly/translator.rb +33 -0
  46. data/lib/friendly/uuid.rb +148 -0
  47. data/lib/tasks/friendly.rake +7 -0
  48. data/rails/init.rb +3 -0
  49. data/spec/config.yml.example +7 -0
  50. data/spec/fakes/data_store_fake.rb +29 -0
  51. data/spec/fakes/database_fake.rb +12 -0
  52. data/spec/fakes/dataset_fake.rb +28 -0
  53. data/spec/fakes/document.rb +18 -0
  54. data/spec/fakes/serializer_fake.rb +12 -0
  55. data/spec/fakes/time_fake.rb +12 -0
  56. data/spec/integration/ad_hoc_scopes_spec.rb +42 -0
  57. data/spec/integration/basic_object_lifecycle_spec.rb +114 -0
  58. data/spec/integration/batch_insertion_spec.rb +29 -0
  59. data/spec/integration/convenience_api_spec.rb +25 -0
  60. data/spec/integration/count_spec.rb +12 -0
  61. data/spec/integration/default_value_spec.rb +30 -0
  62. data/spec/integration/dirty_tracking_spec.rb +43 -0
  63. data/spec/integration/find_via_cache_spec.rb +101 -0
  64. data/spec/integration/finder_spec.rb +71 -0
  65. data/spec/integration/has_many_spec.rb +18 -0
  66. data/spec/integration/index_spec.rb +57 -0
  67. data/spec/integration/named_scope_spec.rb +34 -0
  68. data/spec/integration/offline_indexing_spec.rb +53 -0
  69. data/spec/integration/pagination_spec.rb +63 -0
  70. data/spec/integration/scope_chaining_spec.rb +22 -0
  71. data/spec/integration/table_creator_spec.rb +69 -0
  72. data/spec/integration/write_through_cache_spec.rb +53 -0
  73. data/spec/spec.opts +1 -0
  74. data/spec/spec_helper.rb +105 -0
  75. data/spec/unit/associations/association_spec.rb +57 -0
  76. data/spec/unit/associations/set_spec.rb +43 -0
  77. data/spec/unit/attribute_spec.rb +125 -0
  78. data/spec/unit/cache_by_id_spec.rb +102 -0
  79. data/spec/unit/cache_spec.rb +21 -0
  80. data/spec/unit/data_store_spec.rb +201 -0
  81. data/spec/unit/document/attributes_spec.rb +130 -0
  82. data/spec/unit/document_spec.rb +318 -0
  83. data/spec/unit/document_table_spec.rb +126 -0
  84. data/spec/unit/friendly_spec.rb +25 -0
  85. data/spec/unit/index_spec.rb +196 -0
  86. data/spec/unit/memcached_spec.rb +114 -0
  87. data/spec/unit/query_spec.rb +104 -0
  88. data/spec/unit/scope_proxy_spec.rb +44 -0
  89. data/spec/unit/scope_spec.rb +113 -0
  90. data/spec/unit/storage_factory_spec.rb +59 -0
  91. data/spec/unit/storage_proxy_spec.rb +244 -0
  92. data/spec/unit/translator_spec.rb +91 -0
  93. data/website/index.html +210 -0
  94. data/website/scripts/clipboard.swf +0 -0
  95. data/website/scripts/shBrushAS3.js +61 -0
  96. data/website/scripts/shBrushBash.js +66 -0
  97. data/website/scripts/shBrushCSharp.js +67 -0
  98. data/website/scripts/shBrushColdFusion.js +102 -0
  99. data/website/scripts/shBrushCpp.js +99 -0
  100. data/website/scripts/shBrushCss.js +93 -0
  101. data/website/scripts/shBrushDelphi.js +57 -0
  102. data/website/scripts/shBrushDiff.js +43 -0
  103. data/website/scripts/shBrushErlang.js +54 -0
  104. data/website/scripts/shBrushGroovy.js +69 -0
  105. data/website/scripts/shBrushJScript.js +52 -0
  106. data/website/scripts/shBrushJava.js +59 -0
  107. data/website/scripts/shBrushJavaFX.js +60 -0
  108. data/website/scripts/shBrushPerl.js +74 -0
  109. data/website/scripts/shBrushPhp.js +91 -0
  110. data/website/scripts/shBrushPlain.js +35 -0
  111. data/website/scripts/shBrushPowerShell.js +76 -0
  112. data/website/scripts/shBrushPython.js +66 -0
  113. data/website/scripts/shBrushRuby.js +57 -0
  114. data/website/scripts/shBrushScala.js +53 -0
  115. data/website/scripts/shBrushSql.js +68 -0
  116. data/website/scripts/shBrushVb.js +58 -0
  117. data/website/scripts/shBrushXml.js +71 -0
  118. data/website/scripts/shCore.js +30 -0
  119. data/website/scripts/shLegacy.js +30 -0
  120. data/website/styles/friendly.css +103 -0
  121. data/website/styles/help.png +0 -0
  122. data/website/styles/ie.css +35 -0
  123. data/website/styles/magnifier.png +0 -0
  124. data/website/styles/page_white_code.png +0 -0
  125. data/website/styles/page_white_copy.png +0 -0
  126. data/website/styles/print.css +29 -0
  127. data/website/styles/printer.png +0 -0
  128. data/website/styles/screen.css +257 -0
  129. data/website/styles/shCore.css +330 -0
  130. data/website/styles/shThemeDefault.css +173 -0
  131. data/website/styles/shThemeDjango.css +176 -0
  132. data/website/styles/shThemeEclipse.css +190 -0
  133. data/website/styles/shThemeEmacs.css +175 -0
  134. data/website/styles/shThemeFadeToGrey.css +177 -0
  135. data/website/styles/shThemeMidnight.css +175 -0
  136. data/website/styles/shThemeRDark.css +175 -0
  137. metadata +311 -0
@@ -0,0 +1,57 @@
1
+ require File.expand_path("../../spec_helper", __FILE__)
2
+ require 'active_support/duration'
3
+ require 'active_support/core_ext/integer'
4
+ require 'active_support/core_ext/time'
5
+ require 'active_support/core_ext/object'
6
+ require 'active_support/core_ext/numeric'
7
+
8
+ describe "Finding one object with an index" do
9
+ before do
10
+ @created_user = User.new(:name => "Cleveland")
11
+ @created_user.save
12
+ @found_user = User.first(:name => "Cleveland")
13
+ end
14
+
15
+ it "finds the object" do
16
+ @found_user.should == @created_user
17
+ end
18
+ end
19
+
20
+ describe "Finding one object that doesn't exist in the index" do
21
+ it "returns nil" do
22
+ User.first(:name => "asdf").should be_nil
23
+ end
24
+ end
25
+
26
+ describe "Finding object on an index that doesn't exist" do
27
+ it "raises Friendly::MissingIndex" do
28
+ lambda {
29
+ User.first(:age => 3)
30
+ }.should raise_error(Friendly::MissingIndex)
31
+ end
32
+ end
33
+
34
+ describe "Finding ordered objects" do
35
+ before do
36
+ @three = User.new(:name => "Brian", :created_at => 4.hours.ago)
37
+ @two = User.new(:name => "Brian", :created_at => 2.hours.ago)
38
+ @one = User.new(:name => "Brian", :created_at => 5.minutes.ago)
39
+ [@one, @two, @three].each { |i| i.save }
40
+ end
41
+
42
+ after do
43
+ [@one, @two, @three].each { |i| i.destroy }
44
+ end
45
+
46
+ it "can return the objects in desc order" do
47
+ found_users = User.all(:name => "Brian", :order! => :created_at.desc)
48
+ found_users.should == [@one, @two, @three]
49
+ end
50
+
51
+ it "can return the objects in asc order" do
52
+ found_users = User.all(:name => "Brian", :order! => :created_at.asc)
53
+ found_users.length.should == 3
54
+ found_users.should == [@three, @two, @one]
55
+ end
56
+ end
57
+
@@ -0,0 +1,34 @@
1
+ require File.expand_path("../../spec_helper", __FILE__)
2
+
3
+ describe "named_scope" do
4
+ describe "calling a single named_scope" do
5
+ before do
6
+ User.all(:name => "Quagmire").each { |q| q.destroy }
7
+ 5.times { User.create(:name => "Quagmire") }
8
+ end
9
+
10
+ describe "all" do
11
+ it "returns all objects matching the conditions" do
12
+ User.named_quagmire.all.should == User.all(:name => "Quagmire")
13
+ end
14
+
15
+ it "accepts extra conditions" do
16
+ User.create(:name => "Fred")
17
+ found = User.named_quagmire.all(:name => "Fred")
18
+ found.should == User.all(:name => "Fred")
19
+ end
20
+ end
21
+
22
+ describe "first" do
23
+ it "returns the first object matching the conditions" do
24
+ User.named_quagmire.first.should == User.first(:name => "Quagmire")
25
+ end
26
+
27
+ it "accepts extra conditions" do
28
+ User.create(:name => "Fred")
29
+ found = User.named_quagmire.first(:name => "Fred")
30
+ found.should == User.first(:name => "Fred")
31
+ end
32
+ end
33
+ end
34
+ end
@@ -0,0 +1,53 @@
1
+ require File.expand_path("../../spec_helper", __FILE__)
2
+
3
+ describe "Building an index offline" do
4
+ before do
5
+ $db.drop_table :awesome_things if $db.table_exists?(:awesome_things)
6
+
7
+ if $db.table_exists?(:index_awesome_things_on_name)
8
+ $db.drop_table :index_awesome_things_on_name
9
+ end
10
+
11
+ @klass = Class.new do
12
+ def self.name; "AwesomeThing"; end
13
+ def self.table_name; "awesome_things"; end
14
+
15
+ include Friendly::Document
16
+
17
+ attribute :name, String
18
+ end
19
+ @klass.create_tables!
20
+
21
+ @jameses = [@klass.create(:name => "James"), @klass.create(:name => "James")]
22
+
23
+ @klass.indexes :name
24
+ @klass.create_tables!
25
+ end
26
+
27
+ describe "" do
28
+ before do
29
+ Friendly::Indexer.populate(@klass, :name)
30
+ end
31
+
32
+ it "builds the missing index rows for all the rows in the doc table" do
33
+ @klass.all(:name => "James").should == @jameses
34
+ end
35
+
36
+ it "ignores records that are already in the index" do
37
+ lambda {
38
+ Friendly::Indexer.populate(@klass, :name)
39
+ }.should_not raise_error
40
+ end
41
+ end
42
+
43
+ describe "with more than `Indexer.objects_per_iteration` objects" do
44
+ before do
45
+ Friendly::Indexer.objects_per_iteration = 1
46
+ Friendly::Indexer.populate(@klass, :name)
47
+ end
48
+
49
+ it "builds the missing index rows for all the rows in the doc table" do
50
+ @klass.all(:name => "James").should == @jameses
51
+ end
52
+ end
53
+ end
@@ -0,0 +1,63 @@
1
+ require File.expand_path("../../spec_helper", __FILE__)
2
+ require 'active_support/core_ext'
3
+
4
+ describe "Paginating" do
5
+ before do
6
+ User.all(:name => "Fred").each { |u| u.destroy }
7
+ @users = (0...15).map do |i|
8
+ User.create(:name => "Fred",
9
+ :created_at => i.hours.ago)
10
+ end
11
+ end
12
+
13
+ describe "fetching the nil page" do
14
+ before do
15
+ @found = User.paginate(:name => "Fred",
16
+ :order! => :created_at.desc,
17
+ :page! => nil,
18
+ :per_page! => 5)
19
+ end
20
+
21
+ it "returns the first :per_page results" do
22
+ @found.should == @users.slice(0,5)
23
+ end
24
+
25
+ it "returns an instance of WillPaginate::Collection" do
26
+ @found.should be_instance_of(WillPaginate::Collection)
27
+ end
28
+ end
29
+
30
+ describe "fetching a page by number" do
31
+ before do
32
+ @found = User.paginate(:name => "Fred",
33
+ :order! => :created_at.desc,
34
+ :page! => 2,
35
+ :per_page! => 5)
36
+ end
37
+
38
+ it "returns the :per_page results starting at offset :per_page * page" do
39
+ @found.should == @users.slice(5,5)
40
+ end
41
+
42
+ it "returns an instance of WillPaginate::Collection" do
43
+ @found.should be_instance_of(WillPaginate::Collection)
44
+ end
45
+ end
46
+
47
+ describe "when :page is a string" do
48
+ before do
49
+ @found = User.paginate(:name => "Fred",
50
+ :order! => :created_at.desc,
51
+ :page! => "2",
52
+ :per_page! => 5)
53
+ end
54
+
55
+ it "returns the :per_page results starting at offset :per_page * page" do
56
+ @found.should == @users.slice(5,5)
57
+ end
58
+
59
+ it "returns an instance of WillPaginate::Collection" do
60
+ @found.should be_instance_of(WillPaginate::Collection)
61
+ end
62
+ end
63
+ end
@@ -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
@@ -0,0 +1,69 @@
1
+ require File.expand_path("../../spec_helper", __FILE__)
2
+
3
+ describe "Creating the tables for a model" do
4
+ before do
5
+ @klass = Class.new do
6
+ include Friendly::Document
7
+
8
+ def self.table_name; "stuffs"; end
9
+
10
+ attribute :name, String
11
+ attribute :user_id, Friendly::UUID
12
+
13
+ indexes [:name, :created_at]
14
+ indexes :user_id
15
+ end
16
+
17
+ @klass.create_tables!
18
+ @schema = Friendly.db.schema(:stuffs)
19
+ @unique_id_index = Friendly.db.indexes(:stuffs)
20
+ @table = Hash[*@schema.map { |s| [s.first, s.last] }.flatten]
21
+ @index_schema = Friendly.db.schema(:index_stuffs_on_name_and_created_at)
22
+ @index_table = Hash[*@index_schema.map { |s| [s.first, s.last] }.flatten]
23
+ @id_idx_schema = Friendly.db.schema(:index_stuffs_on_user_id)
24
+ @id_index_table = Hash[*@id_idx_schema.map { |s| [s.first, s.last] }.flatten]
25
+ end
26
+
27
+ after do
28
+ Friendly.db.drop_table(:stuffs)
29
+ Friendly.db.drop_table(:index_stuffs_on_name_and_created_at)
30
+ end
31
+
32
+ it "creates a table for the document" do
33
+ @table.keys.length.should == 5
34
+ @table[:added_id][:db_type].should == "int(11)"
35
+ @table[:added_id][:primary_key].should be_true
36
+ @table[:id][:db_type].should == "binary(16)"
37
+ @table[:attributes][:db_type].should == "text"
38
+ @table[:created_at][:db_type].should == "datetime"
39
+ @table[:updated_at][:db_type].should == "datetime"
40
+
41
+ @unique_id_index.keys.length.should == 1
42
+ @unique_id_index[:id][:columns].should == [:id]
43
+ @unique_id_index[:id][:unique].should be_true
44
+ end
45
+
46
+ it "creates a table for each index" do
47
+ @index_table.keys.length.should == 3
48
+ @index_table[:name][:db_type].should == "varchar(255)"
49
+ @index_table[:name][:primary_key].should be_true
50
+ @index_table[:created_at][:db_type].should == "datetime"
51
+ @index_table[:created_at][:primary_key].should be_true
52
+ @index_table[:id][:db_type].should == "binary(16)"
53
+ @index_table[:id][:primary_key].should be_true
54
+ end
55
+
56
+ it "knows how to create an index for a field of a custom type" do
57
+ @id_index_table.keys.length.should == 2
58
+ @id_index_table[:user_id][:db_type].should == "binary(16)"
59
+ @id_index_table[:user_id][:primary_key].should be_true
60
+ @id_index_table[:id][:db_type].should == "binary(16)"
61
+ @id_index_table[:id][:primary_key].should be_true
62
+ end
63
+
64
+ it "doesn't raise if the tables already exist" do
65
+ lambda {
66
+ @klass.create_tables!
67
+ }.should_not raise_error
68
+ end
69
+ end
@@ -0,0 +1,53 @@
1
+ require File.expand_path("../../spec_helper", __FILE__)
2
+
3
+ describe "Writing through to cache on create" do
4
+ before do
5
+ @address = Address.create :street => "Spooner"
6
+ end
7
+
8
+ it "writes through to memcache using the model street and guid as cache key" do
9
+ $cache.get("Address/0/#{@address.id.to_guid}").should == @address
10
+ end
11
+ end
12
+
13
+ describe "Writing with a version number" do
14
+ before do
15
+ Address.storage_proxy.caches.clear
16
+ Address.caches_by :id, :version => 1
17
+ @address = Address.create :street => "Spooner"
18
+ end
19
+
20
+ after do
21
+ Address.storage_proxy.caches.clear
22
+ Address.caches_by :id
23
+ end
24
+
25
+ it "adds the version number to the cache key" do
26
+ $cache.get("Address/1/#{@address.id.to_guid}").should == @address
27
+ end
28
+ end
29
+
30
+ describe "Writing through to cache on update" do
31
+ before do
32
+ @address = Address.create :street => "Spooner"
33
+ @address.street = "Joe"
34
+ @address.save
35
+ end
36
+
37
+ it "writes through to memcache using the model street and guid as cache key" do
38
+ $cache.get("Address/0/#{@address.id.to_guid}").street.should == @address.street
39
+ end
40
+ end
41
+
42
+ describe "Writing through to cache on destroy" do
43
+ before do
44
+ @address = Address.create :street => "Spooner"
45
+ @address.destroy
46
+ end
47
+
48
+ it "removes the object from cache" do
49
+ lambda {
50
+ $cache.get("Address/0/#{@address.id.to_guid}")
51
+ }.should raise_error(Memcached::NotFound)
52
+ end
53
+ end
@@ -0,0 +1 @@
1
+ --color --backtrace
@@ -0,0 +1,105 @@
1
+ $LOAD_PATH.unshift(File.dirname(__FILE__))
2
+ $LOAD_PATH.unshift(File.join(File.dirname(__FILE__), '..', 'lib'))
3
+ Dir[File.expand_path(File.dirname(__FILE__)) + "/fakes/*.rb"].each do |f|
4
+ require f
5
+ end
6
+ require 'rubygems'
7
+ require 'spec'
8
+ require 'spec/autorun'
9
+ require 'sequel'
10
+ require 'json/pure'
11
+ gem 'jferris-mocha'
12
+ require 'mocha'
13
+ require 'memcached'
14
+ require 'friendly'
15
+
16
+ config = YAML.load(File.read(File.dirname(__FILE__) + "/config.yml"))['test']
17
+ Friendly.configure config
18
+ $db = Friendly.db
19
+ Sequel::MySQL.default_engine = "InnoDB"
20
+
21
+ $db.drop_table :users if $db.table_exists?("users")
22
+ $db.drop_table :index_users_on_name if $db.table_exists?("index_users_on_name")
23
+ if $db.table_exists?("index_users_on_name_and_created_at")
24
+ $db.drop_table :index_users_on_name_and_created_at
25
+ end
26
+
27
+ datastore = Friendly::DataStore.new($db)
28
+ Friendly.datastore = datastore
29
+
30
+ $cache = Memcached.new
31
+ Friendly.cache = Friendly::Memcached.new($cache)
32
+
33
+ class User
34
+ include Friendly::Document
35
+
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
41
+
42
+ indexes :happy
43
+ indexes :friend
44
+ indexes :name
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
55
+ end
56
+
57
+ User.create_tables!
58
+
59
+ class Address
60
+ include Friendly::Document
61
+
62
+ attribute :user_id, Friendly::UUID
63
+ attribute :street, String
64
+
65
+ indexes :user_id
66
+ indexes :street
67
+ indexes :created_at
68
+
69
+ caches_by :id
70
+ end
71
+
72
+ Address.create_tables!
73
+
74
+ module Mocha
75
+ module API
76
+ def setup_mocks_for_rspec
77
+ mocha_setup
78
+ end
79
+ def verify_mocks_for_rspec
80
+ mocha_verify
81
+ end
82
+ def teardown_mocks_for_rspec
83
+ mocha_teardown
84
+ end
85
+ end
86
+ end
87
+
88
+ module Factory
89
+ def row(opts = {})
90
+ { :id => 1, :created_at => Time.new, :updated_at => Time.new }.merge(opts)
91
+ end
92
+
93
+ def query(conditions)
94
+ stub(:order => conditions.delete(:order!),
95
+ :limit => conditions.delete(:limit!),
96
+ :preserve_order? => conditions.delete(:preserve_order!),
97
+ :conditions => conditions,
98
+ :offset => conditions.delete(:offset!))
99
+ end
100
+ end
101
+
102
+ Spec::Runner.configure do |config|
103
+ config.mock_with Mocha::API
104
+ config.include Factory
105
+ end