daylight 0.9.0.rc1

Sign up to get free protection for your applications and to get access to all the features.
Files changed (116) hide show
  1. checksums.yaml +7 -0
  2. data/README.md +113 -0
  3. data/app/controllers/daylight_documentation/documentation_controller.rb +27 -0
  4. data/app/helpers/daylight_documentation/documentation_helper.rb +57 -0
  5. data/app/views/daylight_documentation/documentation/_header.haml +4 -0
  6. data/app/views/daylight_documentation/documentation/index.haml +12 -0
  7. data/app/views/daylight_documentation/documentation/model.haml +114 -0
  8. data/app/views/layouts/documentation.haml +22 -0
  9. data/config/routes.rb +8 -0
  10. data/doc/actions.md +70 -0
  11. data/doc/benchmarks.md +17 -0
  12. data/doc/contribute.md +80 -0
  13. data/doc/develop.md +1205 -0
  14. data/doc/environment.md +109 -0
  15. data/doc/example.md +3 -0
  16. data/doc/framework.md +31 -0
  17. data/doc/install.md +128 -0
  18. data/doc/principles.md +42 -0
  19. data/doc/testing.md +107 -0
  20. data/doc/usage.md +970 -0
  21. data/lib/daylight/api.rb +293 -0
  22. data/lib/daylight/associations.rb +247 -0
  23. data/lib/daylight/client_reloader.rb +45 -0
  24. data/lib/daylight/collection.rb +161 -0
  25. data/lib/daylight/errors.rb +94 -0
  26. data/lib/daylight/inflections.rb +7 -0
  27. data/lib/daylight/mock.rb +282 -0
  28. data/lib/daylight/read_only.rb +88 -0
  29. data/lib/daylight/refinements.rb +63 -0
  30. data/lib/daylight/reflection_ext.rb +67 -0
  31. data/lib/daylight/resource_proxy.rb +226 -0
  32. data/lib/daylight/version.rb +10 -0
  33. data/lib/daylight.rb +27 -0
  34. data/rails/daylight/api_controller.rb +354 -0
  35. data/rails/daylight/documentation.rb +13 -0
  36. data/rails/daylight/helpers.rb +32 -0
  37. data/rails/daylight/params.rb +23 -0
  38. data/rails/daylight/refiners.rb +186 -0
  39. data/rails/daylight/server.rb +29 -0
  40. data/rails/daylight/tasks.rb +37 -0
  41. data/rails/extensions/array_ext.rb +9 -0
  42. data/rails/extensions/autosave_association_fix.rb +49 -0
  43. data/rails/extensions/has_one_serializer_ext.rb +111 -0
  44. data/rails/extensions/inflections.rb +6 -0
  45. data/rails/extensions/nested_attributes_ext.rb +94 -0
  46. data/rails/extensions/read_only_attributes.rb +35 -0
  47. data/rails/extensions/render_json_meta.rb +99 -0
  48. data/rails/extensions/route_options.rb +47 -0
  49. data/rails/extensions/versioned_url_for.rb +22 -0
  50. data/spec/config/dependencies.rb +2 -0
  51. data/spec/config/factory_girl.rb +4 -0
  52. data/spec/config/simplecov_rcov.rb +26 -0
  53. data/spec/config/test_api.rb +1 -0
  54. data/spec/controllers/documentation_controller_spec.rb +24 -0
  55. data/spec/dummy/README.rdoc +28 -0
  56. data/spec/dummy/Rakefile +6 -0
  57. data/spec/dummy/app/assets/images/.keep +0 -0
  58. data/spec/dummy/app/assets/javascripts/application.js +13 -0
  59. data/spec/dummy/app/assets/stylesheets/application.css +13 -0
  60. data/spec/dummy/app/controllers/application_controller.rb +5 -0
  61. data/spec/dummy/app/controllers/concerns/.keep +0 -0
  62. data/spec/dummy/app/helpers/application_helper.rb +2 -0
  63. data/spec/dummy/app/mailers/.keep +0 -0
  64. data/spec/dummy/app/models/.keep +0 -0
  65. data/spec/dummy/app/models/concerns/.keep +0 -0
  66. data/spec/dummy/app/views/layouts/application.html.erb +14 -0
  67. data/spec/dummy/bin/bundle +3 -0
  68. data/spec/dummy/bin/rails +4 -0
  69. data/spec/dummy/bin/rake +4 -0
  70. data/spec/dummy/config/application.rb +24 -0
  71. data/spec/dummy/config/boot.rb +5 -0
  72. data/spec/dummy/config/database.yml +25 -0
  73. data/spec/dummy/config/environment.rb +5 -0
  74. data/spec/dummy/config/environments/development.rb +29 -0
  75. data/spec/dummy/config/environments/production.rb +80 -0
  76. data/spec/dummy/config/environments/test.rb +36 -0
  77. data/spec/dummy/config/initializers/backtrace_silencers.rb +7 -0
  78. data/spec/dummy/config/initializers/daylight.rb +1 -0
  79. data/spec/dummy/config/initializers/filter_parameter_logging.rb +4 -0
  80. data/spec/dummy/config/initializers/inflections.rb +16 -0
  81. data/spec/dummy/config/initializers/mime_types.rb +5 -0
  82. data/spec/dummy/config/initializers/secret_token.rb +12 -0
  83. data/spec/dummy/config/initializers/session_store.rb +3 -0
  84. data/spec/dummy/config/initializers/wrap_parameters.rb +14 -0
  85. data/spec/dummy/config/locales/en.yml +23 -0
  86. data/spec/dummy/config/routes.rb +59 -0
  87. data/spec/dummy/config.ru +4 -0
  88. data/spec/dummy/lib/assets/.keep +0 -0
  89. data/spec/dummy/log/.keep +0 -0
  90. data/spec/dummy/public/404.html +58 -0
  91. data/spec/dummy/public/422.html +58 -0
  92. data/spec/dummy/public/500.html +57 -0
  93. data/spec/dummy/public/favicon.ico +0 -0
  94. data/spec/helpers/documentation_helper_spec.rb +82 -0
  95. data/spec/lib/daylight/api_spec.rb +178 -0
  96. data/spec/lib/daylight/associations_spec.rb +325 -0
  97. data/spec/lib/daylight/collection_spec.rb +235 -0
  98. data/spec/lib/daylight/errors_spec.rb +111 -0
  99. data/spec/lib/daylight/mock_spec.rb +144 -0
  100. data/spec/lib/daylight/read_only_spec.rb +118 -0
  101. data/spec/lib/daylight/refinements_spec.rb +80 -0
  102. data/spec/lib/daylight/reflection_ext_spec.rb +50 -0
  103. data/spec/lib/daylight/resource_proxy_spec.rb +325 -0
  104. data/spec/rails/daylight/api_controller_spec.rb +421 -0
  105. data/spec/rails/daylight/helpers_spec.rb +41 -0
  106. data/spec/rails/daylight/params_spec.rb +45 -0
  107. data/spec/rails/daylight/refiners_spec.rb +178 -0
  108. data/spec/rails/extensions/array_ext_spec.rb +51 -0
  109. data/spec/rails/extensions/has_one_serializer_ext_spec.rb +135 -0
  110. data/spec/rails/extensions/nested_attributes_ext_spec.rb +177 -0
  111. data/spec/rails/extensions/render_json_meta_spec.rb +140 -0
  112. data/spec/rails/extensions/route_options_spec.rb +309 -0
  113. data/spec/rails/extensions/versioned_url_for_spec.rb +46 -0
  114. data/spec/spec_helper.rb +43 -0
  115. data/spec/support/migration_helper.rb +40 -0
  116. metadata +422 -0
