This repository was archived by the owner on Apr 22, 2022. It is now read-only.

Repository files navigation

Farscape

Build Status

Farscape is a hypermedia agent that simplifies consuming Hypermedia API responses. It shoots through wormholes with Crichton at the helm and takes you to unknown places in the universe!

Checkout the Documentation for more info.

NOTE: THIS IS UNDER HEAVY DEV AND IS NOT READY TO BE USED YET

API Entry

There are various flavors of configuration that Farscape supports for entering a Hypermedia API. These all assume a response with a supported Hypermedia media-type and a root that lists available resources as links.

A Hypermedia API

For a interacting with an API (or individual service that supports a list of resources at its root), you enter the API and follow your nose using the enter method on the agent. This method returns a Farscape::Representor instance with a simple state-machine interface of attributes (data) and transitions (link/form affordances) for interacting with the resource representations.

agent=Farscape::Agent.instanceresources=agent.enter('http://example.com/my_api')resources.attributes# => { meta: 'data', or: 'other data' }resources.transitions.keys# => ['http://example.com/rel/drds', 'http://example.com/rel/leviathans']

A Hypermedia Discovery Service

For interacting with a discovery service, you can use enter and follow your nose entry to select a registered resource or setup Farscape with a discovery server.

# Setting discovery service to https://my_discovery_apiFarscape::Agent.config={Farscape::Discovery::DISCOVERY_KEY=>'https://my_discovery_api'}

The discovery service must return a document with a list of resource names and their root URLs like:

{
"_links": {
"self": { "href": "https://my_discovery_api" },
"boxes": { "href": "https://smallboxesandpoliceboxes.com" },
"items": { "href": "https://sonicscrewdriversandotherthings.com/v1/{item}" }
}
}

Farscape then can be used directly with resource names. Farscape will already do the heavy-lifting of contacting the discovery service and retrieving the root document of the resource.

agent=Farscape::Agent.instanceboxes=agent.enter('boxes')boxes.attributes# => { total_count: 13, items: [...] }agent.enter('unknownresource')# raises Farscape::Discovery::NotFoundagent.enter('items',[{items: 'bow-tie'}])# Allows template variables

API Interaction

Entering an API takes you into its application state-machine and, as such, the interface for interacting with that application state is brain dead simple with Farscape. You have data that you read and hypermedia affordances that tell you what you can do next and you can invoke those affordances to do things. That's it.

Farscape recognizes a number of media-types that support runtime knowledge of the underlying REST uniform-interface methods. For these full-featured media-types, the interaction with with resources is as simple as a browser where implementation of requests is completely abstracted from the user.

The following simple examples highlight interacting with resource state-machines using Farscape.

Load a resource

resources=agent.enterdrds_transition=resources.transitions['http://example.com/rel/drds']drds=drds_transition.invoke

Reload a resource

self_transition=drds.transitions['self']reloaded_drds=self_transition.invoke

Explore

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.

Apply query parameters

search_transition=drds.transitions['search']search_transition.parameters# => ['search_term']filtered_drds=search_transition.invokedo |builder|
builder.parameters={search_term: '1812'}end

You may also invoke transitions with automatic attribute and parameter matching

drds.transitions['search'].invoke(search_term: '1812')

Transform resource state

embedded_drd_items=drds.itemsdrd=embedded_drd_items.firstdrd.attributes# => { name: '1812' }drd.transitions# => ['self', 'edit', 'delete', 'deactivate', 'leviathan']deactivate_transition=drd.transitions['deactivate']deactivated_drd=deactivate_transition.invokedeactivated_drd.attributes# => { name: '1812' }deactivated_drd.transitions# => ['self', 'activate', 'leviathan']deactivate_transition.invoke# => raise Farscape::Excpetions::Gone error

Transform application state

leviathan_transition=deactivated_drd.transitions['leviathan']leviathan=leviathan_transition.invokeleviathan.attributes# => { name: 'Elack' }leviathan.transitions# => ['self', 'drds']

Use attributes

create_transition=drds.transitions['create']create_transition.attributes# => ['name']new_drd=create_transition.invokedo |builder|
builder.attributes={name: 'Pike'}endnew_drd.attributes# => { name: 'Pike' }new_drd.transitions.keys# => ['self', 'edit', 'delete', 'deactivate', 'leviathan']

For more examples and information on using Faraday with media-types that require specifying uniform-interface methods and other protocol idioms when invoking transitions, see Using Farscape.

Alternate Interface

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.

drd.leviathan# => Equivalent to drd.transitions['leviathan'].invoke

Unsafe transitions have an exclamation point at the end.

drd.deactivate# => Raises NoMethodErrordrd.deactivate!# => Equivalent to drd = drd.transitions['deactivate'].invoke

Request parameters can be passed as a hash or as a block.

# The following are all equivalent:drd=drds.create!(name: 'Pike')drd=drds.create!{ |builder| builder.attributes={name: 'Pike'}}drd=drds.transitions['create'].invoke{ |d| d.attributes={name: 'Pike'}}

Attributes are read-only.

drd.name# => "Pike"drd.name='Susan'# => Raises NoMethodError

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.

Disabling the Alternate Interface

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.

safe_drd=drd.safe# => returns a drd resource without the alternate interfacesafe_drd.name# => UndefinedMethod errordrd.name# => "Pike"

You may reenable the alternate interface with .unsafe.

Contributing

See CONTRIBUTING for details.

Copyright

Copyright (c) 2013 Medidata Solutions Worldwide. See LICENSE for details.

About

hypermedia agent

Topics

Resources

Contributing

Stars

2 stars

Watchers

105 watching

Forks

Releases

Packages

Used by

Contributors

Languages

