her 0.5.3 → 0.5.4

Sign up to get free protection for your applications and to get access to all the features.
Files changed (72) hide show
  1. checksums.yaml +8 -8
  2. data/.gitignore +2 -2
  3. data/.rspec +1 -2
  4. data/.travis.yml +2 -2
  5. data/README.md +10 -16
  6. data/UPGRADE.md +4 -0
  7. data/examples/grape-and-her/.env.default +3 -0
  8. data/examples/grape-and-her/Procfile +2 -0
  9. data/examples/grape-and-her/README.md +27 -0
  10. data/examples/grape-and-her/api/Gemfile +11 -0
  11. data/examples/grape-and-her/api/Rakefile +14 -0
  12. data/examples/grape-and-her/api/app/api.rb +49 -0
  13. data/examples/grape-and-her/api/app/models/organization.rb +7 -0
  14. data/examples/grape-and-her/api/app/models/user.rb +9 -0
  15. data/examples/grape-and-her/api/app/views/organizations/_base.rabl +2 -0
  16. data/examples/grape-and-her/api/app/views/organizations/index.rabl +3 -0
  17. data/examples/grape-and-her/api/app/views/organizations/show.rabl +3 -0
  18. data/examples/grape-and-her/api/app/views/users/_base.rabl +8 -0
  19. data/examples/grape-and-her/api/app/views/users/index.rabl +3 -0
  20. data/examples/grape-and-her/api/app/views/users/show.rabl +3 -0
  21. data/examples/grape-and-her/api/config.ru +5 -0
  22. data/examples/grape-and-her/api/config/boot.rb +17 -0
  23. data/examples/grape-and-her/api/config/unicorn.rb +7 -0
  24. data/examples/grape-and-her/api/db/migrations/001_create_users.rb +11 -0
  25. data/examples/grape-and-her/api/db/migrations/002_create_organizations.rb +8 -0
  26. data/examples/grape-and-her/consumer/Gemfile +23 -0
  27. data/examples/grape-and-her/consumer/app/assets/stylesheets/application.scss +190 -0
  28. data/examples/grape-and-her/consumer/app/assets/stylesheets/reset.scss +53 -0
  29. data/examples/grape-and-her/consumer/app/consumer.rb +74 -0
  30. data/examples/grape-and-her/consumer/app/models/organization.rb +13 -0
  31. data/examples/grape-and-her/consumer/app/models/user.rb +13 -0
  32. data/examples/grape-and-her/consumer/app/views/index.haml +9 -0
  33. data/examples/grape-and-her/consumer/app/views/layout.haml +20 -0
  34. data/examples/grape-and-her/consumer/app/views/organizations/index.haml +25 -0
  35. data/examples/grape-and-her/consumer/app/views/organizations/show.haml +11 -0
  36. data/examples/grape-and-her/consumer/app/views/users/index.haml +33 -0
  37. data/examples/grape-and-her/consumer/app/views/users/show.haml +9 -0
  38. data/examples/grape-and-her/consumer/config.ru +20 -0
  39. data/examples/grape-and-her/consumer/config/boot.rb +30 -0
  40. data/examples/grape-and-her/consumer/config/unicorn.rb +7 -0
  41. data/examples/grape-and-her/consumer/lib/response_logger.rb +18 -0
  42. data/her.gemspec +2 -2
  43. data/lib/her/model.rb +22 -26
  44. data/lib/her/model/associations.rb +19 -19
  45. data/lib/her/model/attributes.rb +173 -0
  46. data/lib/her/model/base.rb +17 -0
  47. data/lib/her/model/http.rb +58 -242
  48. data/lib/her/model/introspection.rb +7 -8
  49. data/lib/her/model/nested_attributes.rb +3 -3
  50. data/lib/her/model/orm.rb +15 -205
  51. data/lib/her/model/parse.rb +86 -0
  52. data/lib/her/model/paths.rb +54 -14
  53. data/lib/her/version.rb +1 -1
  54. data/spec/model/attributes_spec.rb +139 -0
  55. data/spec/model/dirty_spec.rb +40 -0
  56. data/spec/model/introspection_spec.rb +5 -5
  57. data/spec/model/orm_spec.rb +14 -128
  58. data/spec/model/paths_spec.rb +26 -0
  59. data/spec/model/validations_spec.rb +17 -0
  60. data/spec/spec_helper.rb +7 -32
  61. data/spec/support/extensions/array.rb +5 -0
  62. data/spec/support/extensions/hash.rb +5 -0
  63. data/spec/support/macros/model_macros.rb +29 -0
  64. metadata +52 -15
  65. data/examples/twitter-oauth/Gemfile +0 -13
  66. data/examples/twitter-oauth/app.rb +0 -50
  67. data/examples/twitter-oauth/config.ru +0 -5
  68. data/examples/twitter-oauth/views/index.haml +0 -9
  69. data/examples/twitter-search/Gemfile +0 -12
  70. data/examples/twitter-search/app.rb +0 -55
  71. data/examples/twitter-search/config.ru +0 -5
  72. data/examples/twitter-search/views/index.haml +0 -9
