shopify_api 3.1.8 → 3.2.0

Sign up to get free protection for your applications and to get access to all the features.
Files changed (45) hide show
  1. data/.gitignore +1 -0
  2. data/.travis.yml +32 -0
  3. data/CHANGELOG +14 -2
  4. data/Gemfile.lock +2 -2
  5. data/Gemfile_ar30 +9 -0
  6. data/Gemfile_ar31 +9 -0
  7. data/Gemfile_ar32 +9 -0
  8. data/README.rdoc +18 -15
  9. data/lib/active_resource/base_ext.rb +14 -15
  10. data/lib/active_resource/disable_prefix_check.rb +14 -0
  11. data/lib/active_resource/json_errors.rb +14 -4
  12. data/lib/active_resource/to_query.rb +10 -0
  13. data/lib/shopify_api.rb +1 -0
  14. data/lib/shopify_api/resources.rb +1 -1
  15. data/lib/shopify_api/resources/base.rb +4 -0
  16. data/lib/shopify_api/resources/customer.rb +4 -1
  17. data/lib/shopify_api/resources/fulfillment_service.rb +4 -0
  18. data/lib/shopify_api/resources/order_risk.rb +8 -0
  19. data/lib/shopify_api/session.rb +18 -18
  20. data/lib/shopify_api/version.rb +1 -1
  21. data/test/active_resource/json_errors_test.rb +22 -0
  22. data/test/article_test.rb +11 -0
  23. data/test/asset_test.rb +0 -1
  24. data/test/base_test.rb +0 -7
  25. data/test/customer_test.rb +10 -0
  26. data/test/fixtures/customers_search.json +60 -0
  27. data/test/fixtures/fulfillment_service.json +10 -0
  28. data/test/fixtures/image.json +10 -0
  29. data/test/fixtures/images.json +20 -0
  30. data/test/fixtures/order_risk.json +14 -0
  31. data/test/fixtures/order_risks.json +28 -0
  32. data/test/fixtures/recurring_application_charges.json +10 -0
  33. data/test/fixtures/variant.json +2 -1
  34. data/test/fixtures/variants.json +8 -4
  35. data/test/fulfillment_service_test.rb +17 -0
  36. data/test/image_test.rb +26 -0
  37. data/test/limits_test.rb +0 -1
  38. data/test/order_risk_test.rb +46 -0
  39. data/test/recurring_application_charge_test.rb +21 -0
  40. data/test/session_test.rb +44 -17
  41. data/test/test_helper.rb +6 -6
  42. data/test/variant_test.rb +14 -0
  43. metadata +49 -18
  44. checksums.yaml +0 -7
  45. data/lib/shopify_api/resources/product_search_engine.rb +0 -4
@@ -1,3 +1,3 @@
1
1
  module ShopifyAPI
2
- VERSION = "3.1.8"
2
+ VERSION = "3.2.0"
3
3
  end
@@ -0,0 +1,22 @@
1
+ require 'test_helper'
2
+
3
+ module ActiveResource
4
+ class JsonErrorsTest < Test::Unit::TestCase
5
+
6
+ def test_parsing_of_error_json_hash
7
+ errors = some_error.from_json({errors: {name: ['missing']}}.to_json)
8
+ assert_equal({"name"=>["missing"]}, errors)
9
+ end
10
+
11
+ def test_parsing_of_error_json_plain_string
12
+ errors = some_error.from_json({errors: 'some generic error'}.to_json)
13
+ assert_equal(["some generic error"], errors)
14
+ end
15
+
16
+ private
17
+
18
+ def some_error
19
+ ActiveResource::Errors.new(ShopifyAPI::Order.new)
20
+ end
21
+ end
22
+ end
@@ -2,28 +2,39 @@ require 'test_helper'
2
2
 
3
3
  class ArticleTest < Test::Unit::TestCase
4
4
 
5
+ def test_create_article
6
+ fake "blogs/1008414260/articles", :method => :post, :body => load_fixture('article')
7
+ article = ShopifyAPI::Article.new(:blog_id => 1008414260)
8
+ article.save
9
+ assert_equal "First Post", article.title
10
+ end
11
+
5
12
  def test_get_article
6
13
  fake "articles/6242736", :method => :get, :body => load_fixture('article')
7
14
  article = ShopifyAPI::Article.find(6242736)
8
15
  assert_equal "First Post", article.title