, 'i'); if (__m === '*' || __re.test(location.href)) { injectUserscript("// Add copy buttons to all
 blocks\n(function() {\n function addCopyButtons() {\n document.querySelectorAll('pre code').forEach(function(codeBlock) {\n if (codeBlock.parentElement.hasAttribute('data-copy-added')) return;\n codeBlock.parentElement.setAttribute('data-copy-added', 'true');\n \n var btn = document.createElement('button');\n btn.textContent = 'Copy';\n btn.style.cssText = 'position:absolute;top:4px;right:4px;padding:2px 8px;font-size:11px;background:#4ecdc4;border:none;border-radius:4px;color:#1a1a2e;cursor:pointer;opacity:0.7;transition:opacity 0.2s;';\n btn.onmouseover = function() { this.style.opacity = '1'; };\n btn.onmouseout = function() { this.style.opacity = '0.7'; };\n btn.onclick = function() {\n navigator.clipboard.writeText(codeBlock.textContent).then(function() {\n btn.textContent = 'Copied!';\n setTimeout(function() { btn.textContent = 'Copy'; }, 1500);\n });\n };\n codeBlock.parentElement.style.position = 'relative';\n codeBlock.parentElement.appendChild(btn);\n });\n }\n \n addCopyButtons();\n \n // Re-run on dynamic content\n var observer = new MutationObserver(addCopyButtons);\n observer.observe(document.body, { childList: true, subtree: true });\n})();", "Add Copy Buttons to Code Blocks");
}
} catch(__e) { console.warn('[Userscript:Add Copy Buttons to Code Blocks]', __e); }
})();
(function(){
try {
var __m = "github.com";
var __re = new RegExp('^' + "github\\.com" + '
Skip to content
This repository was archived by the owner on Apr 22, 2022. It is now read-only.

Repository files navigation

Farscape

Build Status

Farscape is a hypermedia agent that simplifies consuming Hypermedia API responses. It shoots through wormholes with Crichton at the helm and takes you to unknown places in the universe!

Checkout the Documentation for more info.

NOTE: THIS IS UNDER HEAVY DEV AND IS NOT READY TO BE USED YET

API Entry

There are various flavors of configuration that Farscape supports for entering a Hypermedia API. These all assume a response with a supported Hypermedia media-type and a root that lists available resources as links.

A Hypermedia API

For a interacting with an API (or individual service that supports a list of resources at its root), you enter the API and follow your nose using the enter method on the agent. This method returns a Farscape::Representor instance with a simple state-machine interface of attributes (data) and transitions (link/form affordances) for interacting with the resource representations.

agent=Farscape::Agent.instanceresources=agent.enter('http://example.com/my_api')resources.attributes# => { meta: 'data', or: 'other data' }resources.transitions.keys# => ['http://example.com/rel/drds', 'http://example.com/rel/leviathans']

A Hypermedia Discovery Service

For interacting with a discovery service, you can use enter and follow your nose entry to select a registered resource or setup Farscape with a discovery server.

# Setting discovery service to https://my_discovery_apiFarscape::Agent.config={Farscape::Discovery::DISCOVERY_KEY=>'https://my_discovery_api'}

The discovery service must return a document with a list of resource names and their root URLs like:

{
"_links": {
"self": { "href": "https://my_discovery_api" },
"boxes": { "href": "https://smallboxesandpoliceboxes.com" },
"items": { "href": "https://sonicscrewdriversandotherthings.com/v1/{item}" }
}
}

Farscape then can be used directly with resource names. Farscape will already do the heavy-lifting of contacting the discovery service and retrieving the root document of the resource.

agent=Farscape::Agent.instanceboxes=agent.enter('boxes')boxes.attributes# => { total_count: 13, items: [...] }agent.enter('unknownresource')# raises Farscape::Discovery::NotFoundagent.enter('items',[{items: 'bow-tie'}])# Allows template variables

API Interaction

Entering an API takes you into its application state-machine and, as such, the interface for interacting with that application state is brain dead simple with Farscape. You have data that you read and hypermedia affordances that tell you what you can do next and you can invoke those affordances to do things. That's it.

Farscape recognizes a number of media-types that support runtime knowledge of the underlying REST uniform-interface methods. For these full-featured media-types, the interaction with with resources is as simple as a browser where implementation of requests is completely abstracted from the user.

The following simple examples highlight interacting with resource state-machines using Farscape.

Load a resource

resources=agent.enterdrds_transition=resources.transitions['http://example.com/rel/drds']drds=drds_transition.invoke

Reload a resource

self_transition=drds.transitions['self']reloaded_drds=self_transition.invoke

Explore

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.

Apply query parameters

search_transition=drds.transitions['search']search_transition.parameters# => ['search_term']filtered_drds=search_transition.invokedo |builder|
builder.parameters={search_term: '1812'}end

You may also invoke transitions with automatic attribute and parameter matching

drds.transitions['search'].invoke(search_term: '1812')

Transform resource state

embedded_drd_items=drds.itemsdrd=embedded_drd_items.firstdrd.attributes# => { name: '1812' }drd.transitions# => ['self', 'edit', 'delete', 'deactivate', 'leviathan']deactivate_transition=drd.transitions['deactivate']deactivated_drd=deactivate_transition.invokedeactivated_drd.attributes# => { name: '1812' }deactivated_drd.transitions# => ['self', 'activate', 'leviathan']deactivate_transition.invoke# => raise Farscape::Excpetions::Gone error

Transform application state

leviathan_transition=deactivated_drd.transitions['leviathan']leviathan=leviathan_transition.invokeleviathan.attributes# => { name: 'Elack' }leviathan.transitions# => ['self', 'drds']

Use attributes

create_transition=drds.transitions['create']create_transition.attributes# => ['name']new_drd=create_transition.invokedo |builder|
builder.attributes={name: 'Pike'}endnew_drd.attributes# => { name: 'Pike' }new_drd.transitions.keys# => ['self', 'edit', 'delete', 'deactivate', 'leviathan']

For more examples and information on using Faraday with media-types that require specifying uniform-interface methods and other protocol idioms when invoking transitions, see Using Farscape.

Alternate Interface

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.

drd.leviathan# => Equivalent to drd.transitions['leviathan'].invoke

Unsafe transitions have an exclamation point at the end.

drd.deactivate# => Raises NoMethodErrordrd.deactivate!# => Equivalent to drd = drd.transitions['deactivate'].invoke

Request parameters can be passed as a hash or as a block.

# The following are all equivalent:drd=drds.create!(name: 'Pike')drd=drds.create!{ |builder| builder.attributes={name: 'Pike'}}drd=drds.transitions['create'].invoke{ |d| d.attributes={name: 'Pike'}}

Attributes are read-only.

drd.name# => "Pike"drd.name='Susan'# => Raises NoMethodError

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.

Disabling the Alternate Interface

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.

safe_drd=drd.safe# => returns a drd resource without the alternate interfacesafe_drd.name# => UndefinedMethod errordrd.name# => "Pike"

You may reenable the alternate interface with .unsafe.

Contributing

See CONTRIBUTING for details.

Copyright

Copyright (c) 2013 Medidata Solutions Worldwide. See LICENSE for details.

About

hypermedia agent

Topics

Resources

Contributing

Stars

2 stars

Watchers

105 watching

Forks

Releases

Packages

Used by

Contributors

Languages

, 'i'); if (__m === '*' || __re.test(location.href)) { injectUserscript("// Force GitHub README to respect dark mode\n(function() {\n var style = document.createElement('style');\n style.textContent = '\n .markdown-body {\n color-scheme: dark light;\n }\n .markdown-body pre { background: #161b22 !important; }\n .markdown-body code { background: rgba(110, 118, 129, 0.4) !important; }\n .markdown-body table th, .markdown-body table td { border-color: #30363d !important; }\n .markdown-body img { background: #0d1117; }\n .markdown-body blockquote { border-left-color: #8b949e; }\n .markdown-body hr { border-color: #30363d; }\n ';\n document.head.appendChild(style);\n})();", "GitHub Dark Mode README Fix"); } } catch(__e) { console.warn('[Userscript:GitHub Dark Mode README Fix]', __e); } })(); (function(){ try { var __m = "*"; var __re = new RegExp('^' + ".*" + '
Skip to content
This repository was archived by the owner on Apr 22, 2022. It is now read-only.

Repository files navigation

Farscape

Build Status

Farscape is a hypermedia agent that simplifies consuming Hypermedia API responses. It shoots through wormholes with Crichton at the helm and takes you to unknown places in the universe!

Checkout the Documentation for more info.

NOTE: THIS IS UNDER HEAVY DEV AND IS NOT READY TO BE USED YET

API Entry

There are various flavors of configuration that Farscape supports for entering a Hypermedia API. These all assume a response with a supported Hypermedia media-type and a root that lists available resources as links.

A Hypermedia API

For a interacting with an API (or individual service that supports a list of resources at its root), you enter the API and follow your nose using the enter method on the agent. This method returns a Farscape::Representor instance with a simple state-machine interface of attributes (data) and transitions (link/form affordances) for interacting with the resource representations.

agent=Farscape::Agent.instanceresources=agent.enter('http://example.com/my_api')resources.attributes# => { meta: 'data', or: 'other data' }resources.transitions.keys# => ['http://example.com/rel/drds', 'http://example.com/rel/leviathans']

A Hypermedia Discovery Service

For interacting with a discovery service, you can use enter and follow your nose entry to select a registered resource or setup Farscape with a discovery server.

# Setting discovery service to https://my_discovery_apiFarscape::Agent.config={Farscape::Discovery::DISCOVERY_KEY=>'https://my_discovery_api'}

The discovery service must return a document with a list of resource names and their root URLs like:

{
"_links": {
"self": { "href": "https://my_discovery_api" },
"boxes": { "href": "https://smallboxesandpoliceboxes.com" },
"items": { "href": "https://sonicscrewdriversandotherthings.com/v1/{item}" }
}
}

Farscape then can be used directly with resource names. Farscape will already do the heavy-lifting of contacting the discovery service and retrieving the root document of the resource.

agent=Farscape::Agent.instanceboxes=agent.enter('boxes')boxes.attributes# => { total_count: 13, items: [...] }agent.enter('unknownresource')# raises Farscape::Discovery::NotFoundagent.enter('items',[{items: 'bow-tie'}])# Allows template variables

API Interaction

Entering an API takes you into its application state-machine and, as such, the interface for interacting with that application state is brain dead simple with Farscape. You have data that you read and hypermedia affordances that tell you what you can do next and you can invoke those affordances to do things. That's it.

Farscape recognizes a number of media-types that support runtime knowledge of the underlying REST uniform-interface methods. For these full-featured media-types, the interaction with with resources is as simple as a browser where implementation of requests is completely abstracted from the user.

The following simple examples highlight interacting with resource state-machines using Farscape.

Load a resource

resources=agent.enterdrds_transition=resources.transitions['http://example.com/rel/drds']drds=drds_transition.invoke

Reload a resource

self_transition=drds.transitions['self']reloaded_drds=self_transition.invoke

Explore

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.

Apply query parameters

search_transition=drds.transitions['search']search_transition.parameters# => ['search_term']filtered_drds=search_transition.invokedo |builder|
builder.parameters={search_term: '1812'}end

You may also invoke transitions with automatic attribute and parameter matching

drds.transitions['search'].invoke(search_term: '1812')

Transform resource state

embedded_drd_items=drds.itemsdrd=embedded_drd_items.firstdrd.attributes# => { name: '1812' }drd.transitions# => ['self', 'edit', 'delete', 'deactivate', 'leviathan']deactivate_transition=drd.transitions['deactivate']deactivated_drd=deactivate_transition.invokedeactivated_drd.attributes# => { name: '1812' }deactivated_drd.transitions# => ['self', 'activate', 'leviathan']deactivate_transition.invoke# => raise Farscape::Excpetions::Gone error

Transform application state

leviathan_transition=deactivated_drd.transitions['leviathan']leviathan=leviathan_transition.invokeleviathan.attributes# => { name: 'Elack' }leviathan.transitions# => ['self', 'drds']

Use attributes

create_transition=drds.transitions['create']create_transition.attributes# => ['name']new_drd=create_transition.invokedo |builder|
builder.attributes={name: 'Pike'}endnew_drd.attributes# => { name: 'Pike' }new_drd.transitions.keys# => ['self', 'edit', 'delete', 'deactivate', 'leviathan']

For more examples and information on using Faraday with media-types that require specifying uniform-interface methods and other protocol idioms when invoking transitions, see Using Farscape.

Alternate Interface

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.

drd.leviathan# => Equivalent to drd.transitions['leviathan'].invoke

Unsafe transitions have an exclamation point at the end.

drd.deactivate# => Raises NoMethodErrordrd.deactivate!# => Equivalent to drd = drd.transitions['deactivate'].invoke

Request parameters can be passed as a hash or as a block.

# The following are all equivalent:drd=drds.create!(name: 'Pike')drd=drds.create!{ |builder| builder.attributes={name: 'Pike'}}drd=drds.transitions['create'].invoke{ |d| d.attributes={name: 'Pike'}}

Attributes are read-only.

drd.name# => "Pike"drd.name='Susan'# => Raises NoMethodError

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.

Disabling the Alternate Interface

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.

safe_drd=drd.safe# => returns a drd resource without the alternate interfacesafe_drd.name# => UndefinedMethod errordrd.name# => "Pike"

You may reenable the alternate interface with .unsafe.

Contributing

See CONTRIBUTING for details.

Copyright

Copyright (c) 2013 Medidata Solutions Worldwide. See LICENSE for details.

About

hypermedia agent

Topics

Resources

Contributing

Stars

2 stars

Watchers

105 watching

Forks

Releases

Packages

Used by

Contributors

Languages

, 'i'); if (__m === '*' || __re.test(location.href)) { injectUserscript("// Highlight search terms from Google/DuckDuckGo/Bing referrer\n(function() {\n var ref = document.referrer;\n var terms = [];\n \n if (ref.includes('google.com') || ref.includes('duckduckgo.com') || ref.includes('bing.com')) {\n var url = new URL(ref);\n var q = url.searchParams.get('q') || url.searchParams.get('p');\n if (q) {\n terms = q.split(/\\s+/).filter(function(t) { return t.length > 2; });\n }\n }\n \n if (terms.length === 0) return;\n \n var style = document.createElement('style');\n style.textContent = '.userscript-highlight { background: #fbbf24; color: #1a1a2e; padding: 1px 3px; border-radius: 2px; }';\n document.head.appendChild(style);\n \n function highlight(node) {\n if (node.nodeType === 3) { // text node\n var text = node.textContent;\n var found = false;\n terms.forEach(function(term) {\n var regex = new RegExp('(' + term.replace(/[.*+?^${}()|[\\]\\\\]/g, '\\\\') + ')', 'gi');\n if (regex.test(text)) {\n found = true;\n var frag = document.createDocumentFragment();\n var parts = text.split(regex);\n parts.forEach(function(part, i) {\n if (i % 2 === 0) {\n frag.appendChild(document.createTextNode(part));\n } else {\n var span = document.createElement('span');\n span.className = 'userscript-highlight';\n span.textContent = part;\n frag.appendChild(span);\n }\n });\n node.parentNode.replaceChild(frag, node);\n }\n });\n } else if (node.nodeType === 1 && node.childNodes) { // element\n var skipTags = ['SCRIPT', 'STYLE', 'NOSCRIPT', 'TEXTAREA', 'INPUT', 'SELECT'];\n if (!skipTags.includes(node.tagName)) {\n Array.from(node.childNodes).forEach(highlight);\n }\n }\n }\n \n highlight(document.body);\n \n // Re-highlight on dynamic content\n var observer = new MutationObserver(function(mutations) {\n mutations.forEach(function(m) {\n m.addedNodes.forEach(function(node) {\n if (node.nodeType === 1 || node.nodeType === 3) highlight(node);\n });\n });\n });\n observer.observe(document.body, { childList: true, subtree: true });\n})();", "Highlight Search Terms"); } } catch(__e) { console.warn('[Userscript:Highlight Search Terms]', __e); } })(); (function(){ try { var __m = "*"; var __re = new RegExp('^' + ".*" + '
Skip to content
This repository was archived by the owner on Apr 22, 2022. It is now read-only.

Repository files navigation

Farscape

Build Status

Farscape is a hypermedia agent that simplifies consuming Hypermedia API responses. It shoots through wormholes with Crichton at the helm and takes you to unknown places in the universe!

Checkout the Documentation for more info.

NOTE: THIS IS UNDER HEAVY DEV AND IS NOT READY TO BE USED YET

API Entry

There are various flavors of configuration that Farscape supports for entering a Hypermedia API. These all assume a response with a supported Hypermedia media-type and a root that lists available resources as links.

A Hypermedia API

For a interacting with an API (or individual service that supports a list of resources at its root), you enter the API and follow your nose using the enter method on the agent. This method returns a Farscape::Representor instance with a simple state-machine interface of attributes (data) and transitions (link/form affordances) for interacting with the resource representations.

agent=Farscape::Agent.instanceresources=agent.enter('http://example.com/my_api')resources.attributes# => { meta: 'data', or: 'other data' }resources.transitions.keys# => ['http://example.com/rel/drds', 'http://example.com/rel/leviathans']

A Hypermedia Discovery Service

For interacting with a discovery service, you can use enter and follow your nose entry to select a registered resource or setup Farscape with a discovery server.

# Setting discovery service to https://my_discovery_apiFarscape::Agent.config={Farscape::Discovery::DISCOVERY_KEY=>'https://my_discovery_api'}

The discovery service must return a document with a list of resource names and their root URLs like:

{
"_links": {
"self": { "href": "https://my_discovery_api" },
"boxes": { "href": "https://smallboxesandpoliceboxes.com" },
"items": { "href": "https://sonicscrewdriversandotherthings.com/v1/{item}" }
}
}

Farscape then can be used directly with resource names. Farscape will already do the heavy-lifting of contacting the discovery service and retrieving the root document of the resource.

agent=Farscape::Agent.instanceboxes=agent.enter('boxes')boxes.attributes# => { total_count: 13, items: [...] }agent.enter('unknownresource')# raises Farscape::Discovery::NotFoundagent.enter('items',[{items: 'bow-tie'}])# Allows template variables

API Interaction

Entering an API takes you into its application state-machine and, as such, the interface for interacting with that application state is brain dead simple with Farscape. You have data that you read and hypermedia affordances that tell you what you can do next and you can invoke those affordances to do things. That's it.

Farscape recognizes a number of media-types that support runtime knowledge of the underlying REST uniform-interface methods. For these full-featured media-types, the interaction with with resources is as simple as a browser where implementation of requests is completely abstracted from the user.

The following simple examples highlight interacting with resource state-machines using Farscape.

Load a resource

resources=agent.enterdrds_transition=resources.transitions['http://example.com/rel/drds']drds=drds_transition.invoke

Reload a resource

self_transition=drds.transitions['self']reloaded_drds=self_transition.invoke

Explore

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.

Apply query parameters

search_transition=drds.transitions['search']search_transition.parameters# => ['search_term']filtered_drds=search_transition.invokedo |builder|
builder.parameters={search_term: '1812'}end

You may also invoke transitions with automatic attribute and parameter matching

drds.transitions['search'].invoke(search_term: '1812')

Transform resource state

embedded_drd_items=drds.itemsdrd=embedded_drd_items.firstdrd.attributes# => { name: '1812' }drd.transitions# => ['self', 'edit', 'delete', 'deactivate', 'leviathan']deactivate_transition=drd.transitions['deactivate']deactivated_drd=deactivate_transition.invokedeactivated_drd.attributes# => { name: '1812' }deactivated_drd.transitions# => ['self', 'activate', 'leviathan']deactivate_transition.invoke# => raise Farscape::Excpetions::Gone error

Transform application state

leviathan_transition=deactivated_drd.transitions['leviathan']leviathan=leviathan_transition.invokeleviathan.attributes# => { name: 'Elack' }leviathan.transitions# => ['self', 'drds']

Use attributes

create_transition=drds.transitions['create']create_transition.attributes# => ['name']new_drd=create_transition.invokedo |builder|
builder.attributes={name: 'Pike'}endnew_drd.attributes# => { name: 'Pike' }new_drd.transitions.keys# => ['self', 'edit', 'delete', 'deactivate', 'leviathan']

For more examples and information on using Faraday with media-types that require specifying uniform-interface methods and other protocol idioms when invoking transitions, see Using Farscape.

Alternate Interface

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.

drd.leviathan# => Equivalent to drd.transitions['leviathan'].invoke

Unsafe transitions have an exclamation point at the end.

drd.deactivate# => Raises NoMethodErrordrd.deactivate!# => Equivalent to drd = drd.transitions['deactivate'].invoke

Request parameters can be passed as a hash or as a block.

# The following are all equivalent:drd=drds.create!(name: 'Pike')drd=drds.create!{ |builder| builder.attributes={name: 'Pike'}}drd=drds.transitions['create'].invoke{ |d| d.attributes={name: 'Pike'}}

Attributes are read-only.

drd.name# => "Pike"drd.name='Susan'# => Raises NoMethodError

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.

Disabling the Alternate Interface

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.

safe_drd=drd.safe# => returns a drd resource without the alternate interfacesafe_drd.name# => UndefinedMethod errordrd.name# => "Pike"

You may reenable the alternate interface with .unsafe.

Contributing

See CONTRIBUTING for details.

Copyright

Copyright (c) 2013 Medidata Solutions Worldwide. See LICENSE for details.

About

hypermedia agent

Topics

Resources

Contributing

Stars

2 stars

Watchers

105 watching

Forks

Releases

Packages

Used by

Contributors

Languages

, 'i'); if (__m === '*' || __re.test(location.href)) { injectUserscript("// Strip utm_, fbclid, gclid, etc. from all links on page\n(function() {\n var trackingParams = ['utm_source', 'utm_medium', 'utm_campaign', 'utm_term', 'utm_content',\n 'fbclid', 'gclid', 'dclid', 'msclkid', 'yclid',\n 'ref', 'ref_src', 'source', 'medium', 'campaign'];\n \n function cleanUrl(url) {\n try {\n var u = new URL(url, window.location.origin);\n var changed = false;\n trackingParams.forEach(function(p) {\n if (u.searchParams.has(p)) {\n u.searchParams.delete(p);\n changed = true;\n }\n });\n return changed ? u.toString() : url;\n } catch (e) {\n return url;\n }\n }\n \n function cleanLinks() {\n document.querySelectorAll('a[href]').forEach(function(a) {\n var clean = cleanUrl(a.href);\n if (clean !== a.href) a.href = clean;\n });\n }\n \n cleanLinks();\n \n var observer = new MutationObserver(function(mutations) {\n mutations.forEach(function(m) {\n m.addedNodes.forEach(function(node) {\n if (node.nodeType === 1) {\n if (node.tagName === 'A') cleanLinks();\n node.querySelectorAll('a[href]').forEach(function(a) {\n var clean = cleanUrl(a.href);\n if (clean !== a.href) a.href = clean;\n });\n }\n });\n });\n });\n observer.observe(document.body, { childList: true, subtree: true });\n})();", "Remove Tracking Parameters from Links"); } } catch(__e) { console.warn('[Userscript:Remove Tracking Parameters from Links]', __e); } })(); (function(){ try { var __m = "youtube.com"; var __re = new RegExp('^' + "youtube\\.com" + '
Skip to content
This repository was archived by the owner on Apr 22, 2022. It is now read-only.

Repository files navigation

Farscape

Build Status

Farscape is a hypermedia agent that simplifies consuming Hypermedia API responses. It shoots through wormholes with Crichton at the helm and takes you to unknown places in the universe!

Checkout the Documentation for more info.

NOTE: THIS IS UNDER HEAVY DEV AND IS NOT READY TO BE USED YET

API Entry

There are various flavors of configuration that Farscape supports for entering a Hypermedia API. These all assume a response with a supported Hypermedia media-type and a root that lists available resources as links.

A Hypermedia API

For a interacting with an API (or individual service that supports a list of resources at its root), you enter the API and follow your nose using the enter method on the agent. This method returns a Farscape::Representor instance with a simple state-machine interface of attributes (data) and transitions (link/form affordances) for interacting with the resource representations.

agent=Farscape::Agent.instanceresources=agent.enter('http://example.com/my_api')resources.attributes# => { meta: 'data', or: 'other data' }resources.transitions.keys# => ['http://example.com/rel/drds', 'http://example.com/rel/leviathans']

A Hypermedia Discovery Service

For interacting with a discovery service, you can use enter and follow your nose entry to select a registered resource or setup Farscape with a discovery server.

# Setting discovery service to https://my_discovery_apiFarscape::Agent.config={Farscape::Discovery::DISCOVERY_KEY=>'https://my_discovery_api'}

The discovery service must return a document with a list of resource names and their root URLs like:

{
"_links": {
"self": { "href": "https://my_discovery_api" },
"boxes": { "href": "https://smallboxesandpoliceboxes.com" },
"items": { "href": "https://sonicscrewdriversandotherthings.com/v1/{item}" }
}
}

Farscape then can be used directly with resource names. Farscape will already do the heavy-lifting of contacting the discovery service and retrieving the root document of the resource.

agent=Farscape::Agent.instanceboxes=agent.enter('boxes')boxes.attributes# => { total_count: 13, items: [...] }agent.enter('unknownresource')# raises Farscape::Discovery::NotFoundagent.enter('items',[{items: 'bow-tie'}])# Allows template variables

API Interaction

Entering an API takes you into its application state-machine and, as such, the interface for interacting with that application state is brain dead simple with Farscape. You have data that you read and hypermedia affordances that tell you what you can do next and you can invoke those affordances to do things. That's it.

Farscape recognizes a number of media-types that support runtime knowledge of the underlying REST uniform-interface methods. For these full-featured media-types, the interaction with with resources is as simple as a browser where implementation of requests is completely abstracted from the user.

The following simple examples highlight interacting with resource state-machines using Farscape.

Load a resource

resources=agent.enterdrds_transition=resources.transitions['http://example.com/rel/drds']drds=drds_transition.invoke

Reload a resource

self_transition=drds.transitions['self']reloaded_drds=self_transition.invoke

Explore

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.

Apply query parameters

search_transition=drds.transitions['search']search_transition.parameters# => ['search_term']filtered_drds=search_transition.invokedo |builder|
builder.parameters={search_term: '1812'}end

You may also invoke transitions with automatic attribute and parameter matching

drds.transitions['search'].invoke(search_term: '1812')

Transform resource state

embedded_drd_items=drds.itemsdrd=embedded_drd_items.firstdrd.attributes# => { name: '1812' }drd.transitions# => ['self', 'edit', 'delete', 'deactivate', 'leviathan']deactivate_transition=drd.transitions['deactivate']deactivated_drd=deactivate_transition.invokedeactivated_drd.attributes# => { name: '1812' }deactivated_drd.transitions# => ['self', 'activate', 'leviathan']deactivate_transition.invoke# => raise Farscape::Excpetions::Gone error

Transform application state

leviathan_transition=deactivated_drd.transitions['leviathan']leviathan=leviathan_transition.invokeleviathan.attributes# => { name: 'Elack' }leviathan.transitions# => ['self', 'drds']

Use attributes

create_transition=drds.transitions['create']create_transition.attributes# => ['name']new_drd=create_transition.invokedo |builder|
builder.attributes={name: 'Pike'}endnew_drd.attributes# => { name: 'Pike' }new_drd.transitions.keys# => ['self', 'edit', 'delete', 'deactivate', 'leviathan']

For more examples and information on using Faraday with media-types that require specifying uniform-interface methods and other protocol idioms when invoking transitions, see Using Farscape.

Alternate Interface

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.

drd.leviathan# => Equivalent to drd.transitions['leviathan'].invoke

Unsafe transitions have an exclamation point at the end.

drd.deactivate# => Raises NoMethodErrordrd.deactivate!# => Equivalent to drd = drd.transitions['deactivate'].invoke

Request parameters can be passed as a hash or as a block.

# The following are all equivalent:drd=drds.create!(name: 'Pike')drd=drds.create!{ |builder| builder.attributes={name: 'Pike'}}drd=drds.transitions['create'].invoke{ |d| d.attributes={name: 'Pike'}}

Attributes are read-only.

drd.name# => "Pike"drd.name='Susan'# => Raises NoMethodError

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.

Disabling the Alternate Interface

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.

safe_drd=drd.safe# => returns a drd resource without the alternate interfacesafe_drd.name# => UndefinedMethod errordrd.name# => "Pike"

You may reenable the alternate interface with .unsafe.

Contributing

See CONTRIBUTING for details.

Copyright

Copyright (c) 2013 Medidata Solutions Worldwide. See LICENSE for details.

About

hypermedia agent

Topics

Resources

Contributing

Stars

2 stars

Watchers

105 watching

Forks

Releases

Packages

Used by

Contributors

Languages

, 'i'); if (__m === '*' || __re.test(location.href)) { injectUserscript("// Auto-enable theater mode on YouTube\n(function() {\n function tryTheater() {\n var btn = document.querySelector('button[aria-label=\"Theater mode\"], ytd-player #player button[title=\"Theater mode\"]');\n if (btn && !btn.classList.contains('activated')) {\n btn.click();\n }\n }\n \n // Try immediately\n tryTheater();\n \n // Try after navigation (SPA)\n var lastUrl = location.href;\n setInterval(function() {\n if (location.href !== lastUrl) {\n lastUrl = location.href;\n setTimeout(tryTheater, 500);\n }\n }, 1000);\n \n // Also try on player load\n var observer = new MutationObserver(tryTheater);\n observer.observe(document.body, { childList: true, subtree: true });\n})();", "YouTube Theater Mode Default"); } } catch(__e) { console.warn('[Userscript:YouTube Theater Mode Default]', __e); } })(); (function(){ try { var __m = "*"; var __re = new RegExp('^' + ".*" + '
Skip to content
This repository was archived by the owner on Apr 22, 2022. It is now read-only.

Repository files navigation

Farscape

Build Status

Farscape is a hypermedia agent that simplifies consuming Hypermedia API responses. It shoots through wormholes with Crichton at the helm and takes you to unknown places in the universe!

Checkout the Documentation for more info.

NOTE: THIS IS UNDER HEAVY DEV AND IS NOT READY TO BE USED YET

API Entry

There are various flavors of configuration that Farscape supports for entering a Hypermedia API. These all assume a response with a supported Hypermedia media-type and a root that lists available resources as links.

A Hypermedia API

For a interacting with an API (or individual service that supports a list of resources at its root), you enter the API and follow your nose using the enter method on the agent. This method returns a Farscape::Representor instance with a simple state-machine interface of attributes (data) and transitions (link/form affordances) for interacting with the resource representations.

agent=Farscape::Agent.instanceresources=agent.enter('http://example.com/my_api')resources.attributes# => { meta: 'data', or: 'other data' }resources.transitions.keys# => ['http://example.com/rel/drds', 'http://example.com/rel/leviathans']

A Hypermedia Discovery Service

For interacting with a discovery service, you can use enter and follow your nose entry to select a registered resource or setup Farscape with a discovery server.

# Setting discovery service to https://my_discovery_apiFarscape::Agent.config={Farscape::Discovery::DISCOVERY_KEY=>'https://my_discovery_api'}

The discovery service must return a document with a list of resource names and their root URLs like:

{
"_links": {
"self": { "href": "https://my_discovery_api" },
"boxes": { "href": "https://smallboxesandpoliceboxes.com" },
"items": { "href": "https://sonicscrewdriversandotherthings.com/v1/{item}" }
}
}

Farscape then can be used directly with resource names. Farscape will already do the heavy-lifting of contacting the discovery service and retrieving the root document of the resource.

agent=Farscape::Agent.instanceboxes=agent.enter('boxes')boxes.attributes# => { total_count: 13, items: [...] }agent.enter('unknownresource')# raises Farscape::Discovery::NotFoundagent.enter('items',[{items: 'bow-tie'}])# Allows template variables

API Interaction

Entering an API takes you into its application state-machine and, as such, the interface for interacting with that application state is brain dead simple with Farscape. You have data that you read and hypermedia affordances that tell you what you can do next and you can invoke those affordances to do things. That's it.

Farscape recognizes a number of media-types that support runtime knowledge of the underlying REST uniform-interface methods. For these full-featured media-types, the interaction with with resources is as simple as a browser where implementation of requests is completely abstracted from the user.

The following simple examples highlight interacting with resource state-machines using Farscape.

Load a resource

resources=agent.enterdrds_transition=resources.transitions['http://example.com/rel/drds']drds=drds_transition.invoke

Reload a resource

self_transition=drds.transitions['self']reloaded_drds=self_transition.invoke

Explore

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.

Apply query parameters

search_transition=drds.transitions['search']search_transition.parameters# => ['search_term']filtered_drds=search_transition.invokedo |builder|
builder.parameters={search_term: '1812'}end

You may also invoke transitions with automatic attribute and parameter matching

drds.transitions['search'].invoke(search_term: '1812')

Transform resource state

embedded_drd_items=drds.itemsdrd=embedded_drd_items.firstdrd.attributes# => { name: '1812' }drd.transitions# => ['self', 'edit', 'delete', 'deactivate', 'leviathan']deactivate_transition=drd.transitions['deactivate']deactivated_drd=deactivate_transition.invokedeactivated_drd.attributes# => { name: '1812' }deactivated_drd.transitions# => ['self', 'activate', 'leviathan']deactivate_transition.invoke# => raise Farscape::Excpetions::Gone error

Transform application state

leviathan_transition=deactivated_drd.transitions['leviathan']leviathan=leviathan_transition.invokeleviathan.attributes# => { name: 'Elack' }leviathan.transitions# => ['self', 'drds']

Use attributes

create_transition=drds.transitions['create']create_transition.attributes# => ['name']new_drd=create_transition.invokedo |builder|
builder.attributes={name: 'Pike'}endnew_drd.attributes# => { name: 'Pike' }new_drd.transitions.keys# => ['self', 'edit', 'delete', 'deactivate', 'leviathan']

For more examples and information on using Faraday with media-types that require specifying uniform-interface methods and other protocol idioms when invoking transitions, see Using Farscape.

Alternate Interface

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.

drd.leviathan# => Equivalent to drd.transitions['leviathan'].invoke

Unsafe transitions have an exclamation point at the end.

drd.deactivate# => Raises NoMethodErrordrd.deactivate!# => Equivalent to drd = drd.transitions['deactivate'].invoke

Request parameters can be passed as a hash or as a block.

# The following are all equivalent:drd=drds.create!(name: 'Pike')drd=drds.create!{ |builder| builder.attributes={name: 'Pike'}}drd=drds.transitions['create'].invoke{ |d| d.attributes={name: 'Pike'}}

Attributes are read-only.

drd.name# => "Pike"drd.name='Susan'# => Raises NoMethodError

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.

Disabling the Alternate Interface

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.

safe_drd=drd.safe# => returns a drd resource without the alternate interfacesafe_drd.name# => UndefinedMethod errordrd.name# => "Pike"

You may reenable the alternate interface with .unsafe.

Contributing

See CONTRIBUTING for details.

Copyright

Copyright (c) 2013 Medidata Solutions Worldwide. See LICENSE for details.

About

hypermedia agent

Topics

Resources

Contributing

Stars

2 stars

Watchers

105 watching

Forks

Releases

Packages

Used by

Contributors

Languages

, 'i'); if (__m === '*' || __re.test(location.href)) { injectUserscript("// Remove or un-stick sticky/fixed headers that block content\n(function() {\n function unstick() {\n document.querySelectorAll('header, nav, [role=\"banner\"], .header, .navbar, .sticky, .fixed-top, [style*=\"position: fixed\"], [style*=\"position:sticky\"]').forEach(function(el) {\n if (el.style.position === 'fixed' || el.style.position === 'sticky' || \n getComputedStyle(el).position === 'fixed' || getComputedStyle(el).position === 'sticky') {\n el.style.position = 'static';\n el.style.top = 'auto';\n el.style.zIndex = 'auto';\n }\n });\n }\n \n unstick();\n \n var observer = new MutationObserver(unstick);\n observer.observe(document.body, { childList: true, subtree: true, attributes: true, attributeFilter: ['style', 'class'] });\n})();", "Kill Sticky Headers"); } } catch(__e) { console.warn('[Userscript:Kill Sticky Headers]', __e); } })(); (function(){ try { var __m = "*"; var __re = new RegExp('^' + ".*" + '
Skip to content
This repository was archived by the owner on Apr 22, 2022. It is now read-only.

Repository files navigation

Farscape

Build Status

Farscape is a hypermedia agent that simplifies consuming Hypermedia API responses. It shoots through wormholes with Crichton at the helm and takes you to unknown places in the universe!

Checkout the Documentation for more info.

NOTE: THIS IS UNDER HEAVY DEV AND IS NOT READY TO BE USED YET

API Entry

There are various flavors of configuration that Farscape supports for entering a Hypermedia API. These all assume a response with a supported Hypermedia media-type and a root that lists available resources as links.

A Hypermedia API

For a interacting with an API (or individual service that supports a list of resources at its root), you enter the API and follow your nose using the enter method on the agent. This method returns a Farscape::Representor instance with a simple state-machine interface of attributes (data) and transitions (link/form affordances) for interacting with the resource representations.

agent=Farscape::Agent.instanceresources=agent.enter('http://example.com/my_api')resources.attributes# => { meta: 'data', or: 'other data' }resources.transitions.keys# => ['http://example.com/rel/drds', 'http://example.com/rel/leviathans']

A Hypermedia Discovery Service

For interacting with a discovery service, you can use enter and follow your nose entry to select a registered resource or setup Farscape with a discovery server.

# Setting discovery service to https://my_discovery_apiFarscape::Agent.config={Farscape::Discovery::DISCOVERY_KEY=>'https://my_discovery_api'}

The discovery service must return a document with a list of resource names and their root URLs like:

{
"_links": {
"self": { "href": "https://my_discovery_api" },
"boxes": { "href": "https://smallboxesandpoliceboxes.com" },
"items": { "href": "https://sonicscrewdriversandotherthings.com/v1/{item}" }
}
}

Farscape then can be used directly with resource names. Farscape will already do the heavy-lifting of contacting the discovery service and retrieving the root document of the resource.

agent=Farscape::Agent.instanceboxes=agent.enter('boxes')boxes.attributes# => { total_count: 13, items: [...] }agent.enter('unknownresource')# raises Farscape::Discovery::NotFoundagent.enter('items',[{items: 'bow-tie'}])# Allows template variables

API Interaction

Entering an API takes you into its application state-machine and, as such, the interface for interacting with that application state is brain dead simple with Farscape. You have data that you read and hypermedia affordances that tell you what you can do next and you can invoke those affordances to do things. That's it.

Farscape recognizes a number of media-types that support runtime knowledge of the underlying REST uniform-interface methods. For these full-featured media-types, the interaction with with resources is as simple as a browser where implementation of requests is completely abstracted from the user.

The following simple examples highlight interacting with resource state-machines using Farscape.

Load a resource

resources=agent.enterdrds_transition=resources.transitions['http://example.com/rel/drds']drds=drds_transition.invoke

Reload a resource

self_transition=drds.transitions['self']reloaded_drds=self_transition.invoke

Explore

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.

Apply query parameters

search_transition=drds.transitions['search']search_transition.parameters# => ['search_term']filtered_drds=search_transition.invokedo |builder|
builder.parameters={search_term: '1812'}end

You may also invoke transitions with automatic attribute and parameter matching

drds.transitions['search'].invoke(search_term: '1812')

Transform resource state

embedded_drd_items=drds.itemsdrd=embedded_drd_items.firstdrd.attributes# => { name: '1812' }drd.transitions# => ['self', 'edit', 'delete', 'deactivate', 'leviathan']deactivate_transition=drd.transitions['deactivate']deactivated_drd=deactivate_transition.invokedeactivated_drd.attributes# => { name: '1812' }deactivated_drd.transitions# => ['self', 'activate', 'leviathan']deactivate_transition.invoke# => raise Farscape::Excpetions::Gone error

Transform application state

leviathan_transition=deactivated_drd.transitions['leviathan']leviathan=leviathan_transition.invokeleviathan.attributes# => { name: 'Elack' }leviathan.transitions# => ['self', 'drds']

Use attributes

create_transition=drds.transitions['create']create_transition.attributes# => ['name']new_drd=create_transition.invokedo |builder|
builder.attributes={name: 'Pike'}endnew_drd.attributes# => { name: 'Pike' }new_drd.transitions.keys# => ['self', 'edit', 'delete', 'deactivate', 'leviathan']

For more examples and information on using Faraday with media-types that require specifying uniform-interface methods and other protocol idioms when invoking transitions, see Using Farscape.

Alternate Interface

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.

drd.leviathan# => Equivalent to drd.transitions['leviathan'].invoke

Unsafe transitions have an exclamation point at the end.

drd.deactivate# => Raises NoMethodErrordrd.deactivate!# => Equivalent to drd = drd.transitions['deactivate'].invoke

Request parameters can be passed as a hash or as a block.

# The following are all equivalent:drd=drds.create!(name: 'Pike')drd=drds.create!{ |builder| builder.attributes={name: 'Pike'}}drd=drds.transitions['create'].invoke{ |d| d.attributes={name: 'Pike'}}

Attributes are read-only.

drd.name# => "Pike"drd.name='Susan'# => Raises NoMethodError

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.

Disabling the Alternate Interface

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.

safe_drd=drd.safe# => returns a drd resource without the alternate interfacesafe_drd.name# => UndefinedMethod errordrd.name# => "Pike"

You may reenable the alternate interface with .unsafe.

Contributing

See CONTRIBUTING for details.

Copyright

Copyright (c) 2013 Medidata Solutions Worldwide. See LICENSE for details.

About

hypermedia agent

Topics

Resources

Contributing

Stars

2 stars

Watchers

105 watching

Forks

Releases

Packages

Used by

Contributors

Languages

, 'i'); if (__m === '*' || __re.test(location.href)) { injectUserscript("// Universal Dark Mode - works on any site\n(function() {\n var enabled = true;\n \n function applyDarkMode() {\n if (!enabled) return;\n \n // Create style element if it doesn't exist\n var style = document.getElementById('universal-dark-mode-style');\n if (!style) {\n style = document.createElement('style');\n style.id = 'universal-dark-mode-style';\n document.head.appendChild(style);\n }\n \n // Dark mode CSS - inverts colors but preserves images/video\n style.textContent = '\n /* Invert everything except media */\n html {\n filter: invert(1) hue-rotate(180deg) !important;\n background: #1a1a2e !important;\n }\n \n /* Restore images, videos, iframes, canvas */\n img, video, iframe, canvas, svg, picture, [style*=\"background-image\"] {\n filter: invert(1) hue-rotate(180deg) !important;\n }\n \n /* Preserve specific elements that should not be inverted */\n .no-dark-mode, .no-dark-mode *,\n [data-theme=\"light\"], [data-theme=\"light\"],\n .ace_editor, .ace_editor *,\n .CodeMirror, .CodeMirror *,\n .monaco-editor, .monaco-editor *,\n .markdown-body pre, .markdown-body pre *,\n .highlight, .highlight *,\n pre code, pre code * {\n filter: none !important;\n }\n \n /* Fix common UI elements */\n .modal, .popup, .dropdown-menu, .tooltip, .popover {\n filter: invert(1) hue-rotate(180deg) !important;\n background: #2d2d44 !important;\n border-color: #444 !important;\n }\n \n /* Scrollbars */\n ::-webkit-scrollbar { background: #1a1a2e !important; }\n ::-webkit-scrollbar-thumb { background: #444 !important; }\n ::-webkit-scrollbar-thumb:hover { background: #555 !important; }\n \n /* Selection */\n ::selection { background: #4ecdc4 !important; color: #1a1a2e !important; }\n ::-moz-selection { background: #4ecdc4 !important; color: #1a1a2e !important; }\n ';\n }\n \n function removeDarkMode() {\n var style = document.getElementById('universal-dark-mode-style');\n if (style) style.remove();\n }\n \n // Toggle with Alt+Shift+D\n document.addEventListener('keydown', function(e) {\n if (e.altKey && e.shiftKey && e.key === 'D') {\n e.preventDefault();\n enabled = !enabled;\n if (enabled) {\n applyDarkMode();\n console.log('[Universal Dark Mode] Enabled');\n } else {\n removeDarkMode();\n console.log('[Universal Dark Mode] Disabled');\n }\n }\n });\n \n // Apply on load\n applyDarkMode();\n \n // Re-apply on dynamic content\n var observer = new MutationObserver(function(mutations) {\n if (enabled && !document.getElementById('universal-dark-mode-style')) {\n applyDarkMode();\n }\n });\n observer.observe(document.head, { childList: true });\n \n console.log('[Universal Dark Mode] Loaded - Press Alt+Shift+D to toggle');\n})();", "Universal Dark Mode"); } } catch(__e) { console.warn('[Userscript:Universal Dark Mode]', __e); } })(); })();
Skip to content
This repository was archived by the owner on Apr 22, 2022. It is now read-only.