data/lib/her/version.rb CHANGED
@@ -1,3 +1,3 @@
1
1
  module Her
2
- VERSION = "0.5.3"
2
+ VERSION = "0.5.4"
3
3
  end
@@ -0,0 +1,139 @@
1
+ # encoding: utf-8
2
+ require File.join(File.dirname(__FILE__), "../spec_helper.rb")
3
+
4
+ describe Her::Model::Attributes do
5
+ context "mapping data to Ruby objects" do
6
+ before { spawn_model "Foo::User" }
7
+
8
+ it "handles new resource" do
9
+ @new_user = Foo::User.new(:fullname => "Tobias Fünke")
10
+ @new_user.new?.should be_true
11
+ @new_user.fullname.should == "Tobias Fünke"
12
+ end
13
+
14
+ it "accepts new resource with strings as hash keys" do
15
+ @new_user = Foo::User.new('fullname' => "Tobias Fünke")
16
+ @new_user.fullname.should == "Tobias Fünke"
17
+ end
18
+
19
+ it "handles method missing for getter" do
20
+ @new_user = Foo::User.new(:fullname => 'Mayonegg')
21
+ lambda { @new_user.unknown_method_for_a_user }.should raise_error(NoMethodError)
22
+ expect { @new_user.fullname }.to_not raise_error(NoMethodError)
23
+ end
24
+
25
+ it "handles method missing for setter" do
26
+ @new_user = Foo::User.new
27
+ expect { @new_user.fullname = "Tobias Fünke" }.to_not raise_error(NoMethodError)
28
+ end
29
+
30
+ it "handles method missing for query" do
31
+ @new_user = Foo::User.new
32
+ expect { @new_user.fullname? }.to_not raise_error(NoMethodError)
33
+ end
34
+
35
+ it "handles respond_to for getter" do
36
+ @new_user = Foo::User.new(:fullname => 'Mayonegg')
37
+ @new_user.should_not respond_to(:unknown_method_for_a_user)
38
+ @new_user.should respond_to(:fullname)
39
+ end
40
+
41
+ it "handles respond_to for setter" do
42
+ @new_user = Foo::User.new
43
+ @new_user.should respond_to(:fullname=)
44
+ end
45
+
46
+ it "handles respond_to for query" do
47
+ @new_user = Foo::User.new
48
+ @new_user.should respond_to(:fullname?)
49
+ end
50
+
51
+ it "handles has_data? for getter" do
52
+ @new_user = Foo::User.new(:fullname => 'Mayonegg')
53
+ @new_user.should_not have_data(:unknown_method_for_a_user)
54
+ @new_user.should have_data(:fullname)
55
+ end
56
+
57
+ it "handles get_data for getter" do
58
+ @new_user = Foo::User.new(:fullname => 'Mayonegg')
59
+ @new_user.get_data(:unknown_method_for_a_user).should be_nil
60
+ @new_user.get_data(:fullname).should == 'Mayonegg'
61
+ end
62
+ end
63
+
64
+
65
+ context "assigning new resource data" do
66
+ before do
67
+ spawn_model "Foo::User"
68
+ @user = Foo::User.new(:active => false)
69
+ end
70
+
71
+ it "handles data update through #assign_attributes" do
72
+ @user.assign_attributes :active => true
73
+ @user.should be_active
74
+ end
75
+ end
76
+
77
+ context "checking resource equality" do
78
+ before do
79
+ Her::API.setup :url => "https://api.example.com" do |builder|
80
+ builder.use Her::Middleware::FirstLevelParseJSON
81
+ builder.use Faraday::Request::UrlEncoded
82
+ builder.adapter :test do |stub|
83
+ stub.get("/users/1") { |env| [200, {}, { :id => 1, :fullname => "Lindsay Fünke" }.to_json] }
84
+ stub.get("/users/2") { |env| [200, {}, { :id => 1, :fullname => "Tobias Fünke" }.to_json] }
85
+ stub.get("/admins/1") { |env| [200, {}, { :id => 1, :fullname => "Lindsay Fünke" }.to_json] }
86
+ end
87
+ end
88
+
89
+ spawn_model "Foo::User"
90
+ spawn_model "Foo::Admin"
91
+ end
92
+
93
+ let(:user) { Foo::User.find(1) }
94
+
95
+ it "returns true for the exact same object" do
96
+ user.should == user
97
+ end
98
+
99
+ it "returns true for the same resource via find" do
100
+ user.should == Foo::User.find(1)
101
+ end
102
+
103
+ it "returns true for the same class with identical data" do
104
+ user.should == Foo::User.new(:id => 1, :fullname => "Lindsay Fünke")
105
+ end
106
+
107
+ it "returns true for a different resource with the same data" do
108
+ user.should == Foo::Admin.find(1)
109
+ end
110
+
111
+ it "returns false for the same class with different data" do
112
+ user.should_not == Foo::User.new(:id => 2, :fullname => "Tobias Fünke")
113
+ end
114
+
115
+ it "returns false for a non-resource with the same data" do
116
+ fake_user = stub(:data => { :id => 1, :fullname => "Lindsay Fünke" })
117
+ user.should_not == fake_user
118
+ end
119
+
120
+ it "delegates eql? to ==" do
121
+ other = Object.new
122
+ user.should_receive(:==).with(other).and_return(true)
123
+ user.eql?(other).should be_true
124
+ end
125
+
126
+ it "treats equal resources as equal for Array#uniq" do
127
+ user2 = Foo::User.find(1)
128
+ [user, user2].uniq.should == [user]
129
+ end
130
+
131
+ it "treats equal resources as equal for hash keys" do
132
+ Foo::User.find(1)
133
+ hash = { user => true }
134
+ hash[Foo::User.find(1)] = false
135
+ hash.size.should == 1
136
+ hash.should == { user => false }
137
+ end
138
+ end
139
+ end
@@ -2,4 +2,44 @@
2
2
  require File.join(File.dirname(__FILE__), "../spec_helper.rb")