16
+ assert_equal 1008414260, article.blog_id
9
17
  end
10
18
 
11
19
  def test_get_articles
12
20
  fake "articles", :method => :get, :body => load_fixture('articles')
13
21
  articles = ShopifyAPI::Article.all
14
22
  assert_equal 3, articles.length
23
+ assert_equal 1008414260, articles.first.blog_id
15
24
  end
16
25
 
17
26
  def test_get_articles_namespaced
18
27
  fake "blogs/1008414260/articles", :method => :get, :body => load_fixture('articles')
19
28
  articles = ShopifyAPI::Article.find(:all, :params => {:blog_id => 1008414260})
20
29
  assert_equal 3, articles.length
30
+ assert_equal 1008414260, articles.first.blog_id
21
31
  end
22
32
 
23
33
  def test_get_article_namespaced
24
34
  fake "blogs/1008414260/articles/6242736", :method => :get, :body => load_fixture('article')
25
35
  article = ShopifyAPI::Article.find(6242736, :params => {:blog_id => 1008414260})
26
36
  assert_equal "First Post", article.title
37
+ assert_equal 1008414260, article.blog_id
27
38
  end
28
39
 
29
40
  def test_get_authors
@@ -1,5 +1,4 @@
1
1
  require 'test_helper'
2
- require 'uri'
3
2
 
4
3
  class AssetTest < Test::Unit::TestCase
5
4
  def test_get_assetss
@@ -54,11 +54,4 @@ class BaseTest < Test::Unit::TestCase
54
54
  ShopifyAPI::Base.delete "1"
55
55
  end
56
56
 
57
- test "#build should send custom headers with request" do
58
- ShopifyAPI::Base.activate_session @session1
59
- ShopifyAPI::Base.expects(:headers).returns({'X-Custom' => 'abc'})
60
- ShopifyAPI::Base.connection.expects(:delete).with('/admin/bases/1.json', {'X-Custom' => 'abc'})
61
- ShopifyAPI::Base.delete "1"
62
- end
63
-
64
57
  end