@@ -0,0 +1,109 @@
1
+ # Build Environment
2
+
3
+ This is the environment that Daylight was built with that may not be obvious
4
+ simply from the Gemfile.
5
+
6
+ ## Requirements
7
+
8
+ Only items requrired are [ruby](https://www.ruby-lang.org/en/downloads/)
9
+ and [bundler](http://bundler.io/) to contribute to Daylight.
10
+
11
+ All other [dependencies](#dependencies) are loaded via `bundler`.
12
+
13
+ ruby 2.0.0-p247
14
+ bundler 1.3.5
15
+
16
+ ## Developer Preferences
17
+
18
+ We use [rbenv](https://github.com/sstephenson/rbenv) to help manage
19
+ our ruby versions and [pow](http://pow.cx/) as our application
20
+ (rack) server.
21
+
22
+ rbenv 0.4.0
23
+ pow 0.4.1
24
+ powder 0.2.0
25
+
26
+ FWIW, we also use rbenv plugin
27
+ [rbenv-gemset](https://github.com/jf/rbenv-gemset),
28
+ [rbenv-bundler](https://github.com/carsomyr/rbenv-bundler), and
29
+ [rbenv-gem-rehash](https://github.com/sstephenson/rbenv-gem-rehash)
30
+
31
+ We control pow using the [powder](https://github.com/Rodreegez/powder) CLI.
32
+
33
+ _YMMV, and can use any tool to handle these functions._
34
+
35
+ ## Runtime Dependencies
36
+
37
+ rails 4.0.5
38
+ activeresource 4.0.0
39
+ active_model_serializers 0.8.1
40
+
41
+ [Rails](https://github.com/rails/rails)) and
42
+ [ActiveModelSerializers](https://github.com/rails-api/active_model_serializers)
43
+ are required by Daylight run the server and extend it.
44
+ [ActiveResource](https://github.com/rails/activeresource) is required by Daylight to run the client and extend it.
45
+
46
+ haml 4.0.5
47
+ hanna-bootstrap 0.0.5
48
+ actionpack-page_caching 1.0.2
49
+
50
+ These are for the Documentation Rails Engine.
51
+ [Haml](https://github.com/haml/haml) is a
52
+ templating engine to generate the API documentation.
53
+ [actionpack-page_caching](https://github.com/rails/actionpack-page_caching)
54
+ will cache the generated documenation so there is no penalty to the server.
55
+
56
+ [hanna-bootstrap](https://github.com/ngs/hanna-bootstrap) to generate
57
+ rdoc using the twitter bootstrap theme.
58
+
59
+
60
+ ## API Development Dependencies
61
+
62
+ We have used the following for API Development. Without any motification we
63
+ expect you to develop your API using:
64
+
65
+ rails 4.0
66
+ activeresource 4.0
67
+ active_model_serializers 0.8
68
+
69
+ In our own API, we use the following to assist us to build our API, these
70
+ libraries are in no way required to use Daylight. These are only offered
71
+ as suggestions:
72
+
73
+ rails-api 0.1.0
74
+ versionist 1.2.1
75
+
76
+ ## Development & Test Dependencies
77
+
78
+ rspec 2.14.1
79
+ rspec-rails 2.14.2
80
+ simplecov-rcov 0.2.3
81
+
82
+ We use [rspec](https://github.com/rspec/rspec) and
83
+ [rspec-rails](https://github.com/rspec/rspec-rails)
84
+ for rails testing and
85
+ [simplecov-rcov](https://github.com/fguillen/simplecov-rcov)
86
+ for coverage testing.
87
+
88
+ webmock 1.18.0
89
+
90
+ Daylight request and responses occur through [webmock](https://github.com/bblimke/webmock).
91
+ `Daylight::Mock` is built on top of it.
92
+
93
+ sqlite3-ruby 1.3.9
94
+ factory_girl 2.0
95
+ faker 1.2.0
96
+
97
+ Database backend is handled by [sqlite3](https://github.com/sparklemotion/sqlite3-ruby),
98
+ you will need to [download](https://www.sqlite.org/download.html)
99
+ the binaries or use your favorite package manager.
100
+
101
+ ````
102
+ brew install sqlite3
103
+ ````
104
+
105
+ We use [factory_girl](https://github.com/thoughtbot/factory_girl)
106
+ to build our fixtures and [faker](https://github.com/stympy/faker)
107
+ to populate its data.
108
+
109
+
data/doc/example.md ADDED
@@ -0,0 +1,3 @@
1
+ ## Example Application
2
+
3
+ Placeholder for explaining how the [example application](example) works and how it was put together.
data/doc/framework.md ADDED
@@ -0,0 +1,31 @@
1
+ # Framework Overview
2
+
3
+ `Daylight` alters or extends Rails components. The framework are tiny
4
+ extensions on the shoulders of giants. See [stats](#loc-statistics).
5
+ Generally, there are 5 main parts to `Daylight`:
6
+
7
+ | Directory | Function | |
8
+ | ---------------- | ---------------- | --------------------------------------------------------------- |
9
+ | lib | Client Modules | Additions and fixes to `ActiveResource::Base` |
10
+ | rails/daylight | Server Modules | Additions to the Rails MVC environment |
11
+ | rails/extensions | Rails Extensions | Patches/Fixes to Rails components and ActiveModelSerializer |
12
+ | app, config | Documentation | `Rails::Engine` to provide documentation of the API |
13
+ | spec, test | Mock Testing | Mock to assist API testing in either RSpec or TestUnit/MiniTest |
14
+
15
+ `Daylight` is a tiny extension on the shoulders of giants and wouldn't be possible without
16
+
17
+ ## Rails Extensions
18
+
19
+ The Rails extensions are loaded along with the Server modules. This is the list
20
+ of extensions and the reason why they are required:
21
+
22
+ | Extension | Reason |
23
+ | ------------------------ | ---------------------------------------------------------------------------- |
24
+ | autosave_association_fix | fix `ActiveRecord::Base` autosaving `inverse_of` associations |
25
+ | has_one_serializer_ext | modify `ActiveModel::Serializer` to recognize belong_to :through association |
26
+ | read_only_attributes | modfiy `ActiveModel::Serializer` to support `read_only` attributes |
27
+ | render_json_meta | mofify `ActiveModel::Serializer` to include metadata to the json response |
28
+ | nested_attributes_ext | allows `ActiveRecord::Base` association between previously existing records |
29
+ | route_options | extend routes to allow `associated` and `remoted` options |
30
+ | versioned_url_for | uses any versioned paths for `url_for` |
31
+
data/doc/install.md ADDED
@@ -0,0 +1,128 @@
1
+ # Installation Steps
2
+
3
+ ## Client Setup
4
+
5
+ Install the gem or add it to your bundler's Gemfile:
6
+
7
+ gem install daylight
8
+
9
+ Require and run setup, no options are needed:
10
+
11
+ ````ruby
12
+ require 'daylight'
13
+
14
+ Daylight::API.setup!
15
+ ````
16
+
17
+ This is the same as specifying `setup!` with the following defaults:
18
+
19
+ ````ruby
20
+ require 'daylight'
21
+
22
+ Daylight::API.setup!({
23
+ namespace: 'API',
24
+ password: nil,
25
+ endpoint: 'http://localhost',
26
+ version: 'v1',
27
+ versions: ['v1'],
28
+ timeout: 60
29
+ })
30
+ ````
31
+
32
+ You can customize each of these options:
33
+ * `namespace` is the module to which you wish ActiveResource client models to belong
34
+ * `password` supplied in the `X-Http-Authentication` header to be used for simple authentication
35
+ * `endpoint` is the URL where your server is running (a synonym for `ActiveResource#site`)
36
+ * `version` is the active version of your API client models
37
+ * `version` the set of all versions of your API client models
38
+ * `timeout` the duration in seconds in which to timeout a request
39
+
40
+ You can customize any combination of these options.
41
+
42
+ ## Server Setup
43
+
44
+ Install the gem or add it to your bundler's Gemfile:
45
+
46
+ gem install daylight
47
+
48
+ In an initializer add the Daylight's Rails extensions and patches:
49
+
50
+ ````ruby
51
+ require 'daylight/server'
52
+ ````
53
+
54
+ ## API Documentation
55
+
56
+ Daylight provides a Rails App Engine for autogenerated API documentation. All
57
+ docs are created on the fly by examining your routes, controllers and
58
+ ActiveRecord models.
59
+
60
+ Add this to `config/application.rb`:
61
+
62
+ ````ruby
63
+ require 'daylight/documentation'
64
+ ````
65
+
66
+ And mount it in your routes:
67
+
68
+ ````ruby
69
+ mount Daylight::Documentation => '/docs/api'
70
+ ````
71
+
72
+ You will also need to include your client models in your application through
73
+ Bundler or some other method.
74
+
75
+ ### Rake Tasks
76
+
77
+ Add this to your `Rakefile`:
78
+
79
+ ```ruby
80
+ require 'daylight/tasks'
81
+ ```
82
+
83
+ This provides two rake tasks:
84
+
85
+ ````
86
+ rake doc:api:generate # Pre-generate the API documentation
87
+ rake doc:api:clean # Clear the API documentation
88
+ ````
89
+
90
+ The `doc:api:generate` tasks requests every page in the documentation so it can
91
+ be pre-cached. Make sure to run it in an environment where caching is turned
92
+ on:
93
+
94
+ ````ruby
95
+ config.action_controller.perform_caching = true
96
+ ````
97
+
98
+ ````
99
+ RAILS_ENV=production rake doc:api:generate
100
+ ````
101
+
102
+ The `doc:api:clean` tasks clears out the documentation cache directory.
103
+
104
+
105
+ ## Testing
106
+
107
+ You can use the supplied mock for [testing](testing.md) when developing your
108
+ API.
109
+
110
+ Add the following to your `test_helper.rb` or `spec_helper.rb`:
111
+
112
+ ````ruby
113
+ require 'daylight/mock'
114
+
115
+ Daylight::Mock.setup
116
+ ````
117
+
118
+ ## Console
119
+
120
+ When developing your API, you will want to ensure your aliases are also updated
121
+ when you `reload!` Add the following to an initializer on your server:
122
+
123
+ ````ruby
124
+ require 'daylight/client_reloader'
125
+ ````
126
+
127
+ More information about the [client reloader](develop.md#client-reloader) is
128
+ available in the development guide.
data/doc/principles.md ADDED
@@ -0,0 +1,42 @@
1
+ # Guiding Principles
2
+
3
+ Here are decisions we made when developing Daylight. We often referred back to these
4
+ to help us decide which approach to take. These are not hard-and-fast rules and can
5
+ be reviewed and changed as the need arises.
6
+
7
+ 1. Let Rails do all of the database work
8
+ * The client should require the least amount of data to peform the database task
9
+ * Avoid replicating `ActiveRecord` functionality in the client framework
10
+ * Improvements to Rails will benefit the client
11
+ * **Examples**: `through: :associations`, `where_values`, `nested_attributes`
12
+
13
+ 2. Maintain symantic URLs and formatted responses
14
+ * Symantic URLs and query parameters are logically grouped and executed without the client
15
+ * Axiomatically, DSL on the client-side is done through symantic URLs and query parameters
16
+ * Consistency on responses allows the client to _just work_ without adjustment
17
+ * Additions are expressed as additions to the formatted responses
18
+ * **Examples**: associations treated like nested routes, properly named root elements in data, metadata is supported
19
+
20
+ 3. The most granualar objects in responses represent models or their collections
21
+ * Return values only in the context of models (not literals like strings, integers etc.)
22
+ * These objects can be manipulated and saved
23
+ * Rails and `ActiveRecord` don't need to be patched to handle this edgecase
24
+ * **Examples**:associated routes, remote methods
25
+
26
+ 4. Expected behavior of dependent software _should_ not change when extended
27
+ * Developers should not be surprised by unexpected results from software they know and love
28
+ * Exception is to fix exposed problems in the underlying software or if the benefits are highly valueble
29
+ * Changes to the underlying software can be triggered through configuration
30
+ * **Examples**:`AutosaveAssociationFix`, `ActiveResource::Base#has_one`, `ReflectionExt`
31
+
32
+ 5. Extend dependent software (gems) by including changes using `ActiveSupport::Concerns`
33
+ * Concerns show in ancestor lists and can chain to original methods via `super`
34
+ * Extensions remain modular and may be extracted individually when needed
35
+ * **Examples**: `read_only`, `where_values` metadata on `Serializer`, `ResourceProxy`, `Refinements`
36
+
37
+ 6. Behavior driven tests drive towards integration testing
38
+ * Use `ActiveRecord` to do the work for retrieving models instead of mocking it out
39
+ * Server responses are a natural place to mock for the client
40
+ * Build models and factories on the fly for the backing test data
41
+ * **Examples**: `Daylight::Mock`, `MigrationHelper`
42
+
data/doc/testing.md ADDED
@@ -0,0 +1,107 @@
1
+ # API Testing
2
+
3
+ Daylight offers a simple mocking framework that simplifies the process of writing tests for your client code.
4
+
5
+ ## Daylight::Mock
6
+
7
+ Works with both Rspec and TestUnit/Minitest.
8
+
9
+ To start add this to your `test_helper.rb` or `spec_helper.rb`:
10
+
11
+ ```ruby
12
+ Daylight::Mock.setup
13
+ ```
14
+
15
+ The mock will simulate valid JSON responses to calls so you don't have to stub out anything, especially not the HTTP calls themselves.
16
+ At the end of the test you can examine the calls that were made by calling *daylight_mock*.
17
+
18
+ For example, say you want to test code that updates a User object.
19
+
20
+ ```ruby
21
+ user = API::V1::User.find(1)
22
+ user.title = 'Grand Poobah'
23
+ user.save
24
+ ```
25
+
26
+ This call to +daylight_mock+ returns a list of all the update calls made on a *User* object:
27
+
28
+ ```ruby
29
+ daylight_mock.updated(:user)
30
+ ```
31
+
32
+ To get only the last update use:
33
+
34
+ ```ruby
35
+ daylight_mock.last_updated(:user)
36
+ ```
37
+
38
+ Here's some things you can do with the recorded request:
39
+
40
+ ```ruby
41
+ mock = daylight_mock.last_updated(:user)
42
+
43
+ mock.post_data
44
+ #=> {"user"=>{"id"=>1, "title"=>"Grand Poobah"}}
45
+
46
+ mock.status
47
+ #=> 201
48
+ ```
49
+
50
+ Each recorded request keeps some data to check against:
51
+
52
+ | Attribute | Description |
53
+ |-----------------|---------------------------------------------------------------------------------------------------------- |
54
+ | resource | The resouce name |
55
+ | path_parts | A `Struct` of the request's path split into resource parts (`version`, `resource`, `id` and `associated`) |
56
+ | path | The request's path |
57
+ | response_body | `Daylight::Mock`'s response to the request |
58
+ | post_data | The request's POST data |
59
+ | params | The request's parsed parameters |
60
+ | action | The request's action (`:created`, `:updated`, `:associated`...) |
61
+ | status | Response status |
62
+ | target_object | The target object response if available (e.g. the response object to a `find(1)` call) |
63
+ | request | The raw request object |
64
+ | response | The raw response object |
65
+
66
+ Supported Calls: *created, updated, associated, indexed, shown, deleted*
67
+
68
+
69
+ #### More Examples
70
+
71
+ ```ruby
72
+ # assert the number of `Posts`
73
+ daylight_mock.updated(:post).count.must_equal 2
74
+
75
+ # assert that the `Post` has a title of "Grand Poobah"
76
+ daylight_mock.last_updated(:post).target_object.title.must_equal "Grand Poobah"
77
+
78
+ # assert that the `User` was created
79
+ daylight_mock.last_created(:user).status.must_equal 201
80
+ ```
81
+
82
+ ## API Integration Testing
83
+
84
+ If you are developing the Rails API and Client models, here is a technique for integration testing:
85
+
86
+ Keeping client and server code in sync is vitally important. It's all too easy for updates on the server to accidently break client integration. Any changes that are not backwards-compatible should induce a version change in the API. Though sometimes it is difficult to recognize when a change breaks some client endpoint.
87
+
88
+ This is where integration testing comes in. We want to write tests that call methods on the client models, hit the rails application and eventually call the database, then all the way back up the chain.
89
+
90
+ In the project that eventually spawned Daylight, we did this without spawning the rails application by using [Artifice](https://github.com/wycats/artifice). Artifice routes all Net::HTTP calls to a Rack application. Rails is a rack application! So now with one line of code, any call we make to our Daylight client library hits the Rails application without having to spin up a server.
91
+
92
+ In your +test_helper.rb+ or +spec_helper.rb+:
93
+
94
+ ```ruby
95
+ require 'artifice'
96
+ Artifice.activate_with(Example::Application)
97
+ ```
98
+
99
+ You applicaiton code must require your client models in your test suite.
100
+
101
+ For instance, we develop our client models alongside our Rails application and link them together using Bundler file path:
102
+
103
+ ```ruby
104
+ gem 'api', '0.0.1', path: '../api'
105
+ ```
106
+
107
+ Your your mileage may vary depending on how you develop in your environment.