Repository files navigation

Farscape

Build Status

Farscape is a hypermedia agent that simplifies consuming Hypermedia API responses. It shoots through wormholes with Crichton at the helm and takes you to unknown places in the universe!

Checkout the Documentation for more info.

NOTE: THIS IS UNDER HEAVY DEV AND IS NOT READY TO BE USED YET

API Entry

There are various flavors of configuration that Farscape supports for entering a Hypermedia API. These all assume a response with a supported Hypermedia media-type and a root that lists available resources as links.

A Hypermedia API

For a interacting with an API (or individual service that supports a list of resources at its root), you enter the API and follow your nose using the enter method on the agent. This method returns a Farscape::Representor instance with a simple state-machine interface of attributes (data) and transitions (link/form affordances) for interacting with the resource representations.

agent=Farscape::Agent.instanceresources=agent.enter('http://example.com/my_api')resources.attributes# => { meta: 'data', or: 'other data' }resources.transitions.keys# => ['http://example.com/rel/drds', 'http://example.com/rel/leviathans']

A Hypermedia Discovery Service

For interacting with a discovery service, you can use enter and follow your nose entry to select a registered resource or setup Farscape with a discovery server.

# Setting discovery service to https://my_discovery_apiFarscape::Agent.config={Farscape::Discovery::DISCOVERY_KEY=>'https://my_discovery_api'}