3
3
 
4
4
  describe "Her::Model and ActiveModel::Dirty" do
5
+ context "checking dirty attributes" do
6
+ before do
7
+ Her::API.setup :url => "https://api.example.com" do |builder|
8
+ builder.use Her::Middleware::FirstLevelParseJSON
9
+ builder.use Faraday::Request::UrlEncoded
10
+ builder.adapter :test do |stub|
11
+ stub.get("/users/1") { |env| [200, {}, { :id => 1, :fullname => "Lindsay Fünke" }.to_json] }
12
+ stub.put("/users/1") { |env| [200, {}, { :id => 1, :fullname => "Tobias Fünke" }.to_json] }
13
+ stub.post("/users") { |env| [200, {}, { :id => 1, :fullname => "Tobias Fünke" }.to_json] }
14
+ end
15
+ end
16
+
17
+ spawn_model "Foo::User" do
18
+ attributes :fullname, :email
19
+ end
20
+ end
21
+
22
+ context "for existing resource" do
23
+ it "tracks dirty attributes" do
24
+ user = Foo::User.find(1)
25
+ user.fullname = "Tobias Fünke"
26
+ user.fullname_changed?.should be_true
27
+ user.email_changed?.should be_false
28
+ user.should be_changed
29
+ user.save
30
+ user.should_not be_changed
31
+ end
32
+ end
33
+
34
+ context "for new resource" do
35
+ it "tracks dirty attributes" do
36
+ user = Foo::User.new
37
+ user.fullname = "Tobias Fünke"
38
+ user.fullname_changed?.should be_true
39
+ user.should be_changed
40
+ user.save
41
+ user.should_not be_changed
42
+ end
43
+ end
44
+ end
5
45
  end
