peleteiro-activecouch 0.2.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 (58) hide show
  1. data/MIT-LICENSE +20 -0
  2. data/README +28 -0
  3. data/Rakefile +52 -0
  4. data/VERSION +1 -0
  5. data/lib/active_couch.rb +12 -0
  6. data/lib/active_couch/base.rb +608 -0
  7. data/lib/active_couch/callbacks.rb +89 -0
  8. data/lib/active_couch/connection.rb +164 -0
  9. data/lib/active_couch/errors.rb +13 -0
  10. data/lib/active_couch/support.rb +3 -0
  11. data/lib/active_couch/support/exporter.rb +97 -0
  12. data/lib/active_couch/support/extensions.rb +86 -0
  13. data/lib/active_couch/support/inflections.rb +52 -0
  14. data/lib/active_couch/support/inflector.rb +279 -0
  15. data/lib/active_couch/views.rb +3 -0
  16. data/lib/active_couch/views/errors.rb +4 -0
  17. data/lib/active_couch/views/raw_view.rb +40 -0
  18. data/lib/active_couch/views/view.rb +85 -0
  19. data/lib/activecouch.rb +1 -0
  20. data/spec/base/after_delete_spec.rb +110 -0
  21. data/spec/base/after_save_spec.rb +102 -0
  22. data/spec/base/before_delete_spec.rb +109 -0
  23. data/spec/base/before_save_spec.rb +101 -0
  24. data/spec/base/count_all_spec.rb +29 -0
  25. data/spec/base/count_spec.rb +77 -0
  26. data/spec/base/create_spec.rb +28 -0
  27. data/spec/base/database_spec.rb +70 -0
  28. data/spec/base/delete_spec.rb +97 -0
  29. data/spec/base/find_from_url_spec.rb +55 -0
  30. data/spec/base/find_spec.rb +383 -0
  31. data/spec/base/from_json_spec.rb +54 -0
  32. data/spec/base/has_many_spec.rb +89 -0
  33. data/spec/base/has_spec.rb +88 -0
  34. data/spec/base/id_spec.rb +25 -0
  35. data/spec/base/initialize_spec.rb +91 -0
  36. data/spec/base/marshal_dump_spec.rb +64 -0
  37. data/spec/base/marshal_load_spec.rb +58 -0
  38. data/spec/base/module_spec.rb +18 -0
  39. data/spec/base/nested_class_spec.rb +19 -0
  40. data/spec/base/rev_spec.rb +20 -0
  41. data/spec/base/save_spec.rb +130 -0
  42. data/spec/base/site_spec.rb +62 -0
  43. data/spec/base/to_json_spec.rb +73 -0
  44. data/spec/connection/initialize_spec.rb +28 -0
  45. data/spec/exporter/all_databases_spec.rb +24 -0
  46. data/spec/exporter/create_database_spec.rb +47 -0
  47. data/spec/exporter/delete_database_spec.rb +45 -0
  48. data/spec/exporter/delete_spec.rb +36 -0
  49. data/spec/exporter/export_spec.rb +62 -0
  50. data/spec/exporter/export_with_raw_views_spec.rb +66 -0
  51. data/spec/spec_helper.rb +9 -0
  52. data/spec/views/define_spec.rb +34 -0
  53. data/spec/views/include_attributes_spec.rb +30 -0
  54. data/spec/views/raw_view_spec.rb +49 -0
  55. data/spec/views/to_json_spec.rb +58 -0
  56. data/spec/views/with_filter_spec.rb +13 -0
  57. data/spec/views/with_key_spec.rb +19 -0
  58. metadata +117 -0