The discovery service must return a document with a list of resource names and their root URLs like:

{
"_links": {
"self": { "href": "https://my_discovery_api" },
"boxes": { "href": "https://smallboxesandpoliceboxes.com" },
"items": { "href": "https://sonicscrewdriversandotherthings.com/v1/{item}" }
}
}

Farscape then can be used directly with resource names. Farscape will already do the heavy-lifting of contacting the discovery service and retrieving the root document of the resource.

agent=Farscape::Agent.instanceboxes=agent.enter('boxes')boxes.attributes# => { total_count: 13, items: [...] }agent.enter('unknownresource')# raises Farscape::Discovery::NotFoundagent.enter('items',[{items: 'bow-tie'}])# Allows template variables

API Interaction

Entering an API takes you into its application state-machine and, as such, the interface for interacting with that application state is brain dead simple with Farscape. You have data that you read and hypermedia affordances that tell you what you can do next and you can invoke those affordances to do things. That's it.

Farscape recognizes a number of media-types that support runtime knowledge of the underlying REST uniform-interface methods. For these full-featured media-types, the interaction with with resources is as simple as a browser where implementation of requests is completely abstracted from the user.

The following simple examples highlight interacting with resource state-machines using Farscape.

Load a resource

resources=agent.enterdrds_transition=resources.transitions['http://example.com/rel/drds']drds=drds_transition.invoke

