stormpath-sdk 0.4.0 → 1.0.0.beta

Sign up to get free protection for your applications and to get access to all the features.
Files changed (89) hide show
  1. data/.gitignore +6 -0
  2. data/.ruby-gemset +1 -0
  3. data/.ruby-version +1 -0
  4. data/.travis.yml +27 -0
  5. data/CHANGES.md +21 -1
  6. data/Gemfile +1 -2
  7. data/README.md +457 -11
  8. data/Rakefile +15 -1
  9. data/lib/stormpath-sdk.rb +52 -33
  10. data/lib/stormpath-sdk/{resource/group_list.rb → api_key.rb} +5 -9
  11. data/lib/stormpath-sdk/auth/authentication_result.rb +3 -13
  12. data/lib/stormpath-sdk/auth/basic_authenticator.rb +5 -11
  13. data/lib/stormpath-sdk/auth/basic_login_attempt.rb +6 -8
  14. data/lib/stormpath-sdk/auth/username_password_request.rb +2 -5
  15. data/lib/stormpath-sdk/cache/cache.rb +54 -0
  16. data/lib/stormpath-sdk/cache/cache_entry.rb +33 -0
  17. data/lib/stormpath-sdk/cache/cache_manager.rb +22 -0
  18. data/lib/stormpath-sdk/cache/cache_stats.rb +35 -0
  19. data/lib/stormpath-sdk/cache/memory_store.rb +29 -0
  20. data/lib/stormpath-sdk/cache/redis_store.rb +32 -0
  21. data/lib/stormpath-sdk/client.rb +111 -0
  22. data/lib/stormpath-sdk/data_store.rb +241 -0
  23. data/lib/stormpath-sdk/{client/api_key.rb → error.rb} +16 -10
  24. data/lib/stormpath-sdk/{util → ext}/hash.rb +1 -2
  25. data/lib/stormpath-sdk/http/authc/sauthc1_signer.rb +8 -4
  26. data/lib/stormpath-sdk/http/http_client_request_executor.rb +8 -7
  27. data/lib/stormpath-sdk/http/request.rb +4 -8
  28. data/lib/stormpath-sdk/{util/request_utils.rb → http/utils.rb} +17 -38
  29. data/lib/stormpath-sdk/resource/account.rb +12 -108
  30. data/lib/stormpath-sdk/resource/application.rb +35 -171
  31. data/lib/stormpath-sdk/resource/associations.rb +97 -0
  32. data/lib/stormpath-sdk/resource/base.rb +256 -0
  33. data/lib/stormpath-sdk/resource/collection.rb +94 -0
  34. data/lib/stormpath-sdk/resource/directory.rb +11 -68
  35. data/lib/stormpath-sdk/resource/email_verification_token.rb +3 -9
  36. data/lib/stormpath-sdk/resource/error.rb +4 -38
  37. data/lib/stormpath-sdk/resource/expansion.rb +28 -0
  38. data/lib/stormpath-sdk/resource/group.rb +8 -66
  39. data/lib/stormpath-sdk/resource/group_membership.rb +4 -55
  40. data/lib/stormpath-sdk/resource/{application_list.rb → instance.rb} +7 -13
  41. data/lib/stormpath-sdk/resource/password_reset_token.rb +5 -23
  42. data/lib/stormpath-sdk/resource/status.rb +22 -28
  43. data/lib/stormpath-sdk/resource/tenant.rb +5 -52
  44. data/lib/stormpath-sdk/resource/utils.rb +43 -13
  45. data/lib/stormpath-sdk/util/assert.rb +5 -15
  46. data/lib/stormpath-sdk/version.rb +3 -3
  47. data/spec/api_key_spec.rb +19 -0
  48. data/spec/auth/basic_authenticator_spec.rb +25 -0
  49. data/spec/auth/sauthc1_signer_spec.rb +42 -0
  50. data/spec/cache/cache_entry_spec.rb +157 -0
  51. data/spec/cache/cache_spec.rb +89 -0
  52. data/spec/cache/cache_stats_spec.rb +106 -0
  53. data/spec/client_spec.rb +538 -0
  54. data/spec/data_store_spec.rb +130 -0
  55. data/spec/resource/account_spec.rb +74 -0
  56. data/spec/resource/application_spec.rb +148 -0
  57. data/spec/resource/base_spec.rb +114 -0
  58. data/spec/resource/collection_spec.rb +169 -0
  59. data/spec/resource/directory_spec.rb +30 -0
  60. data/spec/resource/expansion_spec.rb +100 -0
  61. data/spec/resource/group_spec.rb +49 -0
  62. data/spec/spec_helper.rb +135 -0
  63. data/spec/support/resource_factory.rb +48 -0
  64. data/spec/support/resource_matchers.rb +27 -0
  65. data/spec/support/test_cache_stores.rb +9 -0
  66. data/spec/support/test_request_executor.rb +11 -0
  67. data/stormpath-sdk.gemspec +14 -4
  68. data/support/api.rb +55 -0
  69. metadata +214 -44
  70. data/lib/stormpath-sdk/client/client.rb +0 -38
  71. data/lib/stormpath-sdk/client/client_application.rb +0 -38
  72. data/lib/stormpath-sdk/client/client_application_builder.rb +0 -351
  73. data/lib/stormpath-sdk/client/client_builder.rb +0 -305
  74. data/lib/stormpath-sdk/ds/data_store.rb +0 -210
  75. data/lib/stormpath-sdk/ds/resource_factory.rb +0 -37
  76. data/lib/stormpath-sdk/resource/account_list.rb +0 -32
  77. data/lib/stormpath-sdk/resource/collection_resource.rb +0 -91
  78. data/lib/stormpath-sdk/resource/directory_list.rb +0 -30
  79. data/lib/stormpath-sdk/resource/group_membership_list.rb +0 -32
  80. data/lib/stormpath-sdk/resource/instance_resource.rb +0 -28
  81. data/lib/stormpath-sdk/resource/resource.rb +0 -327
  82. data/lib/stormpath-sdk/resource/resource_error.rb +0 -47
  83. data/test/client/client.yml +0 -16
  84. data/test/client/client_application_builder_spec.rb +0 -114
  85. data/test/client/client_builder_spec.rb +0 -176
  86. data/test/client/read_spec.rb +0 -254
  87. data/test/client/write_spec.rb +0 -420
  88. data/test/resource/resource_spec.rb +0 -41
  89. data/test/resource/test_resource.rb +0 -28