@@ -0,0 +1,36 @@
1
+ require File.dirname(__FILE__) + '/../spec_helper.rb'
2
+
3
+ require 'net/http'
4
+ require 'uri'
5
+
6
+ describe ActiveCouch::Exporter, "#export twice (that actually connects to a CouchDB server)" do
7
+ before(:each) do
8
+ class ByFace < ActiveCouch::View
9
+ define :for_db => 'ac_test_4' do
10
+ with_key 'face'
11
+ end
12
+ end
13
+
14
+ ActiveCouch::Exporter.create_database('http://localhost:5984/', 'ac_test_4')
15
+ end
16
+
17
+ after(:each) do
18
+ ActiveCouch::Exporter.delete_database('http://localhost:5984/', 'ac_test_4')
19
+ end
20
+
21
+ it "should be able to create a permanent view when sent the export method, after the view has already been saved" do
22
+ ActiveCouch::Exporter.export('http://localhost:5984', ByFace).should == true
23
+ # This is the view document. To actually query this particular view, the URL to be used
24
+ # is http://#{host}:#{port}/ac_test_1/_view/by_face/by_face
25
+ # A little unwieldy I know, but the point of ActiveCouch is to abstract this unwieldiness
26
+ response = Net::HTTP.get_response URI.parse("http://localhost:5984/ac_test_4/_design/by_face")
27
+ response.code.should == '200'
28
+
29
+ ActiveCouch::Exporter.delete('http://localhost:5984', ByFace).should == true
30
+ # This is the view document. To actually query this particular view, the URL to be used
31
+ # is http://#{host}:#{port}/ac_test_1/_view/by_face/by_face
32
+ # A little unwieldy I know, but the point of ActiveCouch is to abstract this unwieldiness
33
+ response = Net::HTTP.get_response URI.parse("http://localhost:5984/ac_test_4/_design/by_face")
34
+ response.code.should == '404'
35
+ end
36
+ end
@@ -0,0 +1,62 @@
1
+ require File.dirname(__FILE__) + '/../spec_helper.rb'
2
+
3
+ require 'net/http'
4
+ require 'uri'
5
+
6
+ describe ActiveCouch::Exporter, "#export (that actually connects to a CouchDB server)" do
7
+ before(:each) do
8
+ class ByFace < ActiveCouch::View
9
+ define :for_db => 'ac_test_3' do
10
+ with_key 'face'
11
+ end
12
+ end
13
+
14
+ ActiveCouch::Exporter.create_database('http://localhost:5984/', 'ac_test_3')
15
+ end
16
+
17
+ after(:each) do
18
+ ActiveCouch::Exporter.delete_database('http://localhost:5984/', 'ac_test_3')
19
+ end
20
+
21
+ it "should be able to create a permanent view when sent the export method" do
22
+ ActiveCouch::Exporter.export('http://localhost:5984', ByFace).should == true
23
+ # This is the view document. To actually query this particular view, the URL to be used
24
+ # is http://#{host}:#{port}/ac_test_1/_view/by_face/by_face
25
+ # A little unwieldy I know, but the point of ActiveCouch is to abstract this unwieldiness
26
+ response = Net::HTTP.get_response URI.parse("http://localhost:5984/ac_test_3/_design/by_face")
27
+ response.code.should == '200'
28
+ end
29
+ end
30
+
31
+ describe ActiveCouch::Exporter, "#export twice (that actually connects to a CouchDB server)" do
32
+ before(:each) do
33
+ class ByFace < ActiveCouch::View
34
+ define :for_db => 'ac_test_3' do
35
+ with_key 'face'
36
+ end
37
+ end
38
+
39
+ ActiveCouch::Exporter.create_database('http://localhost:5984/', 'ac_test_3')
40
+ end
41
+
42
+ after(:each) do
43
+ ActiveCouch::Exporter.delete_database('http://localhost:5984/', 'ac_test_3')
44
+ end
45
+
46
+ it "should be able to create a permanent view when sent the export method, after the view has already been saved" do
47
+ ActiveCouch::Exporter.export('http://localhost:5984', ByFace).should == true
48
+ # This is the view document. To actually query this particular view, the URL to be used
49
+ # is http://#{host}:#{port}/ac_test_1/_view/by_face/by_face
50
+ # A little unwieldy I know, but the point of ActiveCouch is to abstract this unwieldiness
51
+ response = Net::HTTP.get_response URI.parse("http://localhost:5984/ac_test_3/_design/by_face")
52
+ response.code.should == '200'
53
+
54
+
55
+ ActiveCouch::Exporter.export('http://localhost:5984', ByFace).should == true
56
+ # This is the view document. To actually query this particular view, the URL to be used
57
+ # is http://#{host}:#{port}/ac_test_1/_view/by_face/by_face
58
+ # A little unwieldy I know, but the point of ActiveCouch is to abstract this unwieldiness
59
+ response = Net::HTTP.get_response URI.parse("http://localhost:5984/ac_test_3/_design/by_face")
60
+ response.code.should == '200'
61
+ end
62
+ end
@@ -0,0 +1,66 @@
1
+ require File.dirname(__FILE__) + '/../spec_helper.rb'
2
+
3
+ require 'net/http'
4
+ require 'uri'
5
+
6
+ describe ActiveCouch::Exporter, "#export a raw view (that actually connects to a CouchDB server)" do
7
+ before(:each) do
8
+ class WordCount < ActiveCouch::RawView
9
+ # Map Function
10
+ map 'function(doc) { if (doc.Type == "customer") { emit(null, 1); } }'
11
+ # Reduce Function
12
+ reduce 'function(key,combine){ return sum(combine); }'
13
+ end
14
+
15
+ ActiveCouch::Exporter.create_database('http://localhost:5984/', 'ac_test_3')
16
+ end
17
+
18
+ after(:each) do
19
+ ActiveCouch::Exporter.delete_database('http://localhost:5984/', 'ac_test_3')
20
+ Object.send(:remove_const, :WordCount)
21
+ end
22
+
23
+ it "should be able to create a permanent view when sent the export method" do
24
+ ActiveCouch::Exporter.export('http://localhost:5984', WordCount, :database => 'ac_test_3').should == true
25
+ # This is the view document. To actually query this particular view, the URL to be used
26
+ # is http://#{host}:#{port}/ac_test_3/_view/word_count/word_count
27
+ # A little unwieldy I know, but the point of ActiveCouch is to abstract this unwieldiness
28
+ response = Net::HTTP.get_response URI.parse("http://localhost:5984/ac_test_3/_design/word_count")
29
+ response.code.should == '200'
30
+ end
31
+ end
32
+
33
+ describe ActiveCouch::Exporter, "#export a raw view twice (that actually connects to a CouchDB server)" do
34
+ before(:each) do
35
+ class WordCount < ActiveCouch::RawView
36
+ # Map Function
37
+ map 'function(doc) { if (doc.Type == "customer") { emit(null, 1); } }'
38
+ # Reduce Function
39
+ reduce 'function(key,combine){ return sum(combine); }'
40
+ end
41
+
42
+ ActiveCouch::Exporter.create_database('http://localhost:5984/', 'ac_test_3')
43
+ end
44
+
45
+ after(:each) do
46
+ ActiveCouch::Exporter.delete_database('http://localhost:5984/', 'ac_test_3')
47
+ Object.send(:remove_const, :WordCount)
48
+ end
49
+
50
+ it "should be able to create a permanent view when sent the export method, after the view has already been saved" do
51
+ ActiveCouch::Exporter.export('http://localhost:5984', WordCount, :database => 'ac_test_3').should == true
52
+ # This is the view document. To actually query this particular view, the URL to be used
53
+ # is http://#{host}:#{port}/ac_test_3/_view/word_count/word_count
54
+ # A little unwieldy I know, but the point of ActiveCouch is to abstract this unwieldiness
55
+ response = Net::HTTP.get_response URI.parse("http://localhost:5984/ac_test_3/_design/word_count")
56
+ response.code.should == '200'
57
+
58
+
59
+ ActiveCouch::Exporter.export('http://localhost:5984', WordCount, :database => 'ac_test_3').should == true
60
+ # This is the view document. To actually query this particular view, the URL to be used
61
+ # is http://#{host}:#{port}/ac_test_3/_view/word_count/word_count
62
+ # A little unwieldy I know, but the point of ActiveCouch is to abstract this unwieldiness
63
+ response = Net::HTTP.get_response URI.parse("http://localhost:5984/ac_test_3/_design/word_count")
64
+ response.code.should == '200'
65
+ end
66
+ end
@@ -0,0 +1,9 @@
1
+ begin
2
+ require 'spec'
3
+ rescue LoadError
4
+ require 'rubygems'
5
+ gem 'rspec'
6
+ require 'spec'
7
+ end
8
+
9
+ require File.dirname(__FILE__) + "/../lib/active_couch"
@@ -0,0 +1,34 @@
1
+ require File.dirname(__FILE__) + '/../spec_helper.rb'
2
+
3
+ describe "ActiveCouch::View #define method" do
4
+ before(:each) do
5
+ class ByName < ActiveCouch::View
6
+ define :by_name, :for_db => 'people'
7
+ end
8
+
9
+ class ByFace < ActiveCouch::View
10
+ define :for_db => 'people'
11
+ end
12
+ end
13
+
14
+ after(:each) do
15
+ Object.send(:remove_const, :ByName)
16
+ Object.send(:remove_const, :ByFace)
17
+ end
18
+
19
+ it "should set @name and @database correctly if the first param is a String/Symbol" do
20
+ ByName.instance_variable_get("@name").should == 'by_name'
21
+ ByName.instance_variable_get("@database").should == 'people'
22
+ end
23
+
24
+ it "should set @name correctly if the first param passed is not a String/Symbol" do
25
+ ByFace.instance_variable_get("@name").should == 'by_face'
26
+ ByFace.instance_variable_get("@database").should == 'people'
27
+ end
28
+
29
+ it "should raise an exception if neither the view nor the database is given as parameters" do
30
+ lambda {
31
+ class Test < ActiveCouch::View; define; end
32
+ }.should raise_error(ArgumentError, 'Wrong arguments used to define the view')
33
+ end
34
+ end
@@ -0,0 +1,30 @@
1
+ require File.dirname(__FILE__) + '/../spec_helper.rb'
2
+
3
+ describe "ActiveCouch::View #include_attributes method" do
4
+ it "should set the attrs instance variable to an array, if passed a multi-element array" do
5
+ class ByLongitude < ActiveCouch::View
6
+ define :for_db => 'hotels' do
7
+ include_attributes :name, :rating, :latitude, :longitude, :address
8
+ end
9
+ end
10
+ ByLongitude.instance_variable_get("@attrs").should == [:name, :rating, :latitude, :longitude, :address]
11
+ end
12
+
13
+ it "should set the attrs instance variable correctly, if passed a single-element array" do
14
+ class ByLongitude < ActiveCouch::View
15
+ define :for_db => 'hotels' do
16
+ include_attributes :name
17
+ end
18
+ end
19
+ ByLongitude.instance_variable_get("@attrs").should == [:name]
20
+ end
21
+
22
+ it "should set the attrs instance variable to an empty array, if not passed an array" do
23
+ class ByLongitude < ActiveCouch::View
24
+ define :for_db => 'hotels' do
25
+ include_attributes {}
26
+ end
27
+ end
28
+ ByLongitude.instance_variable_get("@attrs").should == []
29
+ end
30
+ end
@@ -0,0 +1,49 @@
1
+ require File.dirname(__FILE__) + '/../spec_helper.rb'
2
+
3
+ describe "ActiveCouch::RawView #map method" do
4
+ before(:each) do
5
+ class ByName < ActiveCouch::RawView
6
+ map 'function(doc) { emit(null, doc); }'
7
+ end
8
+ end
9
+
10
+ after(:each) do
11
+ Object.send(:remove_const, :ByName)
12
+ end
13
+
14
+ it "should set @map_function with the value given" do
15
+ ByName.instance_variable_get("@map_function").should == 'function(doc) { emit(null, doc); }'
16
+ end
17
+ end
18
+
19
+ describe "ActiveCouch::RawView #reduce method" do
20
+ before(:each) do
21
+ class ByAge < ActiveCouch::RawView
22
+ reduce 'function(doc) { emit(null, doc); }'
23
+ end
24
+ end
25
+
26
+ after(:each) do
27
+ Object.send(:remove_const, :ByAge)
28
+ end
29
+
30
+ it "should set @reduce_function with the value given" do
31
+ ByAge.instance_variable_get("@reduce_function").should == 'function(doc) { emit(null, doc); }'
32
+ end
33
+ end
34
+
35
+ describe "ActiveCouch::RawView #database method" do
36
+ before(:each) do
37
+ class ByTemperature < ActiveCouch::RawView
38
+ for_database 'mclovin'
39
+ end
40
+ end
41
+
42
+ after(:each) do
43
+ Object.send(:remove_const, :ByTemperature)
44
+ end
45
+
46
+ it "should set @database with the value given" do
47
+ ByTemperature.instance_variable_get("@database").should == 'mclovin'
48
+ end
49
+ end
@@ -0,0 +1,58 @@
1
+ require File.dirname(__FILE__) + '/../spec_helper.rb'
2
+
3
+ describe "ActiveCouch::View #to_json method" do
4
+ before(:each) do
5
+ class ByName < ActiveCouch::View
6
+ define :by_name, :for_db => 'people' do
7
+ with_key 'name'
8
+ end
9
+ end
10
+ end
11
+
12
+ after(:each) do
13
+ Object.send(:remove_const, :ByName)
14
+ end
15
+
16
+ it "should generate the correct javascript to be used in the view" do
17
+ (ByName.to_json =~ /emit\(doc\.name, doc\);/).should_not == nil
18
+ end
19
+ end
20
+
21
+ describe "ActiveCouch::View #to_json method while calling the with_key and with_filter methods" do
22
+ before(:each) do
23
+ class ByLatitude < ActiveCouch::View
24
+ define :for_db => 'hotels' do
25
+ with_key 'latitude'
26
+ with_filter 'doc.name == "Hilton"'
27
+ end
28
+ end
29
+ end
30
+
31
+ after(:each) do
32
+ Object.send(:remove_const, :ByLatitude)
33
+ end
34
+
35
+ it "should generate the correct javascript to be used in the view" do
36
+ (ByLatitude.to_json =~ /emit\(doc\.latitude, doc\);/).should_not == nil
37
+ (ByLatitude.to_json =~ /if\(doc\.name == \\"Hilton\\"\)/).should_not == nil
38
+ end
39
+ end
40
+
41
+ describe "A subclass of ActiveCouch::View while calling with_key and include_attributes method" do
42
+ before(:each) do
43
+ class ByLongitude < ActiveCouch::View
44
+ define :for_db => 'hotels' do
45
+ with_key 'latitude'
46
+ include_attributes :name, :rating, :latitude, :longitude, :address
47
+ end
48
+ end
49
+ end
50
+
51
+ after(:each) do
52
+ Object.send(:remove_const, :ByLongitude)
53
+ end
54
+
55
+ it "should generate the correct javascript which will be used in the permanent view" do
56
+ (ByLongitude.to_json =~ /emit\(doc\.latitude, \{name: doc\.name , rating: doc\.rating , latitude: doc\.latitude , longitude: doc\.longitude , address: doc\.address\}\);/).should_not == nil
57
+ end
58
+ end
@@ -0,0 +1,13 @@
1
+ require File.dirname(__FILE__) + '/../spec_helper.rb'
2
+
3
+ describe "ActiveCouch::View #with_filter method" do
4
+ it "should set @filter correctly" do
5
+ class ByLatitude < ActiveCouch::View
6
+ define :for_db => 'hotels' do
7
+ with_filter 'doc.name == "Hilton"'
8
+ end
9
+ end
10
+ # Assertion
11
+ ByLatitude.instance_variable_get("@filter").should == 'doc.name == "Hilton"'
12
+ end
13
+ end
@@ -0,0 +1,19 @@
1
+ require File.dirname(__FILE__) + '/../spec_helper.rb'
2
+
3
+ describe "ActiveCouch::View #with_key method" do
4
+ before(:each) do
5
+ class ByName < ActiveCouch::View
6
+ define :by_name, :for_db => 'people' do
7
+ with_key 'name'
8
+ end
9
+ end
10
+ end
11
+
12
+ after(:each) do
13
+ Object.send(:remove_const, :ByName)
14
+ end
15
+
16
+ it "should set @key correctly" do
17
+ ByName.instance_variable_get("@key").should == 'name'
18
+ end
19
+ end
metadata ADDED
@@ -0,0 +1,117 @@
1
+ --- !ruby/object:Gem::Specification
2
+ name: peleteiro-activecouch
3
+ version: !ruby/object:Gem::Version
4
+ version: 0.2.1
5
+ platform: ruby
6
+ authors:
7
+ - Arun Thampi & Cheah Chu Yeow
8
+ autorequire:
9
+ bindir: bin
10
+ cert_chain: []
11
+
12
+ date: 2009-06-05 00:00:00 -07:00
13
+ default_executable:
14
+ dependencies:
15
+ - !ruby/object:Gem::Dependency
16
+ name: json
17
+ type: :runtime
18
+ version_requirement:
19
+ version_requirements: !ruby/object:Gem::Requirement
20
+ requirements:
21
+ - - ">="
22
+ - !ruby/object:Gem::Version
23
+ version: 1.1.2
24
+ version:
25
+ description: ActiveCouch wants to be a simple, convenient, idiomatic Object Relational Mapper for the hot new kid on the block - CouchDB. CouchDB (simplistically speaking) is a document store, which essentially means that objects can be stored in a schema-less environment.
26
+ email: arun.thampi@gmail.com, chuyeow@gmail.com
27
+ executables: []
28
+
29
+ extensions: []
30
+
31
+ extra_rdoc_files:
32
+ - README
33
+ files:
34
+ - MIT-LICENSE
35
+ - README
36
+ - Rakefile
37
+ - VERSION
38
+ - lib/active_couch.rb
39
+ - lib/active_couch/base.rb
40
+ - lib/active_couch/callbacks.rb
41
+ - lib/active_couch/connection.rb
42
+ - lib/active_couch/errors.rb
43
+ - lib/active_couch/support.rb
44
+ - lib/active_couch/support/exporter.rb
45
+ - lib/active_couch/support/extensions.rb
46
+ - lib/active_couch/support/inflections.rb
47
+ - lib/active_couch/support/inflector.rb
48
+ - lib/active_couch/views.rb
49
+ - lib/active_couch/views/errors.rb
50
+ - lib/active_couch/views/raw_view.rb
51
+ - lib/active_couch/views/view.rb
52
+ - lib/activecouch.rb
53
+ has_rdoc: false
54
+ homepage: http://github.com/arunthampi/activecouch
55
+ post_install_message:
56
+ rdoc_options:
57
+ - --charset=UTF-8
58
+ require_paths:
59
+ - lib
60
+ required_ruby_version: !ruby/object:Gem::Requirement
61
+ requirements:
62
+ - - ">="
63
+ - !ruby/object:Gem::Version
64
+ version: "0"
65
+ version:
66
+ required_rubygems_version: !ruby/object:Gem::Requirement
67
+ requirements:
68
+ - - ">="
69
+ - !ruby/object:Gem::Version
70
+ version: "0"
71
+ version:
72
+ requirements: []
73
+
74
+ rubyforge_project: activecouch
75
+ rubygems_version: 1.2.0
76
+ signing_key:
77
+ specification_version: 3
78
+ summary: Ruby-based wrapper for CouchDB
79
+ test_files:
80
+ - spec/base/after_delete_spec.rb
81
+ - spec/base/after_save_spec.rb
82
+ - spec/base/before_delete_spec.rb
83
+ - spec/base/before_save_spec.rb
84
+ - spec/base/count_all_spec.rb
85
+ - spec/base/count_spec.rb
86
+ - spec/base/create_spec.rb
87
+ - spec/base/database_spec.rb
88
+ - spec/base/delete_spec.rb
89
+ - spec/base/find_from_url_spec.rb
90
+ - spec/base/find_spec.rb
91
+ - spec/base/from_json_spec.rb
92
+ - spec/base/has_many_spec.rb
93
+ - spec/base/has_spec.rb
94
+ - spec/base/id_spec.rb
95
+ - spec/base/initialize_spec.rb
96
+ - spec/base/marshal_dump_spec.rb
97
+ - spec/base/marshal_load_spec.rb
98
+ - spec/base/module_spec.rb
99
+ - spec/base/nested_class_spec.rb
100
+ - spec/base/rev_spec.rb
101
+ - spec/base/save_spec.rb
102
+ - spec/base/site_spec.rb
103
+ - spec/base/to_json_spec.rb
104
+ - spec/connection/initialize_spec.rb
105
+ - spec/exporter/all_databases_spec.rb
106
+ - spec/exporter/create_database_spec.rb
107
+ - spec/exporter/delete_database_spec.rb
108
+ - spec/exporter/delete_spec.rb
109
+ - spec/exporter/export_spec.rb
110
+ - spec/exporter/export_with_raw_views_spec.rb
111
+ - spec/spec_helper.rb
112
+ - spec/views/define_spec.rb
113
+ - spec/views/include_attributes_spec.rb
114
+ - spec/views/raw_view_spec.rb
115
+ - spec/views/to_json_spec.rb
116
+ - spec/views/with_filter_spec.rb
117
+ - spec/views/with_key_spec.rb