@@ -0,0 +1,10 @@
1
+ require 'test_helper'
2
+
3
+ class CustomerTest < Test::Unit::TestCase
4
+ def test_search
5
+ fake "customers/search.json?query=Bob+country%3AUnited+States", extension: false, body: load_fixture('customers_search')
6
+
7
+ results = ShopifyAPI::Customer.search(query: 'Bob country:United States')
8
+ assert_equal 'Bob', results.first.first_name
9
+ end
10
+ end
@@ -0,0 +1,60 @@
1
+ {
2
+ "customers": [
3
+ {
4
+ "accepts_marketing": false,
5
+ "created_at": "2014-01-20T17:25:18-05:00",
6
+ "email": "bob.norman@hostmail.com",
7
+ "first_name": "Bob",
8
+ "id": 207119551,
9
+ "last_name": "Norman",
10
+ "last_order_id": null,
11
+ "multipass_identifier": null,
12
+ "note": null,
13
+ "orders_count": 0,
14
+ "state": "disabled",
15
+ "total_spent": "0.00",
16
+ "updated_at": "2014-01-20T17:25:18-05:00",
17
+ "verified_email": true,
18
+ "tags": "",
19
+ "last_order_name": null,
20
+ "default_address": {
21
+ "address1": "Chestnut Street 92",
22
+ "address2": "",
23
+ "city": "Louisville",
24
+ "company": null,
25
+ "country": "United States",
26
+ "first_name": null,
27
+ "id": 207119551,
28
+ "last_name": null,
29
+ "phone": "555-625-1199",
30
+ "province": "Kentucky",
31
+ "zip": "40202",
32
+ "name": null,
33
+ "province_code": "KY",
34
+ "country_code": "US",
35
+ "country_name": "United States",
36
+ "default": true
37
+ },
38
+ "addresses": [
39
+ {
40
+ "address1": "Chestnut Street 92",
41
+ "address2": "",
42
+ "city": "Louisville",
43
+ "company": null,
44
+ "country": "United States",
45
+ "first_name": null,
46
+ "id": 207119551,
47
+ "last_name": null,
48
+ "phone": "555-625-1199",
49
+ "province": "Kentucky",
50
+ "zip": "40202",
51
+ "name": null,
52
+ "province_code": "KY",
53
+ "country_code": "US",
54
+ "country_name": "United States",
55
+ "default": true
56
+ }
57
+ ]
58
+ }
59
+ ]
60
+ }
@@ -0,0 +1,10 @@
1
+ {
2
+ "fulfillment_service": {
3
+ "name": "SomeService",
4
+ "id": 123456,
5
+ "inventory_management": false,
6
+ "tracking_support": true,
7
+ "requires_shipping_method": false,
8
+ "format": "json"
9
+ }
10
+ }
@@ -0,0 +1,10 @@
1
+ {
2
+ "image": {
3
+ "created_at": "2014-01-10T16:15:40-05:00",
4
+ "id": 850703190,
5
+ "position": 1,
6
+ "product_id": 632910392,
7
+ "updated_at": "2014-01-10T16:15:40-05:00",
8
+ "src": "http://cdn.shopify.com/s/files/1/0006/9093/3842/products/ipod-nano.png?v=1389388540"
9
+ }
10
+ }
@@ -0,0 +1,20 @@
1
+ {
2
+ "images": [
3
+ {
4
+ "created_at": "2014-01-17T16:11:52-05:00",
5
+ "id": 850703190,
6
+ "position": 1,
7
+ "product_id": 632910392,
8
+ "updated_at": "2014-01-17T16:11:52-05:00",
9
+ "src": "http://cdn.shopify.com/s/files/1/0006/9093/3842/products/ipod-nano.png?v=1389993112"
10
+ },
11
+ {
12
+ "created_at": "2014-01-17T16:11:52-05:00",
13
+ "id": 562641783,
14
+ "position": 2,
15
+ "product_id": 632910392,
16
+ "updated_at": "2014-01-17T16:11:52-05:00",
17
+ "src": "http://cdn.shopify.com/s/files/1/0006/9093/3842/products/ipod-nano-2.png?v=1389993112"
18
+ }
19
+ ]
20
+ }
@@ -0,0 +1,14 @@
1
+ {
2
+ "risk": {
3
+ "cause_cancel": true,
4
+ "checkout_id": null,
5
+ "display": true,
6
+ "id": 284138680,
7
+ "message": "This order was placed from a proxy IP",
8
+ "order_id": 450789469,
9
+ "recommendation": "cancel",
10
+ "score": "1.0",
11
+ "source": "External",
12
+ "merchant_message": "This order was placed from a proxy IP"
13
+ }
14
+ }
@@ -0,0 +1,28 @@
1
+ {
2
+ "risks": [
3
+ {
4
+ "cause_cancel": true,
5
+ "checkout_id": null,
6
+ "display": true,
7
+ "id": 284138680,
8
+ "message": "This order was placed from a proxy IP",
9
+ "order_id": 450789469,
10
+ "recommendation": "cancel",
11
+ "score": "1.0",
12
+ "source": "External",
13
+ "merchant_message": "This order was placed from a proxy IP"
14
+ },
15
+ {
16
+ "cause_cancel": true,
17
+ "checkout_id": null,
18
+ "display": true,
19
+ "id": 432527878,
20
+ "message": "This order came from an anonymous proxy",
21
+ "order_id": 450789469,
22
+ "recommendation": "cancel",
23
+ "score": "1.0",
24
+ "source": "External",
25
+ "merchant_message": "This order came from an anonymous proxy"
26
+ }
27
+ ]
28
+ }
@@ -0,0 +1,10 @@
1
+ {
2
+ "recurring_application_charges": [
3
+ {
4
+ "name": "Super Duper Plan",
5
+ "price": 10.0,
6
+ "return_url": "http://super-duper.shopifyapps.com",
7
+ "status": "active"
8
+ }
9
+ ]
10
+ }
@@ -17,6 +17,7 @@
17
17
  "option1": "Pink",
18
18
  "option2": null,
19
19
  "fulfillment_service": "manual",
20
- "option3": null
20
+ "option3": null,
21
+ "product_id": 632910392
21
22
  }
22
23
  }
@@ -18,7 +18,8 @@
18
18
  "option1": "Pink",
19
19
  "option2": null,
20
20
  "fulfillment_service": "manual",
21
- "option3": null
21
+ "option3": null,
22
+ "product_id": 632910392
22
23
  },