@@ -39,7 +39,7 @@ describe Her::Model::Introspection do
39
39
  end
40
40
  end
41
41
 
42
- describe "#nearby_class" do
42
+ describe "#her_nearby_class" do
43
43
  context "for a class inside of a module" do
44
44
  before do
45
45
  spawn_model "Foo::User"
@@ -49,10 +49,10 @@ describe Her::Model::Introspection do
49
49
  end
50
50
 
51
51
  it "returns a sibling class, if found" do
52
- Foo::User.nearby_class("AccessRecord").should == Foo::AccessRecord
53
- AccessRecord.nearby_class("Log").should == Log
54
- Foo::User.nearby_class("Log").should == Log
55
- Foo::User.nearby_class("X").should be_nil
52
+ Foo::User.her_nearby_class("AccessRecord").should == Foo::AccessRecord
53
+ AccessRecord.her_nearby_class("Log").should == Log
54
+ Foo::User.her_nearby_class("Log").should == Log
55
+ Foo::User.her_nearby_class("X").should be_nil
56
56
  end
57
57
  end
58
58
  end
@@ -11,7 +11,8 @@ describe Her::Model::ORM do
11
11
  builder.adapter :test do |stub|
12
12
  stub.get("/users/1") { |env| [200, {}, { :id => 1, :name => "Tobias Fünke" }.to_json] }
13
13
  stub.get("/users") { |env| [200, {}, [{ :id => 1, :name => "Tobias Fünke" }, { :id => 2, :name => "Lindsay Fünke" }].to_json] }
14
- stub.get("/admin_users") { |env| [200, {}, [{ :id => 1, :name => "Tobias Fünke" }, { :id => 2, :name => "Lindsay Fünke" }].to_json] }
14
+ stub.get("/admin_users") { |env| [200, {}, [{ :admin_id => 1, :name => "Tobias Fünke" }, { :admin_id => 2, :name => "Lindsay Fünke" }].to_json] }
15
+ stub.get("/admin_users/1") { |env| [200, {}, { :admin_id => 1, :name => "Tobias Fünke" }.to_json] }
15
16
  end
16
17
  end
17
18
 
@@ -21,6 +22,7 @@ describe Her::Model::ORM do
21
22
 
22
23
  spawn_model "Foo::AdminUser" do
23
24
  uses_api api
25
+ primary_key :admin_id
24
26
  end
25
27
  end
26
28
 
@@ -28,6 +30,10 @@ describe Her::Model::ORM do
28
30
  @user = Foo::User.find(1)
29
31
  @user.id.should == 1
30
32
  @user.name.should == "Tobias Fünke"
33
+
34
+ @admin = Foo::AdminUser.find(1)
35
+ @admin.id.should == 1
36
+ @admin.name.should == "Tobias Fünke"
31
37
  end
32
38
 
33
39
  it "maps a collection of resources to an array of Ruby objects" do
@@ -49,53 +55,12 @@ describe Her::Model::ORM do
49
55
  @existing_user.new?.should be_false
50
56
  end
51
57
 
