farscape 1.2.0

Sign up to get free protection for your applications and to get access to all the features.
checksums.yaml ADDED
@@ -0,0 +1,7 @@
1
+ ---
2
+ SHA1:
3
+ metadata.gz: aaaa9b7314cc73ef0fd43a9de2e7f17f78999636
4
+ data.tar.gz: ff6f62f5301162b952821b23a80be847fac1627e
5
+ SHA512:
6
+ metadata.gz: 401dc3a255241d08565bc7067e6933b8450e48a97b87af028011e5600a41db720570731059486269daf4e95200c0f283cd5068666c90ff702699fa7de7e2022a
7
+ data.tar.gz: dbf7b5362bc58a66771ba67b8f8b822a17947eafc92ee5ca043019c3d2a76eb9907272919790b5dc23980e940f35f170e83f72724965509d19d20d2baf5ef5ef
data/CHANGELOG.md ADDED
@@ -0,0 +1,14 @@
1
+ # 1.2.0
2
+ * Add support for templated URIs and POST requests
3
+
4
+ # 1.1.2
5
+ * Prevent crash when an embedded resource is not an enumerable
6
+
7
+ # 1.1.1
8
+ * Add POST method support
9
+
10
+ # 1.1.0
11
+ * Update Faraday dependency to ~> 0.9
12
+
13
+ # 0.0.1
14
+ * Initial Release
data/CONTRIBUTING.md ADDED
@@ -0,0 +1,3 @@
1
+ # Contributing
2
+ Farscape is associated with the [Crichton Library](https://github.com/mdsol/crichton) and follows the same
3
+ [guidelines](https://github.com/mdsol/crichton/blob/develop/CONTRIBUTING.md).
data/Gemfile ADDED
@@ -0,0 +1,6 @@
1
+ source 'https://rubygems.org'
2
+
3
+ gemspec
4
+
5
+ gem 'crichton', git: 'https://github.com/mdsol-share/crichton.git', branch: 'develop'
6
+ gem 'moya', git: 'https://github.com/mdsol/moya.git', branch: 'develop'
data/Gemfile.lock ADDED
@@ -0,0 +1,192 @@
1
+ GIT
2
+ remote: git@github.com:mdsol/representors.git
3
+ revision: 958dc778d62897f09dd0200f3690f96cb5265b98
4
+ branch: 0-0-stable
5
+ specs:
6
+ representors (0.0.3)
7
+ addressable (~> 2.3)
8
+ rake
9
+
10
+ GIT
11
+ remote: https://www.github.com/mdsol-share/crichton.git
12
+ revision: 63c74ce781a41538454b81fd8c44dc36117aa92b
13
+ branch: develop
14
+ specs:
15
+ crichton (0.1.0)
16
+ activesupport (>= 3.2.0)
17
+ addressable (~> 2.3.0)
18
+ builder (>= 3.0.0)
19
+ colorize (~> 0.6.0)
20
+ dice_bag (~> 0.8)
21
+ diffy (~> 3.0.1)
22
+ i18n (>= 0.6.5)
23
+ nokogiri (>= 1.6.0)
24
+ rake
25
+
26
+ GIT
27
+ remote: https://www.github.com/mdsol/moya.git
28
+ revision: 6e8b324dee6335e5d56d320ba0a605b6ebae8574
29
+ branch: develop
30
+ specs:
31
+ moya (1.0.0)
32
+ activeuuid
33
+ launchy
34
+ nokogiri
35
+ rack
36
+ rails (= 4.1.8)
37
+ sqlite3
38
+ yajl-ruby (~> 1.2.0)
39
+
40
+ PATH
41
+ remote: .
42
+ specs:
43
+ farscape (1.0.1)
44
+ activesupport
45
+ addressable (~> 2.3.0)
46
+ dice_bag
47
+ faraday (~> 0.8.8)
48
+ faraday_middleware (~> 0.9)
49
+ rake
50
+
51
+ GEM
52
+ remote: https://rubygems.org/
53
+ specs:
54
+ actionmailer (4.1.8)
55
+ actionpack (= 4.1.8)
56
+ actionview (= 4.1.8)
57
+ mail (~> 2.5, >= 2.5.4)
58
+ actionpack (4.1.8)
59
+ actionview (= 4.1.8)
60
+ activesupport (= 4.1.8)
61
+ rack (~> 1.5.2)
62
+ rack-test (~> 0.6.2)
63
+ actionview (4.1.8)
64
+ activesupport (= 4.1.8)
65
+ builder (~> 3.1)
66
+ erubis (~> 2.7.0)
67
+ activemodel (4.1.8)
68
+ activesupport (= 4.1.8)
69
+ builder (~> 3.1)
70
+ activerecord (4.1.8)
71
+ activemodel (= 4.1.8)
72
+ activesupport (= 4.1.8)
73
+ arel (~> 5.0.0)
74
+ activesupport (4.1.8)
75
+ i18n (~> 0.6, >= 0.6.9)
76
+ json (~> 1.7, >= 1.7.7)
77
+ minitest (~> 5.1)
78
+ thread_safe (~> 0.1)
79
+ tzinfo (~> 1.1)
80
+ activeuuid (0.6.0)
81
+ activerecord (>= 3.1)
82
+ uuidtools
83
+ addressable (2.3.7)
84
+ arel (5.0.1.20140414130214)
85
+ awesome_print (1.1.0)
86
+ builder (3.2.2)
87
+ coderay (1.1.0)
88
+ colorize (0.6.0)
89
+ crack (0.4.2)
90
+ safe_yaml (~> 1.0.0)
91
+ dice_bag (0.8.0)
92
+ rake
93
+ diff-lcs (1.2.5)
94
+ diffy (3.0.7)
95
+ docile (1.1.5)
96
+ erubis (2.7.0)
97
+ faraday (0.8.9)
98
+ multipart-post (~> 1.2.0)
99
+ faraday_middleware (0.9.1)
100
+ faraday (>= 0.7.4, < 0.10)
101
+ hike (1.2.3)
102
+ i18n (0.7.0)
103
+ json (1.8.2)
104
+ launchy (2.4.3)
105
+ addressable (~> 2.3)
106
+ mail (2.6.3)
107
+ mime-types (>= 1.16, < 3)
108
+ method_source (0.8.2)
109
+ mime-types (2.4.3)
110
+ mini_portile (0.6.2)
111
+ minitest (5.5.1)
112
+ multi_json (1.11.0)
113
+ multipart-post (1.2.0)
114
+ nokogiri (1.6.6.2)
115
+ mini_portile (~> 0.6.0)
116
+ pry (0.10.1)
117
+ coderay (~> 1.1.0)
118
+ method_source (~> 0.8.1)
119
+ slop (~> 3.4)
120
+ rack (1.5.2)
121
+ rack-test (0.6.3)
122
+ rack (>= 1.0)
123
+ rails (4.1.8)
124
+ actionmailer (= 4.1.8)
125
+ actionpack (= 4.1.8)
126
+ actionview (= 4.1.8)
127
+ activemodel (= 4.1.8)
128
+ activerecord (= 4.1.8)
129
+ activesupport (= 4.1.8)
130
+ bundler (>= 1.3.0, < 2.0)
131
+ railties (= 4.1.8)
132
+ sprockets-rails (~> 2.0)
133
+ railties (4.1.8)
134
+ actionpack (= 4.1.8)
135
+ activesupport (= 4.1.8)
136
+ rake (>= 0.8.7)
137
+ thor (>= 0.18.1, < 2.0)
138
+ rake (0.9.6)
139
+ redcarpet (3.2.2)
140
+ rspec (2.99.0)
141
+ rspec-core (~> 2.99.0)
142
+ rspec-expectations (~> 2.99.0)
143
+ rspec-mocks (~> 2.99.0)
144
+ rspec-core (2.99.2)
145
+ rspec-expectations (2.99.2)
146
+ diff-lcs (>= 1.1.3, < 2.0)
147
+ rspec-mocks (2.99.3)
148
+ safe_yaml (1.0.4)
149
+ simplecov (0.9.2)
150
+ docile (~> 1.1.0)
151
+ multi_json (~> 1.0)
152
+ simplecov-html (~> 0.9.0)
153
+ simplecov-html (0.9.0)
154
+ slop (3.6.0)
155
+ sprockets (2.12.3)
156
+ hike (~> 1.2)
157
+ multi_json (~> 1.0)
158
+ rack (~> 1.0)
159
+ tilt (~> 1.1, != 1.3.0)
160
+ sprockets-rails (2.2.4)
161
+ actionpack (>= 3.0)
162
+ activesupport (>= 3.0)
163
+ sprockets (>= 2.8, < 4.0)
164
+ sqlite3 (1.3.10)
165
+ thor (0.19.1)
166
+ thread_safe (0.3.5)
167
+ tilt (1.4.1)
168
+ tzinfo (1.2.2)
169
+ thread_safe (~> 0.1)
170
+ uuidtools (2.1.5)
171
+ webmock (1.13.0)
172
+ addressable (>= 2.2.7)
173
+ crack (>= 0.3.2)
174
+ yajl-ruby (1.2.1)
175
+ yard (0.8.7.6)
176
+
177
+ PLATFORMS
178
+ ruby
179
+
180
+ DEPENDENCIES
181
+ awesome_print (~> 1.1.0)
182
+ crichton!
183
+ farscape!
184
+ moya!
185
+ pry
186
+ rake (~> 0.9)
187
+ redcarpet
188
+ representors!
189
+ rspec (~> 2.14)
190
+ simplecov (~> 0.7)
191
+ webmock (~> 1.13.0)
192
+ yard (~> 0.8.5)
data/LICENSE.md ADDED
@@ -0,0 +1,19 @@
1
+ Copyright (c) 2013 Medidata Solutions Worldwide
2
+
3
+ Permission is hereby granted, free of charge, to any person obtaining a copy
4
+ of this software and associated documentation files (the "Software"), to deal
5
+ in the Software without restriction, including without limitation the rights
6
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
7
+ copies of the Software, and to permit persons to whom the Software is
8
+ furnished to do so, subject to the following conditions:
9
+
10
+ The above copyright notice and this permission notice shall be included in
11
+ all copies or substantial portions of the Software.
12
+
13
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
14
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
15
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
16
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
17
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
18
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
19
+ THE SOFTWARE.
data/README.md ADDED
@@ -0,0 +1,189 @@
1
+ # Farscape
2
+ [![Build Status](https://travis-ci.org/mdsol/farscape.svg)](https://travis-ci.org/mdsol/farscape)
3
+
4
+ Farscape is a hypermedia agent that simplifies consuming Hypermedia API responses. It shoots through wormholes with
5
+ [Crichton](https://github.com/mdsol/crichton) at the helm and takes you to unknown places in the universe!
6
+
7
+ Checkout the [Documentation][] for more info.
8
+
9
+ NOTE: THIS IS UNDER HEAVY DEV AND IS NOT READY TO BE USED YET
10
+
11
+
12
+ ## API Entry
13
+ There are various flavors of configuration that Farscape supports for entering a Hypermedia API. These all assume
14
+ a response with a supported Hypermedia media-type and a root that lists available resources as links.
15
+
16
+ ### A Hypermedia API
17
+ For a interacting with an API (or individual service that supports a list of resources at its root), you enter the
18
+ API and follow your nose using the `enter` method on the agent. This method returns a [Farscape::Representor]()
19
+ instance with a simple state-machine interface of `attributes` (data) and `transitions` (link/form affordances) for
20
+ interacting with the resource representations.
21
+
22
+ ```ruby
23
+ agent = Farscape::Agent.new('http://example.com/my_api')
24
+
25
+ resources = agent.enter
26
+ resources.attributes # => { meta: 'data', or: 'other data' }
27
+ resources.transitions.keys # => ['http://example.com/rel/drds', 'http://example.com/rel/leviathans']
28
+ ```
29
+
30
+ ### A Hypermedia Discovery Service
31
+ For interacting with a discovery service, Farscape supports follow your nose entry to select a registered resource
32
+ or immediately loading a discoverable resource if known to be registered in the service *a priori*.
33
+
34
+ ```ruby
35
+ agent = Farscape::Agent.new('http://example.com/my_discovery_service')
36
+
37
+ resources = agent.enter
38
+ resources.attributes # => { meta: 'data', or: 'other data' }
39
+ resources.transitions.keys # => ['http://example.com/rel/drds', 'http://example.com/rel/leviathans', 'next', 'last']
40
+
41
+ drds = agent.enter('http://example.com/rel/drds')
42
+ drds.attributes # => { total_count: 25, items: [...] }
43
+ drds.transitions.keys # => ['self', 'search', 'create', 'next', 'last']
44
+
45
+ agent.enter('http://example.com/rel/unknown_resource') # => raises Farscape::Agent::UnknownEntryPoint
46
+ ```
47
+
48
+ ## API Interaction
49
+ Entering an API takes you into its application state-machine and, as such, the interface for interacting with that
50
+ application state is brain dead simple with Farscape. You have data that you read and hypermedia affordances that tell
51
+ you what you can do next and you can invoke those affordances to do things. That's it.
52
+
53
+ Farscape recognizes a number of media-types that support runtime knowledge of the underlying REST uniform-interface
54
+ methods. For these full-featured media-types, the interaction with with resources is as simple as a browser where
55
+ implementation of requests is completely abstracted from the user.
56
+
57
+ The following simple examples highlight interacting with resource state-machines using Farscape.
58
+
59
+ ### Load a resource
60
+ ```ruby
61
+ resources = agent.enter
62
+ drds_transition = resources.transitions['http://example.com/rel/drds']
63
+ drds = drds_transition.invoke
64
+ ```
65
+
66
+ ### Reload a resource
67
+ ```ruby
68
+ self_transition = drds.transitions['self']
69
+ reloaded_drds = self_transition.invoke
70
+ ```
71
+
72
+ ### Explore
73
+
74
+ The sample code given below often depicts the client making *assumptions* that a specific transition or attribute will be available in a certain state. *This is unsafe*, and production code should include conditionals or rescues for the case when an assumption proves incorrect. Whenever possible, Farscape should be used more dynamically, by letting user interaction or a crawling algorithm drive transitions.
75
+
76
+ ### Apply query parameters
77
+ ```ruby
78
+ search_transition = drds.transitions['search']
79
+ search_transition.parameters # => ['search_term']
80
+
81
+ filtered_drds = search_transition.invoke do |builder|
82
+ builder.parameters = { search_term: '1812' }
83
+ end
84
+ ```
85
+
86
+ You may also invoke transitions with automatic attribute and parameter matching
87
+ ```ruby
88
+ drds.transitions['search'].invoke(search_term: '1812')
89
+ ```
90
+
91
+ ### Transform resource state
92
+ ```ruby
93
+ embedded_drd_items = drds.items
94
+
95
+ drd = embedded_drd_items.first
96
+ drd.attributes # => { name: '1812' }
97
+ drd.transitions # => ['self', 'edit', 'delete', 'deactivate', 'leviathan']
98
+
99
+ deactivate_transition = drd.transitions['deactivate']
100
+
101
+ deactivated_drd = deactivate_transition.invoke
102
+ deactivated_drd.attributes # => { name: '1812' }
103
+ deactivated_drd.transitions # => ['self', 'activate', 'leviathan']
104
+
105
+ deactivate_transition.invoke # => raise Farscape::Excpetions::Gone error
106
+ ```
107
+
108
+ ### Transform application state
109
+ ```ruby
110
+ leviathan_transition = deactivated_drd.transitions['leviathan']
111
+
112
+ leviathan = leviathan_transition.invoke
113
+ leviathan.attributes # => { name: 'Elack' }
114
+ leviathan.transitions # => ['self', 'drds']
115
+ ```
116
+
117
+ ### Use attributes
118
+
119
+ ```ruby
120
+ create_transition = drds.transitions['create']
121
+ create_transition.attributes # => ['name']
122
+
123
+ new_drd = create_transition.invoke do |builder|
124
+ builder.attributes = { name: 'Pike' }
125
+ end
126
+
127
+ new_drd.attributes # => { name: 'Pike' }
128
+ new_drd.transitions.keys # => ['self', 'edit', 'delete', 'deactivate', 'leviathan']
129
+ ```
130
+
131
+ For more examples and information on using Faraday with media-types that require specifying uniform-interface methods
132
+ and other protocol idioms when invoking transitions, see [Using Farscape]().
133
+
134
+ ## Alternate Interface
135
+
136
+ For developers more used to ActiveRecord syntax, Farscape resources also expose all transitions and attributes as Ruby methods. Safe (i.e. read) transitions are exposed verbatim.
137
+
138
+ ```ruby
139
+ drd.leviathan # => Equivalent to drd.transitions['leviathan'].invoke
140
+ ```
141
+
142
+ Unsafe transitions have an exclamation point at the end.
143
+
144
+ ```ruby
145
+ drd.deactivate # => Raises NoMethodError
146
+
147
+ drd.deactivate! # => Equivalent to drd = drd.transitions['deactivate'].invoke
148
+ ```
149
+
150
+ Request parameters can be passed as a hash or as a block.
151
+
152
+ ```ruby
153
+ # The following are all equivalent:
154
+
155
+ drd = drds.create!(name: 'Pike')
156
+ drd = drds.create! { |builder| builder.attributes = {name: 'Pike'} }
157
+ drd = drds.transitions['create'].invoke{ |d| d.attributes = {name: 'Pike'} }
158
+ ```
159
+
160
+ Attributes are read-only.
161
+
162
+ ```ruby
163
+ drd.name # => "Pike"
164
+
165
+ drd.name = 'Susan' # => Raises NoMethodError
166
+ ```
167
+
168
+ If an attribute or transition's name conflicts with an existing method or reserved word, it will not be methodized and must be accessed through the hash interface.
169
+
170
+ ### Disabling the Alternate Interface
171
+ If you're concerned about namespace collisions, or want to ensure that your code is highly flexible and explicit (albeit verbose), you may turn off the interface with the .safe method.
172
+ ```ruby
173
+ safe_drd = drd.safe # => returns a drd resource without the alternate interface
174
+ safe_drd.name # => UndefinedMethod error
175
+ drd.name # => "Pike"
176
+ ```
177
+
178
+ You may reenable the alternate interface with `.unsafe`.
179
+
180
+ ## Contributing
181
+ See [CONTRIBUTING][] for details.
182
+
183
+ ## Copyright
184
+ Copyright (c) 2013 Medidata Solutions Worldwide. See [LICENSE][] for details.
185
+
186
+ [Crichton]: https://github.com/mdsol/crichton
187
+ [CONTRIBUTING]: CONTRIBUTING.md
188
+ [Documentation]: http://rubydoc.info/github/mdsol/farscape/develop/file/README.md
189
+ [LICENSE]: LICENSE.md
data/Rakefile ADDED
@@ -0,0 +1,10 @@
1
+ lib_dir = File.expand_path('../lib', __FILE__)
2
+ $LOAD_PATH.unshift(lib_dir)
3
+ $LOAD_PATH.uniq!
4
+
5
+ require 'rubygems'
6
+ require 'rake'
7
+ require 'dice_bag/tasks'
8
+ require 'farscape'
9
+
10
+ Dir['tasks/**/*.rake'].each { |rake| load rake }
@@ -0,0 +1,162 @@
1
+ # Writing Plugins
2
+
3
+ Farscape Plugins can be used to add drop-in functionality to Farscape.
4
+
5
+ ## Farscape Plugin Hash
6
+
7
+ A Farscape plugin is described with a hash with a set of well defined keys
8
+
9
+ #### name
10
+ This is a symbol representing the name of the plugin
11
+
12
+ #### type
13
+ The type of plugin, this is useful for manipulating multiple plguins simultaneously
14
+
15
+ #### middleware
16
+ Middleware objects as per [Faraday plugins](https://github.com/lostisland/faraday#writing-middleware)
17
+
18
+ #### extensions
19
+ A hash whos keys are the Farscape clases to be extended and values are a list of classes which will extend the default functionality of Farscape clases.
20
+ Possible keys are: `:Agent`, `:HttpClient`, `:SafeRepresentorAgent`, `:RepresentorAgent`, and `:TransitionAgent`
21
+
22
+ #### default_state
23
+ When registered, is this :enabled or :disabled by default it is :enabled
24
+
25
+ ### Example
26
+ ```ruby
27
+ {
28
+ name: :PluginName
29
+ type: :example_type
30
+ middleware: PluginMiddlewareClass
31
+ extension: { :Agent => [ExtenstionClass], :RepresentorAgent => [ExtensionClass, Walrus] }
32
+ default_state: :enabled
33
+ }
34
+ ```
35
+
36
+ # Registration
37
+
38
+ To register your plugin, run
39
+
40
+ ```ruby
41
+ Farscape.register_plugin(name: :Cachinator, type: :cache, ...)
42
+ ```
43
+
44
+ Feel free to put this code at the bottom of `cachinator.rb` so that it turns on automatically after the gem is loaded. Consumers who want control can include a line in their initializer reading `Farscape.disable!(:Cachinator)` or `Farscape.disable!(:cache)`. If you'd rather have your plugin be off by default, you could instead wrap the register_plugin call in, say, a `Cachinator.activate` method for the consumer to call as desired. If your plugin is http-specific (say it adds Authentication headers), include `protocol: :http`.
45
+
46
+ # Managing Plugins
47
+ Of the set of registered plugins, some will be enabled, and others disabled. Enabled plugins will apply their specified functionality to any current Farscape instance.
48
+
49
+ ## Introspecting plguins
50
+
51
+ You can get a list of plugins with the `Farscape.plugins` method. If you want only the enabled plugins you can use `Farscape.enabled_plugins`, and if you want only disabled plguins you can do `Farscape.disabled_plugins`.
52
+
53
+ ## Enabling and Disabling plugins
54
+
55
+ The default state of a plugin is defined by the Plugin Hash. To enable a disabled plugin you need to call `Farscape.enable!(options)` where options is a hash containing a `:name` key or a `:type` key. The name key will only enable plugins with the matching name, whereas type will enable all plugins of that type.
56
+ Likewise you can disable plugins with `Farscape.disable!(options)`, in which case you disable the plugins.
57
+
58
+ ## Example Enable / Disable Workflow
59
+
60
+ ```ruby
61
+ Farscape.register_plugin({name: :Peacekeeper, type: :sebacean, middleware: [TestMiddleware::NoGetNoProblem]})
62
+ Farscape.plugins #=> [{name: :Peacekeeper, type: :sebacean, middleware: [TestMiddleware::NoGetNoProblem]}]
63
+ Farscape.enabled_plugins #=> [{name: :Peacekeeper, type: :sebacean, middleware: [TestMiddleware::NoGetNoProblem]}]
64
+ Farscape.disabled_plugins #=> []
65
+ Farscape.disable!(type: :sebacean)
66
+ Farscape.enabled_plugins #=> []
67
+ Farscape.disabled_plugins #=> [{name: :Peacekeeper, type: :sebacean, middleware: [TestMiddleware::NoGetNoProblem]}]
68
+ Farscape.enable!(name: :Peacekeeper)
69
+ Farscape.enabled_plugins #=> [{name: :Peacekeeper, type: :sebacean, middleware: [TestMiddleware::NoGetNoProblem]}]
70
+ Farscape.disabled_plugins #=> []
71
+ ```
72
+
73
+ ## Plugins on instances
74
+
75
+ Plugins can also be modified on a per instance basis. Agent, SafeRepresentorAgent, and Representor all support #plugins, #enabled_plugins, and #disabled_plugins on a per instance basis. These behave the same as their associated Farscape class methods, except they communicate which plugins are enabled or diables for that instance only.
76
+ When an object creates a new object (for example `Agent.new(entry).enter` returns a RepresentorAgent), those instances will have the same plugins enabled as their parent.
77
+
78
+ ### Enabling and Disabling Plugins for Instances
79
+
80
+ To enable plugins on an instance invoke the `using(name_or_type)` method, to disable plugins on an instance invoke the `omitting(name_or_type)` method. These methods will return a new instance with the associated plugin(s) in their new state. They do not modify the original object.
81
+
82
+ ### Example instance workflow
83
+
84
+ ```ruby
85
+ Farscape.enabled_plugins #=> [{name: :Peacekeeper, type: :sebacean, middleware: [TestMiddleware::NoGetNoProblem]}]
86
+ agent = Farscape.Agent.new.omitting(name: :Peacekeeper)
87
+ agent.plugins # => [{name: :Peacekeeper, type: :sebacean, middleware: [TestMiddleware::NoGetNoProblem]}]
88
+ agent.enabled_plugins # => []
89
+ agent.plugins # => [{name: :Peacekeeper, type: :sebacean, middleware: [TestMiddleware::NoGetNoProblem]}]
90
+ resource = agent.enter(entry_point).transitions[:listing].invoke
91
+ resource.plugins # => [{name: :Peacekeeper, type: :sebacean, middleware: [TestMiddleware::NoGetNoProblem]}]
92
+ resource.enabled_plugins # => []
93
+ resource.plugins # => [{name: :Peacekeeper, type: :sebacean, middleware: [TestMiddleware::NoGetNoProblem]}]
94
+ details = resource.using(name: :Peacekeeper).transitions[:items][0].invoke
95
+ details.plugins # => [{name: :Peacekeeper, type: :sebacean, middleware: [TestMiddleware::NoGetNoProblem]}]
96
+ details.enabled_plugins # => []
97
+ details.disabled_plugins # => [{name: :Peacekeeper, type: :sebacean, middleware: [TestMiddleware::NoGetNoProblem]}]
98
+ ```
99
+
100
+ # Adding Middleware
101
+
102
+ You can probably do what you need to do by writing [Faraday](https://github.com/lostisland/faraday)-style middleware. Middleware can inspect and alter outgoing requests and incoming responses, abort requests, and define hooks that run after a request/response cycle completes. All it needs to do is obey [a simple API](https://github.com/lostisland/faraday#writing-middleware).
103
+
104
+ To add your middleware to the stack, run
105
+
106
+ ```ruby
107
+ Farscape.register_plugin(name: :Cachinator, type: :cache, middleware: [Cachinator::Middleware], ...)
108
+ ```
109
+
110
+ If you need to partially order your middleware, the elements of the middleware array can be hashes of the form:
111
+
112
+ ```ruby
113
+ { class: Cachinator::Middleware,
114
+ before: RequestSigner,
115
+ after: ['HubSubscriber', 'Ouroborous', :authorization]
116
+ }
117
+ ```
118
+
119
+ In this example, Cachinator::Middleware will be inserted before the RequestSigner middleware if it is present, and after the latest of HubSubscriber, Ouroborous, or any middleware of type "authorization". Note that Middleware classes can and should be given as strings if your plugin is not providing them, so that Ruby won't throw a NameError if they are undefined. If the ordering constraints given are impossible to satisfy, Farscape will throw an error.
120
+
121
+ You can also add config to your middleware when passing it in hash form:
122
+
123
+ ```ruby
124
+ { class: Cachinator::Middleware,
125
+ config: MyApp.config[:cache]
126
+ }
127
+ ```
128
+
129
+ The config hash will be passed to your middleware as a second argument to `new`, as in Faraday. To pass multiple arguments, use `config: [arg1, arg2]`.
130
+
131
+ # Extending Agent
132
+
133
+ If you want to provide a more interactive API, or reference the deserialized response using the Representor interface, you can define a module that will be available to mix in to Farscape::Agent.
134
+
135
+ ```ruby
136
+ module Peacekeeper
137
+ def pacify!
138
+ raise if representor.transitions.keys.include?(:attack)
139
+ end
140
+ end
141
+ Farscape.register_plugin(name: :Peacekeeper, type: :security, extensions: [Peacekeeper], extends: [:Agent])
142
+ agent.enter(url).using(:Peacekeeper).pacify!
143
+ ```
144
+
145
+ # Farscape Utilities
146
+
147
+ Any plugin can reference `Farscape.cache`, which exposes [the same API as Rails.cache](http://apidock.com/rails/ActiveSupport/Cache/Store), `Farscape.logger`, which exposes [the same API as the built-in Ruby logger](http://apidock.com/ruby/Logger). By default, Farscape.cache operates in-memory and Farscape.logger writes to STDOUT. Plugins can provide enhanced versions of these utilities by modifying the global state of the Farscape object:
148
+
149
+ ```ruby
150
+ module Peacekeeper
151
+ class DalliCache
152
+ # code that implements the Cache api
153
+ end
154
+ end
155
+ Farscape.cache = Peacekeeper::DalliCache.new(config)
156
+ ```
157
+
158
+ Future updates will provide Farscape.jobs, a backgrounding utility along similar lines.
159
+
160
+ # Creating a Client
161
+
162
+ By default, Farscape uses the [Net::HTTP](http://ruby-doc.org/stdlib-2.1.5/libdoc/net/http/rdoc/Net/HTTP.html) library to make HTTP requests. You can replace this client with `Faraday.clients[:http] = MyClient` or define one for a new protocol with `Faraday.clients[:amqp] = Jessica::Rabbit`. When a Farscape agent follows a link with a given protocol, it will use the client for that protocol if one has been provided. Required interface tk.