23
24
  {
24
25
  "price": "199.00",
@@ -38,7 +39,8 @@
38
39
  "option1": "Red",
39
40
  "option2": null,
40
41
  "fulfillment_service": "manual",
41
- "option3": null
42
+ "option3": null,
43
+ "product_id": 632910392
42
44
  },
43
45
  {
44
46
  "price": "199.00",
@@ -58,7 +60,8 @@
58
60
  "option1": "Green",
59
61
  "option2": null,
60
62
  "fulfillment_service": "manual",
61
- "option3": null
63
+ "option3": null,
64
+ "product_id": 632910392
62
65
  },
63
66
  {
64
67
  "price": "199.00",
@@ -78,7 +81,8 @@
78
81
  "option1": "Black",
79
82
  "option2": null,
80
83
  "fulfillment_service": "manual",
81
- "option3": null
84
+ "option3": null,
85
+ "product_id": 632910392
82
86
  }
83
87
  ]
84
88
  }
@@ -0,0 +1,17 @@
1
+ require 'test_helper'
2
+
3
+ class FulFillmentServiceTest < Test::Unit::TestCase
4
+ test 'new should create fulfillment service' do
5
+ fake "fulfillment_services", :method => :post, :body => load_fixture('fulfillment_service')
6
+ fulfillment_service = ShopifyAPI::FulfillmentService.new(:name => "SomeService")
7
+ fulfillment_service.save
8
+ assert_equal "SomeService" , fulfillment_service.name
9
+ end
10
+
11
+ test 'find should return the fulfillment service' do
12
+ fake "fulfillment_services/123456", :method => :get, :body => load_fixture('fulfillment_service')
13
+ fulfillment_service = ShopifyAPI::FulfillmentService.find(123456)
14
+ assert_equal 123456 , fulfillment_service.id
15
+ assert_equal "SomeService", fulfillment_service.name
16
+ end
17
+ end
@@ -0,0 +1,26 @@
1
+ require 'test_helper'
2
+
3
+ class ImageTest < Test::Unit::TestCase
4
+ def test_create_image
5
+ fake "products/632910392/images", :method => :post, :body => load_fixture('image')
6
+ image = ShopifyAPI::Image.new(:product_id => 632910392)
7
+ image.position = 1
8
+ image.attachment = "R0lGODlhbgCMAPf/APbr48VySrxTO7IgKt2qmKQdJeK8lsFjROG5p/nz7Zg3MNmnd7Q1MLNVS9GId71hSJMZIuzTu4UtKbeEeakhKMl8U8WYjfr18YQaIbAf=="
9
+ image.save
10
+
11
+ assert_equal 'http://cdn.shopify.com/s/files/1/0006/9093/3842/products/ipod-nano.png?v=1389388540', image.src
12
+ assert_equal 850703190, image.id
13
+ end
14
+
15
+ def test_get_images
16
+ fake "products/632910392/images", :method => :get, :body => load_fixture('images')
17
+ image = ShopifyAPI::Image.find(:all, :params => {:product_id => 632910392})
18
+ assert_equal 2, image.size
19
+ end
20
+
21
+ def test_get_image
22
+ fake "products/632910392/images/850703190", :method => :get, :body => load_fixture('image')
23
+ image = ShopifyAPI::Image.find(850703190, :params => {:product_id => 632910392})
24
+ assert_equal 850703190, image.id
25
+ end
26
+ end
@@ -1,5 +1,4 @@
1
1
  require 'test_helper'
2
- require 'mocha'
3
2
 
4
3
  class LimitsTest < Test::Unit::TestCase
5
4
  def setup