52
- it "accepts new resource with strings as hash keys" do
53
- @new_user = Foo::User.new('fullname' => "Tobias Fünke")
54
- @new_user.fullname.should == "Tobias Fünke"
55
- end
56
-
57
- it "handles method missing for getter" do
58
- @new_user = Foo::User.new(:fullname => 'Mayonegg')
59
- lambda { @new_user.unknown_method_for_a_user }.should raise_error(NoMethodError)
60
- expect { @new_user.fullname }.to_not raise_error(NoMethodError)
61
- end
62
-
63
- it "handles method missing for setter" do
64
- @new_user = Foo::User.new
65
- expect { @new_user.fullname = "Tobias Fünke" }.to_not raise_error(NoMethodError)
66
- end
67
-
68
- it "handles method missing for query" do
69
- @new_user = Foo::User.new
70
- expect { @new_user.fullname? }.to_not raise_error(NoMethodError)
71
- end
72
-
73
- it "handles respond_to for getter" do
74
- @new_user = Foo::User.new(:fullname => 'Mayonegg')
75
- @new_user.should_not respond_to(:unknown_method_for_a_user)
76
- @new_user.should respond_to(:fullname)
77
- end
78
-
79
- it "handles respond_to for setter" do
80
- @new_user = Foo::User.new
81
- @new_user.should respond_to(:fullname=)
82
- end
83
-
84
- it "handles respond_to for query" do
85
- @new_user = Foo::User.new
86
- @new_user.should respond_to(:fullname?)
87
- end
58
+ it 'handles new resource with custom primary key' do
59
+ @new_user = Foo::AdminUser.new(:fullname => 'Lindsay Fünke', :id => -1)
60
+ @new_user.should be_new
88
61
 
89
- it "handles has_data? for getter" do
90
- @new_user = Foo::User.new(:fullname => 'Mayonegg')
91
- @new_user.should_not have_data(:unknown_method_for_a_user)
92
- @new_user.should have_data(:fullname)
93
- end
94
-
95
- it "handles get_data for getter" do
96
- @new_user = Foo::User.new(:fullname => 'Mayonegg')
97
- @new_user.get_data(:unknown_method_for_a_user).should be_nil
98
- @new_user.get_data(:fullname).should == 'Mayonegg'
62
+ @existing_user = Foo::AdminUser.find(1)
63
+ @existing_user.should_not be_new
99
64
  end
100
65
  end
101
66
 
@@ -308,26 +273,6 @@ describe Her::Model::ORM do
308
273
  end
309
274
  end
310
275
 
311
- context "assigning new resource data" do
312
- before do
313
- Her::API.setup :url => "https://api.example.com" do |builder|
314
- builder.use Her::Middleware::FirstLevelParseJSON
315
- builder.use Faraday::Request::UrlEncoded
316
- builder.adapter :test do |stub|
317
- stub.get("/users/1") { |env| [200, {}, { :id => 1, :fullname => "Tobias Fünke", :active => true }.to_json] }
318
- end
319
- end
320
-
321
- spawn_model "Foo::User"
322
- @user = Foo::User.find(1)
323
- end
324
-
325
- it "handles data update through #assign_attributes" do
326
- @user.assign_attributes :active => true
327
- @user.should be_active
328
- end
329
- end
330
-
331
276
  context "deleting resources" do
332
277
  before do
333
278
  Her::API.setup :url => "https://api.example.com" do |builder|
@@ -436,7 +381,7 @@ describe Her::Model::ORM do
436
381
 
437
382
  it "delegates eql? to ==" do
438
383
  other = Object.new
439
- user.expects(:==).with(other).returns(true)
384
+ user.should_receive(:==).with(other).and_return(true)
440
385
  user.eql?(other).should be_true
441
386
  end
442
387
 
@@ -454,65 +399,6 @@ describe Her::Model::ORM do
454
399
  end
455
400
  end
456
401
 