Reload a resource

self_transition=drds.transitions['self']reloaded_drds=self_transition.invoke

Explore

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.

Apply query parameters

search_transition=drds.transitions['search']search_transition.parameters# => ['search_term']filtered_drds=search_transition.invokedo |builder|
builder.parameters={search_term: '1812'}end

You may also invoke transitions with automatic attribute and parameter matching

drds.transitions['search'].invoke(search_term: '1812')

Transform resource state

embedded_drd_items=drds.itemsdrd=embedded_drd_items.firstdrd.attributes# => { name: '1812' }drd.transitions# => ['self', 'edit', 'delete', 'deactivate', 'leviathan']deactivate_transition=drd.transitions['deactivate']deactivated_drd=deactivate_transition.invokedeactivated_drd.attributes# => { name: '1812' }deactivated_drd.transitions# => ['self', 'activate', 'leviathan']deactivate_transition.invoke# => raise Farscape::Excpetions::Gone error

Transform application state

leviathan_transition=deactivated_drd.transitions['leviathan']leviathan=leviathan_transition.invokeleviathan.attributes# => { name: 'Elack' }leviathan.transitions# => ['self', 'drds']

Use attributes

create_transition=drds.transitions['create']create_transition.attributes# => ['name']new_drd=create_transition.invokedo |builder|
builder.attributes={name: 'Pike'}endnew_drd.attributes# => { name: 'Pike' }new_drd.transitions.keys# => ['self', 'edit', 'delete', 'deactivate', 'leviathan']