@@ -0,0 +1,46 @@
1
+ require 'test_helper'
2
+
3
+ class OrderRiskTest < Test::Unit::TestCase
4
+ def test_create_order_risk
5
+ fake "orders/450789469/risks", :method => :post, :body => load_fixture('order_risk')
6
+ v = ShopifyAPI::OrderRisk.new(:order_id => 450789469)
7
+ v.message = "This order was placed from a proxy IP"
8
+ v.recommendation = "cancel"
9
+ v.score = "1.0"
10
+ v.source = "External"
11
+ v.merchant_message = "This order was placed from a proxy IP"
12
+ v.display = true
13
+ v.cause_cancel = true
14
+ v.save
15
+
16
+ assert_equal 284138680, v.id
17
+ end
18
+
19
+ def test_get_order_risks
20
+ fake "orders/450789469/risks", :method => :get, :body => load_fixture('order_risks')
21
+ v = ShopifyAPI::OrderRisk.find(:all, :params => {:order_id => 450789469})
22
+ assert_equal 2, v.size
23
+ end
24
+
25
+ def test_get_order_risk
26
+ fake "orders/450789469/risks/284138680", :method => :get, :body => load_fixture('order_risk')
27
+ v = ShopifyAPI::OrderRisk.find(284138680, :params => {:order_id => 450789469})
28
+ assert_equal 284138680, v.id
29
+ end
30
+
31
+ def test_delete_order_risk
32
+ fake "orders/450789469/risks/284138680", :method => :get, :body => load_fixture('order_risk')
33
+ fake "orders/450789469/risks/284138680", :method => :delete, :body => "destroyed"
34
+ v = ShopifyAPI::OrderRisk.find(284138680, :params => {:order_id => 450789469})
35
+ assert v.destroy
36
+ end
37
+
38
+ def test_delete_order_risk
39
+ fake "orders/450789469/risks/284138680", :method => :get, :body => load_fixture('order_risk')
40
+ fake "orders/450789469/risks/284138680", :method => :put, :body => load_fixture('order_risk')
41
+
42
+ v = ShopifyAPI::OrderRisk.find(284138680, :params => {:order_id => 450789469})
43
+ v.position = 3
44
+ v.save
45
+ end
46
+ end
@@ -0,0 +1,21 @@
1
+ require 'test_helper'
2
+
3
+ class RecurringApplicationChargeTest < Test::Unit::TestCase
4
+ context "#all" do
5
+ should "get all charges" do
6
+ fake "recurring_application_charges"
7
+
8
+ charges = ShopifyAPI::RecurringApplicationCharge.all
9
+ assert_equal "Super Duper Plan", charges.first.name
10
+ end
11
+ end
12
+
13
+
14
+ should "not fail when there are no charges" do
15
+ fake "recurring_application_charges", body: {recurring_application_charges: []}.to_json
16
+
17
+ assert_equal 0, ShopifyAPI::RecurringApplicationCharge.all.count
18
+ assert_equal nil, ShopifyAPI::RecurringApplicationCharge.current
19
+ assert_equal [], ShopifyAPI::RecurringApplicationCharge.pending
20
+ end
21
+ end
@@ -1,13 +1,13 @@
1
1
  require 'test_helper'
2
2
 
3
3
  class SessionTest < Test::Unit::TestCase
4
-
4
+
5
5
  context "Session" do
6
6
  should "not be valid without a url" do
7
7
  session = ShopifyAPI::Session.new(nil, "any-token")
8
8
  assert_not session.valid?
9
9
  end
10
-
10
+
11
11
  should "not be valid without token" do
12
12
  session = ShopifyAPI::Session.new("testshop.myshopify.com")
13
13
  assert_not session.valid?
@@ -17,7 +17,7 @@ class SessionTest < Test::Unit::TestCase
17
17
  session = ShopifyAPI::Session.new("testshop.myshopify.com", "any-token")
18
18
  assert session.valid?
19
19
  end
20
-
20
+
21
21
  should "not raise error without params" do
22
22
  assert_nothing_raised do
23
23
  session = ShopifyAPI::Session.new("testshop.myshopify.com", "any-token")
@@ -26,7 +26,8 @@ class SessionTest < Test::Unit::TestCase
26
26
 
27
27
  should "raise error if params passed but signature omitted" do
28
28
  assert_raises(RuntimeError) do
29
- session = ShopifyAPI::Session.new("testshop.myshopify.com", "any-token", {'foo' => 'bar'})
29
+ session = ShopifyAPI::Session.new("testshop.myshopify.com")
30
+ session.request_token({'code' => 'any-code'})
30
31
  end
31
32
  end
32
33
 
@@ -35,13 +36,13 @@ class SessionTest < Test::Unit::TestCase
35
36
  assert_equal "My test key", ShopifyAPI::Session.api_key
36
37
  assert_equal "My test secret", ShopifyAPI::Session.secret
37
38
  end
38
-
39
+
39
40
  should "use 'https' protocol by default for all sessions" do
40
41
  assert_equal 'https', ShopifyAPI::Session.protocol
41
42
  end
42
43
 
43
44
  should "#temp reset ShopifyAPI::Base.site to original value" do