457
- context "checking dirty attributes" do
458
- before do
459
- Her::API.setup :url => "https://api.example.com" do |builder|
460
- builder.use Her::Middleware::FirstLevelParseJSON
461
- builder.use Faraday::Request::UrlEncoded
462
- builder.adapter :test do |stub|
463
- stub.get("/users/1") { |env| [200, {}, { :id => 1, :fullname => "Lindsay Fünke" }.to_json] }
464
- stub.put("/users/1") { |env| [200, {}, { :id => 1, :fullname => "Tobias Fünke" }.to_json] }
465
- stub.post("/users") { |env| [200, {}, { :id => 1, :fullname => "Tobias Fünke" }.to_json] }
466
- end
467
- end
468
-
469
- spawn_model "Foo::User" do
470
- attributes :fullname, :email
471
- end
472
- end
473
-
474
- context "for existing resource" do
475
- it "tracks dirty attributes" do
476
- user = Foo::User.find(1)
477
- user.fullname = "Tobias Fünke"
478
- user.fullname_changed?.should be_true
479
- user.email_changed?.should be_false
480
- user.should be_changed
481
- user.save
482
- user.should_not be_changed
483
- end
484
- end
485
-
486
- context "for new resource" do
487
- it "tracks dirty attributes" do
488
- user = Foo::User.new
489
- user.fullname = "Tobias Fünke"
490
- user.fullname_changed?.should be_true
491
- user.should be_changed
492
- user.save
493
- user.should_not be_changed
494
- end
495
- end
496
- end
497
-
498
- context "validating attributes" do
499
- before do
500
- spawn_model "Foo::User" do
501
- attributes :fullname, :email
502
- validates_presence_of :fullname
503
- validates_presence_of :email
504
- end
505
- end
506
-
507
- it "validates attributes when calling #valid?" do
508
- user = Foo::User.new
509
- user.should_not be_valid
510
- user.fullname = "Tobias Fünke"
511
- user.email = "tobias@bluthcompany.com"
512
- user.should be_valid
513
- end
514
- end
515
-
516
402
  context "when include_root_in_json is true" do
517
403
  context "when include_root_in_json is true" do
518
404
  before do
@@ -523,7 +409,7 @@ describe Her::Model::ORM do
523
409
 
524
410
  it "wraps params in the element name" do
525
411
  @new_user = Foo::User.new(:fullname => "Tobias Fünke")
526
- @new_user.to_params.should == { 'user' => { :fullname => "Tobias Fünke" } }
412
+ @new_user.to_params.should == { :user => { :fullname => "Tobias Fünke" } }
527
413
  end
528
414
  end
529
415
 
@@ -137,6 +137,32 @@ describe Her::Model::Paths do
137
137
  end
138
138
  end
139
139
  end
140
+
141
+ context 'custom primary key' do
142
+ before do
143
+ spawn_model 'User' do
144
+ primary_key 'UserId'
145
+ resource_path 'users/:UserId'
146
+ end
147
+
148
+ spawn_model 'Customer' do
149
+ primary_key :customer_id
150
+ resource_path 'customers/:id'
151
+ end
152
+ end
153
+
154
+ describe '#build_request_path' do
155
+ it 'uses the correct primary key attribute' do
156
+ User.build_request_path(:UserId => 'foo').should == 'users/foo'
157
+ User.build_request_path(:id => 'foo').should == 'users'
158
+ end
159
+
160
+ it 'replaces :id with the appropriate primary key' do
161
+ Customer.build_request_path(:customer_id => 'joe').should == 'customers/joe'
162
+ Customer.build_request_path(:id => 'joe').should == 'customers'
163
+ end
164
+ end
165
+ end
140
166
  end
141
167
 
142
168
  context "making subdomain HTTP requests" do
@@ -2,4 +2,21 @@
2
2
  require File.join(File.dirname(__FILE__), "../spec_helper.rb")
3
3
 
4
4
  describe "Her::Model and ActiveModel::Validations" do
5
+ context "validating attributes" do
6
+ before do
7
+ spawn_model "Foo::User" do
8
+ attributes :fullname, :email
9
+ validates_presence_of :fullname
10
+ validates_presence_of :email
11
+ end
12
+ end
13
+
14
+ it "validates attributes when calling #valid?" do
15
+ user = Foo::User.new
16
+ user.should_not be_valid
17
+ user.fullname = "Tobias Fünke"
18
+ user.email = "tobias@bluthcompany.com"
19
+ user.should be_valid
20
+ end
21
+ end
5
22
  end