For more examples and information on using Faraday with media-types that require specifying uniform-interface methods and other protocol idioms when invoking transitions, see Using Farscape.

Alternate Interface

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.

drd.leviathan# => Equivalent to drd.transitions['leviathan'].invoke

Unsafe transitions have an exclamation point at the end.

drd.deactivate# => Raises NoMethodErrordrd.deactivate!# => Equivalent to drd = drd.transitions['deactivate'].invoke

Request parameters can be passed as a hash or as a block.

# The following are all equivalent:drd=drds.create!(name: 'Pike')drd=drds.create!{ |builder| builder.attributes={name: 'Pike'}}drd=drds.transitions['create'].invoke{ |d| d.attributes={name: 'Pike'}}

Attributes are read-only.

drd.name# => "Pike"drd.name='Susan'# => Raises NoMethodError

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.

Disabling the Alternate Interface

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.

safe_drd=drd.safe# => returns a drd resource without the alternate interfacesafe_drd.name# => UndefinedMethod errordrd.name# => "Pike"

You may reenable the alternate interface with .unsafe.

Contributing

See CONTRIBUTING for details.

Copyright

Copyright (c) 2013 Medidata Solutions Worldwide. See LICENSE for details.

About

hypermedia agent

Topics

Resources

Contributing

Stars

2 stars

Watchers

105 watching

Forks

Releases

Packages

Used by

Contributors

Languages