44
-
45
+
45
46
  ShopifyAPI::Session.setup(:api_key => "key", :secret => "secret")
46
47
  session1 = ShopifyAPI::Session.new('fakeshop.myshopify.com', 'token1')
47
48
  ShopifyAPI::Base.activate_session(session1)
@@ -85,19 +86,12 @@ class SessionTest < Test::Unit::TestCase
85
86
  assert_equal "https://localhost.myshopify.com/admin/oauth/authorize?client_id=My_test_key&scope=", permission_url
86
87
  end
87
88
 
88
- should "request token should get token" do
89
- ShopifyAPI::Session.setup(:api_key => "My test key", :secret => "My test secret")
90
- session = ShopifyAPI::Session.new('http://localhost.myshopify.com')
91
- fake nil, :url => 'https://localhost.myshopify.com/admin/oauth/access_token',:method => :post, :body => '{"access_token" : "token"}'
92
- assert_equal "token", session.request_token("code")
93
- end
94
-
95
89
  should "raise exception if code invalid in request token" do
96
90
  ShopifyAPI::Session.setup(:api_key => "My test key", :secret => "My test secret")
97
91
  session = ShopifyAPI::Session.new('http://localhost.myshopify.com')
98
92
  fake nil, :url => 'https://localhost.myshopify.com/admin/oauth/access_token',:method => :post, :status => 404, :body => '{"error" : "invalid_request"}'
99
93
  assert_raises(RuntimeError) do
100
- session.request_token("bad_code")
94
+ session.request_token(params={:code => "bad-code"})
101
95
  end
102
96
  assert_equal false, session.valid?
103
97
  end
@@ -119,13 +113,46 @@ class SessionTest < Test::Unit::TestCase
119
113
  assert_equal "https://testshop.myshopify.com/admin", session.site
120
114
  end
121
115
 
116
+ should "return_token_if_signature_is_valid" do
117
+ ShopifyAPI::Session.secret = 'secret'
118
+ params = {:code => 'any-code', :timestamp => Time.now}
119
+ sorted_params = make_sorted_params(params)
120
+ signature = Digest::MD5.hexdigest(ShopifyAPI::Session.secret + sorted_params)
121
+ fake nil, :url => 'https://testshop.myshopify.com/admin/oauth/access_token',:method => :post, :body => '{"access_token" : "any-token"}'
122
+ session = ShopifyAPI::Session.new("testshop.myshopify.com")
123
+ token = session.request_token(params.merge(:signature => signature))
124
+ assert_equal "any-token", token
125
+ end
126
+
122
127
  should "raise error if signature does not match expected" do
123
128
  ShopifyAPI::Session.secret = 'secret'
124
- params = {:foo => 'hello', :foo => 'world', :timestamp => Time.now}
125
- sorted_params = params.except(:signature, :action, :controller).collect{|k,v|"#{k}=#{v}"}.sort.join
129
+ params = {:code => "any-code", :timestamp => Time.now}
130
+ sorted_params = make_sorted_params(params)
126
131
  signature = Digest::MD5.hexdigest(ShopifyAPI::Session.secret + sorted_params)
132
+ params[:foo] = 'world'
133
+ assert_raises(RuntimeError) do
134
+ session = ShopifyAPI::Session.new("testshop.myshopify.com")
135
+ session.request_token(params.merge(:signature => signature))
136
+ end
137
+ end
127
138
 
128
- session = ShopifyAPI::Session.new("testshop.myshopify.com", "any-token", params.merge(:signature => signature))
139
+ should "raise error if timestamp is too old" do
140
+ ShopifyAPI::Session.secret = 'secret'
141
+ params = {:code => "any-code", :timestamp => Time.now - 2.days}
142
+ sorted_params = make_sorted_params(params)
143
+ signature = Digest::MD5.hexdigest(ShopifyAPI::Session.secret + sorted_params)
144
+ params[:foo] = 'world'
145
+ assert_raises(RuntimeError) do
146
+ session = ShopifyAPI::Session.new("testshop.myshopify.com")
147
+ session.request_token(params.merge(:signature => signature))
148
+ end
149
+ end
150
+
151
+ private
152
+
153
+ def make_sorted_params(params)
154
+ sorted_params = params.except(:signature, :action, :controller).collect{|k,v|"#{k}=#{v}"}.sort.join
129
155
  end
156
+
130
157
  end
131
158
  end