@@ -0,0 +1,6 @@
1
+ *~
2
+ !.travis.yml
3
+ pkg/*
4
+ Gemfile.lock
5
+ coverage
6
+ spec/fixtures/vcr_cassettes
@@ -0,0 +1 @@
1
+ stormpath-sdk-ruby
@@ -0,0 +1 @@
1
+ 1.9.3-p429
@@ -0,0 +1,27 @@
1
+ ---
2
+ env:
3
+ global:
4
+ - secure: |-
5
+ USqTzWnRXTxgwF6wpwT3UHx4hoo5QIWP6bmmJMOIG/jLWTV3cCZ4y3LEuGx/
6
+ 2x4gTyRuAAbVpJD8n9k/h7Vh6xZ8ts715kRL2OCqD+yOEioKG9RTWWzFCsNY
7
+ V3wic2+25BHgyCcnXtNFTjP/TfIKjVQETQjn3Ast2foThmyLVYo=
8
+ - secure: |-
9
+ UDZPetAAzPMtlosw481STlt60UFEei28DlBrHzDS7j/MKgkum9ZECPm4h7Dr
10
+ MXsDBp3GghLqGWc5+yNvlV2RSSZdI6YCeTSG7dXu3Tb1vTLd8Ia7pVZg3Ut2
11
+ 5Ru3+xBVHZ/4xrc+8bfL2FGpH8T1mT6GZt9BfkY8v4a+rsn8FLA=
12
+ - secure: |-
13
+ FRAT9Sjpv2MUmIu9/uaXAa2LRKmsR0vHz3Vc8oCiDIKihqFq4YF9DpHtQ0nF
14
+ OYwdcprSH5RzV/bFsf6XcPP4IlIhSRj333FJoHXfmon5cdPcpt70O4in+4YI
15
+ gXPNGgbzCpZ4WRsFbmtVazQwpXBUg2fYK8y0jNrhZoVZ9UK61Zc=
16
+ - secure: |-
17
+ WvFXv/1RKdnh/tJFRlCDMKpiNre0+K3qWLf9PaQzjvWyTAVtq3NK7Av3JSoR
18
+ EoPy/0VCzlBJPfcPa+EmLO518trgsAJ1dmBOSUmNhQOyl9bfOFlzvjqZ20Ru
19
+ eMksU4t3HbbBdh3LqR9LPxViJGfUk3LvaiuWwp8y0GB5jMjrASg=
20
+ - secure: |-
21
+ HggUi1F44G7uHy3PyKcBE54JpBMIjrmROvzrKkCPOmRP5DoeZQ+wU+8zJ89z
22
+ 4YsxOzQjYLwStB4U95LEZgQYvcbdfjkCY4uthb8XbmSeT7KSkMifhNT619+D
23
+ sTekVOlY9Ei+8O/BUr4ZCE+Fs8/8obFOxooWxxdHlaYsjj/atm4=
24
+ - secure: |-
25
+ cJ2ExNmmGSjBHIgjZ6zs+AUEVBSfyTj2qG9SNV0j/P9mnjg32irU/tG7f1yI
26
+ yvaGCsPr2h6rwtiMb8QHA4tMqHsoay0e9s7jyBtn6amgUaLsV2vdhQTWDuIU
27
+ cksOE3PMIovYl9ANLb6KrhDWt7ue/fOxEALh0a5rAu50C/tNvgo=
data/CHANGES.md CHANGED
@@ -1,6 +1,26 @@
1
1
  stormpath-sdk-ruby Changelog
2
2
  ====================
3
3
 
4
+ Version 1.0.0.beta
5
+ ------------------
6
+
7
+ Released on June 12, 2013
8
+
9
+ - Bumping version to reflect stability
10
+
11
+ Version 1.0.0.alpha
12
+ -------------
13
+
14
+ Released on June 11, 2013
15
+
16
+ - Added a new API on client for accessing applications and directories
17
+ - Added a new API on directories for accessing and creating accounts and groups
18
+ - Added account authentication by application
19
+ - Added the ability to load an application by credentialed uri
20
+ - Added caching, with Redis default
21
+ - Added pagination and querying to collections
22
+ - Added entity expansion
23
+
4
24
  Version 0.4.0
5
25
  -------------
6
26
 
@@ -45,4 +65,4 @@ Version 0.1.0
45
65
 
46
66
  Released on July 27, 2012
47
67
 
48
- - First release of the Stormpath Ruby SDK where all of the features available on the REST API by the release date were implemented.
68
+ - First release of the Stormpath Ruby SDK where all of the features available on the REST API by the release date were implemented.
data/Gemfile CHANGED
@@ -1,4 +1,3 @@
1
- source :rubygems
1
+ source 'https://rubygems.org'
2
2
 
3
3
  gemspec
4
-
data/README.md CHANGED
@@ -1,27 +1,473 @@
1
- Stormpath SDK For Ruby
2
- Copyright (c) 2012 Stormpath, Inc. and contributors.
1
+ [![Build Status](https://api.travis-ci.org/stormpath/stormpath-sdk-ruby.png?branch=master)](https://travis-ci.org/stormpath/stormpath-sdk-ruby)
3
2
 
4
- This project is licensed under the Apache 2.0 Open Source License:
3
+ # Stormpath Ruby SDK
5
4
 
6
- http://www.apache.org/licenses/LICENSE-2.0
5
+ Stormpath is the first easy, secure user management and authentication service for developers.
6
+ This is the Ruby SDK to ease integration of its features with any Ruby language based
7
+ application.
7
8
 
8
- Project Documentation is here:
9
+ ## Install
9
10
 
10
- https://github.com/stormpath/stormpath-sdk-ruby/wiki
11
+ ```sh
12
+ $ gem install stormpath-sdk
13
+ ```
14
+
15
+ ## Quickstart Guide
16
+
17
+ 1. If you have not already done so, register as a developer on
18
+ [Stormpath][stormpath] and set up your API credentials and resources:
19
+
20
+ 1. Create a [Stormpath][stormpath] developer account and [create your API Keys][create-api-keys]
21
+ downloading the <code>apiKey.properties</code> file into a <code>.stormpath</code>
22
+ folder under your local home directory.
23
+
24
+ 1. Create an application and a directory to store your users'
25
+ accounts. Make sure the directory is assigned as a login source
26
+ to the application.
27
+
28
+ 1. Take note of the _REST URL_ of the application and of directory
29
+ you just created.
30
+
31
+ 1. **Require the Stormpath Ruby SDK**
32
+
33
+ ```ruby
34
+ require 'stormpath-sdk'
35
+ ```
36
+
37
+ 1. **Create a client** using the API key properties file
38
+
39
+ ```ruby
40
+ client = Stormpath::Client.new api_key_file_location: File.join(ENV['HOME']), '.stormpath', 'apiKey.properties')
41
+ ```
42
+
43
+ 1. **List all your applications and directories**
44
+
45
+ ```ruby
46
+ client.applications.each do |application|
47
+ p "Application: #{application.name}"
48
+ end
49
+
50
+ client.directories.each do |directory|
51
+ p "Directory: #{directory.name}"
52
+ end
53
+ ```
54
+
55
+ 1. **Get access to the specific application and directory** using the
56
+ URLs you acquired above.
57
+
58
+ ```ruby
59
+ application = client.applications.get application_url
60
+
61
+ directory = client.directories.get application_url
62
+ ```
63
+
64
+ 1. **Create an account for a user** on the directory.
65
+
66
+ ```ruby
67
+ account = directory.accounts.create({
68
+ given_name: 'John',
69
+ surname: 'Smith',
70
+ email: 'john.smith@example.com',
71
+ username: 'johnsmith',
72
+ password: '4P@$$w0rd!'
73
+ })
74
+ ```
75
+
76
+ 1. **Update an account**
77
+
78
+ ```ruby
79
+ account.given_name = 'Johnathan'
80
+ account.middle_name = 'A.'
81
+ account.save
82
+ ```
83
+
84
+ 1. **Authenticate the Account** for use with an application:
85
+
86
+ ```ruby
87
+ auth_request =
88
+ Stormpath::Authentication::UsernamePasswordRequest.new 'johnsmith', '4P@$$w0rd!'
89
+
90
+ begin
91
+ auth_result = application.authenticate_account auth_request
92
+ account = auth_result.account
93
+ rescue Stormpath::Error => e
94
+ #If credentials are invalid or account doesn't exist
95
+ end
96
+ ```
97
+
98
+ 1. **Send a password reset request**
99
+
100
+ ```ruby
101
+ application.send_password_reset_email 'john.smith@example.com'
102
+ ```
103
+
104
+ 1. **Create a group** in a directory
105
+
106
+ ```ruby
107
+ directory.groups.create name: 'Admins'
108
+ ```
109
+
110
+ 1. **Add the account to the group**
111
+
112
+ ```ruby
113
+ group.add_account account
114
+ ```
115
+
116
+ 1. **Check for account inclusion in group** by reloading the account
117
+
118
+ ```ruby
119
+ account = clients.accounts.get account.href
120
+ is_admin = account.groups.any? { |group| group.name == 'Admins' }
121
+ ```
11
122
 
12
- # Build Instructions
123
+ ## Common Uses
13
124
 
14
- Via rubygems.org:
125
+ ### Creating a client
15
126
 
127
+ All Stormpath features are accessed through a
128
+ <code>Stormpath::Client</code> instance, or a resource
129
+ created from one. A client needs an API key (made up of an _id_ and a
130
+ _secret_) from your Stormpath developer account to manage resources
131
+ on that account. That API key can be specified any number of ways
132
+ in the hash of values passed on Client initialization:
133
+
134
+ * The location of API key properties file:
135
+
136
+ ```ruby
137
+ client = Stormpath::Client.new
138
+ api_key_file_location: '/some/path/to/apiKey.properties'
139
+ ```
140
+
141
+ You can even identify the names of the properties to use as the API
142
+ key id and secret. For example, suppose your properties was:
143
+
144
+ ```
145
+ foo=APIKEYID
146
+ bar=APIKEYSECRET
147
+ ```
148
+
149
+ You could load it with the following:
150
+
151
+ ```ruby
152
+ client = Stormpath::Client.new
153
+ api_key_file_location: '/some/path/to/apiKey.properties',
154
+ api_key_id_property_name: 'foo',
155
+ api_key_secret_property_name: 'bar'
156
+ ```
157
+
158
+ * Passing in a Stormpath::APIKey instance:
159
+
160
+ ```ruby
161
+ api_key = Stormpath::APIKey.new api_id, api_secret
162
+ client = Stormpath::Client.new api_key: api_key
163
+ ```
164
+
165
+ * By explicitly setting the API key id and secret:
166
+
167
+ ```ruby
168
+ client = Stormpath::Client.new
169
+ api_key: { id: api_id, secret: api_secret }
170
+ ```
171
+
172
+ * By passing the REST URL of a Stormpath application on your account
173
+ with the API id and secret embedded. For example, the URL would look
174
+ like:
175
+
176
+ ```
177
+ http://#{api_key_id}:#{api_key_secret}@api.stormpath.com/v1/applications/#{application_id}
178
+ ```
179
+
180
+ The client could then be created with the above URL:
181
+
182
+ ```ruby
183
+ client = Stormpath::Client.new application_url: application_url
184
+ ```
185
+
186
+ This method will also provide a <code>application</code> property on
187
+ on the client to directly access that resource.
188
+
189
+ ### Accessing Resources
190
+
191
+ Most of the work you do with Stormpath is done through the applications
192
+ and directories you have registered. You use the client to access them
193
+ with their REST URL:
194
+
195
+ ```ruby
196
+ application = client.applications.get application_url
197
+
198
+ directory = client.directories.get directory_url
16
199
  ```
17
- $ gem install stormpath-sdk
200
+
201
+ The <code>applications</code> and <code>directories</code> property on a
202
+ client instance are also <code>Enumerable</code> allowing you to iterate
203
+ and scan for resources via that interface.
204
+
205
+ Additional resources are <code>accounts</code>, <code>groups</code>,
206
+ <code>group_membership</code>, and the single reference to your
207
+ <code>tenant</code>.
208
+
209
+ ### Registering Accounts
210
+
211
+ Accounts are created on a directory instance. They can be created in two
212
+ ways:
213
+
214
+ * With the <code>create_account</code> method:
215
+
216
+ ```ruby
217
+ account = directory.create_account({
218
+ given_name: 'John',
219
+ surname: 'Smith',
220
+ email: 'john.smith@example.com',
221
+ username: 'johnsmith',
222
+ password: '4P@$$w0rd!'
223
+ })
224
+ ```
225
+
226
+ This metod can take an additional flag to indicate if the account
227
+ can skip any registration workflow configured on the directory.
228
+
229
+ ```ruby
230
+ ## Will skip workflow, if any
231
+ account = directory.create_account account_props, false
232
+ ```
233
+
234
+ * Creating it directly on the <code>accounts</code> collection property
235
+ on the directory:
236
+
237
+ ```ruby
238
+ account = directory.accounts.create({
239
+ given_name: 'John',
240
+ surname: 'Smith',
241
+ email: 'john.smith@example.com',
242
+ username: 'johnsmith',
243
+ password: '4P@$$w0rd!'
244
+ })
245
+ ```
246
+
247
+ Both these methods can take either a <code>Hash</code> of the account
248
+ properties, or a <code>Stormpath::Account</code>.
249
+
250
+ If the directory has been configured with an email verification workflow
251
+ and a non-Stormpath URL, you have to pass the verification token sent to
252
+ the URL in a <code>sptoken</code> query parameter back to Stormpath to
253
+ complete the workflow. This is done through the
254
+ <code>verify_email_token</code> on the <code>accounts</code> collection.
255
+
256
+ For example, suppose you have a Sinatra application
257
+ that is handling the email verification at the path
258
+ <code>/users/verify</code>. You could use the following code:
259
+
260
+ ```ruby
261
+ get '/users/verify' do
262
+ token = params[:sptoken]
263
+ account = client.accounts.verify_email_token token
264
+ #proceed to update session, display account, etc
265
+ end
266
+ ```
267
+
268
+ ### Authentication
269
+
270
+ Authentication is accomplished by passing a username or an email and a
271
+ password to <code>authenticate_account</code> of an application we've
272
+ registered on Stormpath. This will either return a
273
+ <code>Stormpath::Authentication::AuthenticationResult</code> instance if
274
+ the credentials are valid, or raise a <code>Stormpath::Error</code>
275
+ otherwise. In the former case, you can get the <code>account</code>
276
+ associated with the credentials.
277
+
278
+ ```ruby
279
+ auth_request =
280
+ Stormpath::Authentication::UsernamePasswordRequest.new 'johnsmith', '4P@$$w0rd!'
281
+
282
+ begin
283
+ auth_result = application.authenticate_account auth_request
284
+ account = auth_result.account
285
+ rescue Stormpath::Error => e
286
+ #If credentials are invalid or account doesn't exist
287
+ end
288
+ ```
289
+
290
+ ### Password Reset
291
+
292
+ A password reset workflow, if configured on the directory the account is
293
+ registered on, can be kicked off with the
294
+ <code>send_password_reset_email</code> method on an application:
295
+
296
+ ```ruby
297
+ application.send_password_reset_email 'john.smith@example.com'
298
+ ```
299
+
300
+ If the workflow has been configured to verify through a non-Stormpath
301
+ URL, you can verify the token sent in the query parameter
302
+ <code>sptoken</code> with the <code>verify_password_reset_token</code>
303
+ method on the application.
304
+
305
+ For example, suppose you have a Sinatra application that is verifying
306
+ the tokens. You use the following to carry it out:
307
+
308
+ ```ruby
309
+ get '/users/verify' do
310
+ token = params[:sptoken]
311
+ account = application.verify_password_reset_token token
312
+ #proceed to update session, display account, etc
313
+ end
314
+ ```
315
+
316
+ With the account acquired you can then update the password:
317
+
318
+ ```ruby
319
+ account.password = new_password
320
+ account.save
321
+ ```
322
+
323
+ _NOTE :_ Confirming a new password is left up to the web application
324
+ code calling the Stormpath SDK. The SDK does not require confirmation.
325
+
326
+ ### ACL through Groups
327
+
328
+ Memberships of accounts in certain groups can be used as an
329
+ authorization mechanism. As the <code>groups</code> collection property
330
+ on an account instance is <code>Enumerable</code>, you can use any of
331
+ that module's methods to determine if an account belongs to a specific
332
+ group:
333
+
334
+ ```ruby
335
+ account.groups.any?{|group| group.name == 'administrators'
336
+ ```
337
+
338
+ You can create groups and assign them to accounts using the Stormpath
339
+ web console, or programmatically. Groups are created on directories:
340
+
341
+ ```ruby
342
+ group = directory.groups.create name: 'administrators'
343
+ ```
344
+
345
+ Group membership can be created by:
346
+
347
+ * Explicitly creating a group membership resource with your client:
348
+
349
+ ```ruby
350
+ group_memebership = client.group_memberships.create group, account
351
+ ```
352
+
353
+ * Using the <code>add_group</code> method on the account instance:
354
+
355
+ ```ruby
356
+ account.add_group group
357
+ ```
358
+
359
+ * Using the <code>add_account</code> method on the group instance:
360
+
361
+ ```ruby
362
+ group.add_group account
363
+ ```
364
+
365
+ You will need to reload the account or group resource after these
366
+ operations to ensure they've picked up the changes.
367
+
368
+ ## Testing
369
+
370
+ ### Setup
371
+
372
+ The functional tests of the SDK run against a Stormpath tenant. In that
373
+ account, create:
374
+
375
+ * An application reserved for testing.
376
+ * A directory reserved for test accounts. _Be sure to associate this
377
+ directory to the test application as a login source_.
378
+ * Another directory reserved for test accounts with the account
379
+ verification workflow turned on. _Be sure to associate this directory
380
+ to the test application as a login source_.
381
+
382
+ The following environment variables need will then need to be set:
383
+
384
+ * <code>STORMPATH_SDK_TEST_API_KEY_ID</code> - The <code>id</code> from
385
+ your Stormpath API key.
386
+ * <code>STORMPATH_SDK_TEST_API_KEY_SECRET</code> - The
387
+ <code>secret</code> from your Stormpath API key.
388
+ * <code>STORMPATH_SDK_TEST_APPLICATION_URL</code> - The URL to the
389
+ application created above.
390
+ * <code>STORMPATH_SDK_TEST_DIRECTORY_URL</code> - The URL to the first
391
+ directory created above.
392
+ * <code>STORMPATH_SDK_TEST_DIRECTORY_WITH_VERIFICATION_URL</code> - The
393
+ URL to the second directory created above.
394
+
395
+ ### Running
396
+
397
+ Once properly configured, the tests can be run as the default
398
+ <code>Rake<code> task:
399
+
400
+ ```sh
401
+ $ rake
18
402
  ```
19
403
 
404
+ Or by specifying the <code>spec</code> task:
405
+
406
+ ```sh
407
+ $ rake spec
408
+ ```
409
+
410
+ Or through <code>rspec</code>.
411
+
412
+ ## Contributing
413
+
414
+ You can make your own contributions by forking the <code>development</code>
415
+ branch, making your changes, and issuing pull-requests on the
416
+ <code>development</code> branch.
417
+
418
+ ### Building the Gem
419
+
20
420
  To build and install the development branch yourself from the latest source:
21
421
 
22
422
  ```
23
423
  $ git clone git@github.com:stormpath/stormpath-sdk-ruby.git
24
424
  $ cd stormpath-sdk-ruby
25
425
  $ rake gem
26
- $ gem install pkg/stormpath-sdk-{version}
27
- ```
426
+ $ gem install pkg/stormpath-sdk-{version}.gem
427
+ ```
428
+
429
+ ## Quick Class Diagram
430
+
431
+ ```
432
+ +-------------+
433
+ | Application |
434
+ | |
435
+ +-------------+
436
+ + 1
437
+ |
438
+ | +-------------+
439
+ | | LoginSource |
440
+ o- - - - - -| |
441
+ | +-------------+
442
+ |
443
+ v 0..*
444
+ +--------------+ +--------------+
445
+ | Directory | 1 1 | Account |1
446
+ | |<----------+| |+----------+
447
+ | | | | |
448
+ | | 1 0..* | |0..* |
449
+ | |+---------->| |+-----+ |
450
+ | | +--------------+ | | +-----------------+
451
+ | | | | | GroupMembership |
452
+ | | o- - o - - - - | |
453
+ | | +--------------+ | | +-----------------+
454
+ | | 1 0..* | Group |1 | |
455
+ | |+---------->| |<-----+ |
456
+ | | | | |
457
+ | | 1 1 | |0..* |
458
+ | |<----------+| |<----------+
459
+ +--------------+ +--------------+
460
+ ```
461
+
462
+ ## Copyright & Licensing
463
+
464
+ Copyright &copy; 2012 Stormpath, Inc. and contributors.
465
+
466
+ This project is licensed under the [Apache 2.0 Open Source License](http://www.apache.org/licenses/LICENSE-2.0).
467
+
468
+ For additional information, please see the full [Project Documentation](https://www.stormpath.com/docs/ruby/product-guide).
469
+
470
+ [bundler]: http://gembundler.com/
471
+ [stormpath]: http://stormpath.com/
472
+ [create-api-keys]: http://www.stormpath.com/docs/ruby/product-guide#AssignAPIkeys
473
+ [stormpath_bootstrap]: https://github.com/stormpath/stormpath-sdk-ruby/wiki/Bootstrapping-Stormpath