aphro 0.0.1

Sign up to get free protection for your applications and to get access to all the features.
Files changed (78) hide show
  1. data/.gitignore +6 -0
  2. data/.travis.yml +7 -0
  3. data/Gemfile +7 -0
  4. data/README.md +264 -0
  5. data/Rakefile +55 -0
  6. data/SPEC.md +17 -0
  7. data/aphro.gemspec +23 -0
  8. data/docs/.gitignore +0 -0
  9. data/docs/index.html +88 -0
  10. data/docs/lib/hateoas.html +88 -0
  11. data/docs/lib/hateoas/version.html +30 -0
  12. data/index.html +1 -0
  13. data/lib/client/aphro.rb +88 -0
  14. data/lib/server/.gitignore +69 -0
  15. data/lib/server/.rails_footnotes +3 -0
  16. data/lib/server/Gemfile +53 -0
  17. data/lib/server/README +261 -0
  18. data/lib/server/Rakefile +7 -0
  19. data/lib/server/app/assets/images/rails.png +0 -0
  20. data/lib/server/app/assets/javascripts/application.js +7 -0
  21. data/lib/server/app/assets/javascripts/jqueryui.js +406 -0
  22. data/lib/server/app/assets/javascripts/services.js.coffee +3 -0
  23. data/lib/server/app/assets/stylesheets/application.css +7 -0
  24. data/lib/server/app/assets/stylesheets/services.css.sass +3 -0
  25. data/lib/server/app/controllers/application_controller.rb +15 -0
  26. data/lib/server/app/controllers/services_controller.rb +5 -0
  27. data/lib/server/app/controllers/sessions_controller.rb +6 -0
  28. data/lib/server/app/helpers/application_helper.rb +2 -0
  29. data/lib/server/app/helpers/services_helper.rb +2 -0
  30. data/lib/server/app/mailers/.gitkeep +0 -0
  31. data/lib/server/app/models/.gitkeep +0 -0
  32. data/lib/server/app/views/layouts/application.html.erb +14 -0
  33. data/lib/server/app/views/services/index.html.haml +6 -0
  34. data/lib/server/app/views/sessions/index.html.haml +3 -0
  35. data/lib/server/config.ru +4 -0
  36. data/lib/server/config/application.rb +61 -0
  37. data/lib/server/config/boot.rb +6 -0
  38. data/lib/server/config/cucumber.yml +8 -0
  39. data/lib/server/config/environment.rb +5 -0
  40. data/lib/server/config/environments/development.rb +30 -0
  41. data/lib/server/config/environments/production.rb +60 -0
  42. data/lib/server/config/environments/test.rb +39 -0
  43. data/lib/server/config/initializers/backtrace_silencers.rb +7 -0
  44. data/lib/server/config/initializers/generators.rb +2 -0
  45. data/lib/server/config/initializers/inflections.rb +10 -0
  46. data/lib/server/config/initializers/mime_types.rb +5 -0
  47. data/lib/server/config/initializers/rails_footnotes.rb +5 -0
  48. data/lib/server/config/initializers/sass.rb +1 -0
  49. data/lib/server/config/initializers/secret_token.rb +7 -0
  50. data/lib/server/config/initializers/session_store.rb +8 -0
  51. data/lib/server/config/initializers/wrap_parameters.rb +14 -0
  52. data/lib/server/config/locales/en.yml +5 -0
  53. data/lib/server/config/routes.rb +60 -0
  54. data/lib/server/db/seeds.rb +7 -0
  55. data/lib/server/doc/README_FOR_APP +2 -0
  56. data/lib/server/features/demo_json_service.feature +14 -0
  57. data/lib/server/features/step_definitions/demo_json_services_steps.rb +10 -0
  58. data/lib/server/features/support/env.rb +51 -0
  59. data/lib/server/lib/assets/.gitkeep +0 -0
  60. data/lib/server/lib/tasks/.gitkeep +0 -0
  61. data/lib/server/lib/tasks/cucumber.rake +65 -0
  62. data/lib/server/public/404.html +26 -0
  63. data/lib/server/public/422.html +26 -0
  64. data/lib/server/public/500.html +26 -0
  65. data/lib/server/public/favicon.ico +0 -0
  66. data/lib/server/public/robots.txt +5 -0
  67. data/lib/server/script/cucumber +10 -0
  68. data/lib/server/script/rails +6 -0
  69. data/lib/server/spec/controllers/services_controller_spec.rb +9 -0
  70. data/lib/server/spec/spec_helper.rb +33 -0
  71. data/lib/server/vendor/assets/stylesheets/.gitkeep +0 -0
  72. data/lib/server/vendor/plugins/.gitkeep +0 -0
  73. data/lib/version.rb +3 -0
  74. data/spec/aphro_spec.rb +56 -0
  75. data/spec/fixtures/index.html.haml +1 -0
  76. data/spec/spec_helper.rb +1 -0
  77. data/tmp.html +1192 -0
  78. metadata +184 -0
@@ -0,0 +1,6 @@
1
+ *.gem
2
+ .bundle
3
+ Gemfile.lock
4
+ pkg/*
5
+ *.swp
6
+ .rbx/
@@ -0,0 +1,7 @@
1
+ rvm:
2
+ - rbx-18mode
3
+ - rbx-19mode
4
+ - jruby
5
+ - 1.9.3
6
+ - 1.9.2
7
+ - 1.8.7
data/Gemfile ADDED
@@ -0,0 +1,7 @@
1
+ source "http://rubygems.org"
2
+ gem 'httparty'
3
+ gem 'ruby-debug19'
4
+ gem 'mechanize'
5
+ gem 'haml'
6
+ # Specify your gem's dependencies in aphro.gemspec
7
+ gemspec
@@ -0,0 +1,264 @@
1
+ Why am I forking this repo?
2
+ ===========================
3
+
4
+ * Because I think there's mileage in exploring ideas. I'm interested in the direction
5
+ the web is taking and the future of APIs and human and machine interaction.
6
+ * Because I want to braindump some of these ideas.
7
+ * Because I was going to grab the hateoas name for a rubygem but didn't get round to doing it.
8
+
9
+ HATEOAS, JSON and forms
10
+ -----------------------
11
+ I like JSON, it's readable and clear. It lends itself well to friendly formatted output
12
+ it's beautiful because it sits in a comfortable place for humans and machines.
13
+ It doesn't work well for HATEOAS. HATEOAS requires links for conveying information
14
+ about where you can move to next and forms.
15
+ Well you can't really have JSON forms. Not without some predetermined way of expressing
16
+ them. And that presumably counts as out-of-band communication according to Roy Fielding.
17
+
18
+ I'm not entirely sold on Roy Fielding's ideas. I don't feel that anyone has truly made
19
+ them accessible to people like me and reading his blog posts doesn't help a great deal.
20
+ That doesn't mean I think it's wrong. It's just that without explicit examples of exactly
21
+ what a 100% REST compliant system is, it's difficult to decipher Roy's prose.
22
+
23
+ Trying to understand HATEOAS
24
+ ----------------------------
25
+ I'd like to gain a full understanding of what something that is not just RESTful, RESTish
26
+ or RESTesque, but actually REST. Roy Fielding does the community a bit of a dis-service
27
+ by not providing positive examples of REST systems, only examples that fail to meet his
28
+ expectations. To paraphrase:
29
+
30
+ "If you don't have full HATEOAS compliance then you don't get REST and you
31
+ shouldn't call it REST."
32
+
33
+ OK.
34
+
35
+ Good. I'd like to create an API that is fully REST compliant. Where do I start?
36
+
37
+ I need to understand what HATEOAS is and how I can use it.
38
+
39
+ So it's Hypermedia as the Engine of Application State.
40
+
41
+ A REST client needs no prior knowledge about how to interact with any particular
42
+ application or server beyond a generic understanding of hypermedia.
43
+
44
+ A generic understanding of hypermedia is a little vague.
45
+
46
+ from http://roy.gbiv.com/untangled/2008/rest-apis-must-be-hypertext-driven
47
+
48
+ ` A REST API should be entered with no prior knowledge beyond the initial URI
49
+ (bookmark) and set of standardized media types that are appropriate for the
50
+ intended audience (i.e., expected to be understood by any client that might
51
+ use the API). From that point on, all application state transitions must be
52
+ driven by client selection of server-provided choices that are present in the
53
+ received representations or implied by the user’s manipulation of those representations.
54
+ The transitions may be determined (or limited by) the client’s knowledge of media types
55
+ and resource communication mechanisms, both of which may be improved on-the-fly
56
+ (e.g., code-on-demand). [Failure here implies that out-of-band information is
57
+ driving interaction instead of hypertext.]`
58
+
59
+ ^
60
+ This is a part that I am conceptually struggling with.
61
+ It seems to me that there would always be coupling between a server and a client.
62
+ By trying to remove the constraint of using out-of-band information you reduce
63
+ the amount of pre-requisites but you lose something at the same time.
64
+
65
+ Communication is about expressing ideas and it would be interesting to create a
66
+ means of communication between any two systems that requires NO PRIOR KNOWLEDGE
67
+ of what that system is and/or does. Communication also depends on a common shared
68
+ vocabulary.
69
+ If I were to consume a twitter API then I would have prior knowledge of why
70
+ I wanted to consume it. I have a huge amount of out-of-band information about
71
+ why I should use it. I also might reasonably assume that I can POST tweets.
72
+ But programatically navigating this API without knowledge of anything about it?
73
+ Perhaps I'm misunderstanding but the idea of writing client code that is so flexible that
74
+ the services it connects to could completely change boggles my mind.
75
+ If I picture this scenario of ultimate future flexibility of decades long systems
76
+ that can be so flexible because they are fully REST and HATEOAS compliant and can
77
+ adapt to all sorts of changes because the client assumes nothing in advance then
78
+ I can only picture at the other end some kind of human interaction.
79
+
80
+ Pseudo-interaction of a REST client:
81
+
82
+ * Visit api.twitter.com
83
+ * See list of links in headers for possible next actions
84
+ * Check for tweet rel < - how is this determined ? (no prior knowledge so how would I even know I might want to tweet?)
85
+ * It appears twitter no longer has a tweet action
86
+ * ...
87
+ * Perform alternative action (Who or what decided this)
88
+
89
+ So maybe we can write clients that explore APIs and report back to more intelligent
90
+ agents about what is currently possible. Who are these agents? Intelligent software
91
+ agents? Humans? Is this out-of-band communication yet?
92
+
93
+ HATEOAS seems akin to inventing a human communication protocol that requires only an introduction
94
+ of two individuals on earth. Maybe this is a strawman but it sounds a little bit
95
+ like two people dropping the prerequisite for speaking the same language.
96
+ But perhaps this relates to the concepts of 'standards'
97
+
98
+
99
+ A REST API should never have “typed” resources that are significant to the client.
100
+ Specification authors may use resource types for describing server implementation
101
+ behind the interface, but those types must be irrelevant and invisible to the client.
102
+ The only types that are significant to a client are the current representation’s media
103
+ type and standardized relation names.
104
+
105
+ 'Standardized relation names' sounds to me like out of band communication.
106
+ It may sound like I'm naively comparing each point to human language and communication
107
+ but I think there are parallels.
108
+
109
+ It seems like it is confidently suggesting a group can come up with a set
110
+ of standards that can summarize and categorize all future expected communications.
111
+
112
+ It sounds worryingly like a top-down approach to thinking about how communications occur.
113
+ It reminds me of attempts to make languages like Esperanto.
114
+
115
+ An idealist would hope all humans can work towards creating a common language
116
+ and that if we can create a common enough framework that can be expanded upon
117
+ as necessary then we can allow communication from anyone to anyone else on earth.
118
+
119
+ But these approaches have so far failed. They mainly ignore the advantages of
120
+ specialisation, the benefits of exclusivity within a small group and the efficiency of
121
+ communication when new thoughts and ideas that might be relevant only to the local
122
+ area can be expressed. Languages merge into one another and dialects merge into
123
+ one another. Each language has a wealth of predetermined pre-encoded information
124
+ that is specific to the culture of the speakers of that language. There are
125
+ optimisations for expressing more common things more easily and I don't doubt that
126
+ certain languages lead and guide the speakers to communicate in ways or about topics
127
+ that lend themselves to easier communication in their tongue.
128
+
129
+ The gist of what I'm saying probably sounds thoroughly disconnected from what
130
+ REST is or aspires to be, but I'd like to express the parallels.
131
+ I don't think standardised out-of-band communication could work.
132
+ I think out-of-band communication and state are necessary to have expressive
133
+ and meaningfully useful communication.
134
+
135
+
136
+ **Out-of-band and state have parallels with pre-learned human language, human memory and interactions.**
137
+
138
+
139
+ I think that for machines to have meaningful relationships with each other they
140
+ may need to have a wealth of pre-determined concepts to build upon and expand
141
+ upon. I also think that at present the quickest way to express this information
142
+ in a way that enables humans and machines to interact well together is for
143
+ humans to have discussions, ideas and inspiration and to communicate how these
144
+ ideas might be shared between machines by agreeing upon simple meaningful APIs.
145
+
146
+ **I may well be missing the point, but I think coupling in communication is
147
+ possibly a necessity for meaningful communication.**
148
+ Client and server like speaker and listener are inherently bound together
149
+ and without shared prior knowledge machines can no more have meaningful discourse
150
+ than a native Mandarin speaker who speaks no other language can have a deep and
151
+ meaningful discussion with a native Spanish speaker who speaks no other language.
152
+ And any simple prescriptive predetermined protocol that the two might agree upon
153
+ is still bound to failure.
154
+
155
+ RDF
156
+ ---
157
+ I need to understand RDF as it might give more insight into how XHTML APIs
158
+ could utilise more human input and actually meet the HATEOAS constraint.
159
+
160
+ RDF primer http://www.w3.org/TR/xhtml-rdfa-primer/
161
+
162
+ Summary
163
+ =======
164
+
165
+ This README has turned more into a blog post and brain dump.
166
+ It's not particularly well formed, but I'm interested in exploring my ideas
167
+ further and seeing where I end up. I'd like to find that by the end of this
168
+ exploration I understand HATEAOS and REST fully and I'd be happy to have my
169
+ doubts quashed by a crystallising epiphany about what HATEOAS truly means.
170
+
171
+ I've benefitted greatly from utilising principles of 'REST-ish' design (sorry
172
+ Roy) and so I'm not about to cast Roy Fielding and his thesis off as emperor's
173
+ clothes troll-tastic material just yet. But I am prepared to mention my doubts.
174
+
175
+ The ultimate troll would be if the complete conclusion of a 100% REST compliant
176
+ system is that of a user interacting with a web browser and reading the information
177
+ presented to them by first visiting the 'homepage' of the site.
178
+
179
+
180
+ API Exploration
181
+ ===============
182
+
183
+ Without fully understanding the benefits of a HATEOAS compliant system I'd like to consider what
184
+ I might expect from using a client API:
185
+
186
+ ```ruby
187
+
188
+ twitter = Aphro.site "api.twitter.com"
189
+ twitter.actions
190
+ # => [sign_in: {method: :get}]
191
+ twitter.sign_in user: "mark@example.com", password: "password"
192
+ twitter.actions
193
+ # => [read_messages: {method: :get}, tweet: {method: :post}]
194
+ ```
195
+
196
+ Now the main gist of my point comes down to the prior knowledge of the system
197
+
198
+ Looking at the example again:
199
+
200
+ ```ruby
201
+ twitter = Aphro.site "api.twitter.com" #agreed prior knowledge of URL
202
+ twitter.actions #generic - no prior knowledge of twitter API. (actions method could be a generic method defined on the API but it is not preagreed information between client and server)
203
+ # => [sign_in: {method: :get}]
204
+ twitter.sign_in user: "mark@example.com", password: "password" #choosing to call this is prior shared knowledge
205
+ ```
206
+
207
+ Now HATEOAS will protect us from URL changes which is a Good Thing. Less to document and more flexibility on the server end.
208
+ But it wouldn't protect us from changes in the rel attributes.
209
+ In the RESTbucks world it doesn't help us if you suddenly have to respond to the question "Would you like syrup with that?" before processing or if during payment processing a 3dsecure step is added.
210
+ It's just about moving the contract between client and server to a different place. It's probably a good thing because we probably do change URLs a lot, or would if we could do it without breaking clients.
211
+ But I can't see how it could ever be the magical solution to end out of band communication.
212
+
213
+
214
+ I think what HATEOAS does is shift the point at which APIs break and changes the boundary point of the client server contract.
215
+ Yes you can tweak your server URLs by adding an extra layer of defence.
216
+ But you can't have clients that don't know about the actions they might expect to take when interacting with a service and the data they might be expected to fill in.
217
+
218
+ If you go to Starbucks then there's some prior implicit knowledge about interacting with the overall service.
219
+ The staff can guide you through the transaction. But if you're a tribesman from Papua New Guinea just landed in the UK as part of a documentary, then you could be lacking a lot of prior information that's necessary for a smooth transaction. You're going to need to understand some basic capitalism, what's being traded, why you might want a Hazelnut Latte, and to be honest if you speak English, it's gonna be a lot easier.
220
+ I think HATEOAS is the member of staff. They can't teach you English. They can't give you free coffee. But if you have some level of implicit understanding of the overall process they can guide you through to the next steps.
221
+
222
+
223
+
224
+ So what's the upshot of this?
225
+ -----------------------------
226
+
227
+ HATEOAS seems like it will give you a slightly more friendly contract between clients and servers.
228
+ When writing our API documentation (because I can't see a way around the pre-determined information)
229
+ we can write thinner more human readable documents.
230
+ We can say things like:
231
+
232
+ These are the services currently available:
233
+
234
+ * sign_in
235
+ * read_messages
236
+ * tweet
237
+
238
+ To see what fields you need to fill in you can visit the relevant URLs from the API root.
239
+
240
+ And yes you could make this straightforward XHTML and then a person can even go and browse in a web browser.
241
+ And then your API interaction could becomes the same as a human, filling in forms and clicking links (Capybara interaction).
242
+ I think that's probably writing more code than you need to, but it does give us an interesting way of exploring an API.
243
+ e.g. as a developer I'd rather write
244
+
245
+ ```ruby
246
+ twitter.sign_in user: "mark@example.com", password: "password"
247
+ twitter.tweet "HATEOAS could make my life easier"
248
+ #than
249
+ visit "/sign_in"
250
+ fill_in "user", with: "password"
251
+ fill_in "password", with: "password"
252
+ click_button "Sign in"
253
+ fill_in "tweet", with: "HATEOAS could make my life easier"
254
+ click_button "Tweet"
255
+ ```
256
+
257
+ Discoverable APIs could lead to some interesting ways that we'll be able to make our code and APIs more maintainable and easier to developer in the future. I like the idea of an API that's also browsable.
258
+ And I think as API documentation it's a killer idea.
259
+ In this way we reduce prior information, in that someone writing a client still goes and looks at the
260
+ API in advance, but you don't have external documentation explaining the API that you need to keep in sync.
261
+
262
+ It won't get us around the fact that if you change your fields or validations or the order in which it's possible to progress through a service/application, you will break your clients.
263
+
264
+ Your clients are still tied to the specific version of the API they were written against but they'll be a lot easier to update if you have a web browsable version of your API and discoverable URLs.
@@ -0,0 +1,55 @@
1
+ require "bundler/gem_tasks"
2
+ require 'rake/clean'
3
+
4
+ task :default => :spec
5
+
6
+ require 'rspec/core/rake_task'
7
+ RSpec::Core::RakeTask.new(:spec) do |t|
8
+ t.rspec_opts = ["--color"]
9
+ end
10
+
11
+ begin
12
+ # Bring in Rocco tasks
13
+ require 'rocco/tasks'
14
+ Rocco::make 'docs/'
15
+
16
+ desc 'Build rocco docs'
17
+ task :docs => :rocco
18
+ directory 'docs/'
19
+
20
+ file 'docs/index.html' => 'docs/lib/aphro.html' do |f|
21
+ cp 'docs/lib/aphro.html', 'docs/index.html', :preserve => true
22
+ end
23
+ task :docs => 'docs/index.html'
24
+ CLEAN.include 'docs/index.html'
25
+
26
+ # Alias for docs task
27
+ task :doc => :docs
28
+
29
+ # GITHUB PAGES ===============================================================
30
+
31
+ desc 'Update gh-pages branch'
32
+ task :pages => ['docs/.git', :docs] do
33
+ rev = `git rev-parse --short HEAD`.strip
34
+ Dir.chdir 'docs' do
35
+ sh "git add *.html"
36
+ sh "git commit -m 'rebuild pages from #{rev}'" do |ok,res|
37
+ if ok
38
+ verbose { puts "gh-pages updated" }
39
+ sh "git push -q o HEAD:gh-pages"
40
+ end
41
+ end
42
+ end
43
+ end
44
+
45
+ # Update the pages/ directory clone
46
+ file 'docs/.git' => ['docs/', '.git/refs/heads/gh-pages'] do |f|
47
+ sh "cd docs && git init -q && git remote add o ../.git" if !File.exist?(f.name)
48
+ sh "cd docs && git fetch -q o && git reset -q --hard o/gh-pages && touch ."
49
+ end
50
+ CLOBBER.include 'docs/.git'
51
+
52
+ rescue LoadError => e
53
+ puts "Something with rocco didn't load, you can't build the docs! Try bundle install?"
54
+ end
55
+
data/SPEC.md ADDED
@@ -0,0 +1,17 @@
1
+ About
2
+ =====
3
+
4
+ Aphro helps you build hypermedia API clients, by providing a simple
5
+ interface to navigate through pages, submitting forms, and caching local
6
+ responses.
7
+
8
+ The current version is 0.0.1.
9
+
10
+ Find out more at [the aphro homepage][homepage].
11
+
12
+ Installing
13
+ ==========
14
+
15
+ ```
16
+ gem install aphro
17
+ ```
@@ -0,0 +1,23 @@
1
+ # -*- encoding: utf-8 -*-
2
+ $:.push File.expand_path("../lib", __FILE__)
3
+ require "version"
4
+
5
+ Gem::Specification.new do |s|
6
+ s.name = "aphro"
7
+ s.version = Aphro::VERSION
8
+ s.authors = ["Forked from Steve Klabnik's hateoas gem", "Mark Burns"]
9
+ s.email = ["markthedeveloper@gmail.com"]
10
+ s.homepage = "http://markburns.github.com/aphro"
11
+ s.summary = %q{Build easy clients for Hypermedia APIs.}
12
+ s.description = %q{A set of tools to help build clients for Hypermedia APIs.}
13
+
14
+ s.files = `git ls-files`.split("\n")
15
+ s.test_files = `git ls-files -- {test,spec,features}/*`.split("\n")
16
+ s.executables = `git ls-files -- bin/*`.split("\n").map{ |f| File.basename(f) }
17
+ s.require_paths = ["lib"]
18
+
19
+ s.add_dependency "mechanize"
20
+
21
+ s.add_development_dependency "rspec"
22
+ s.add_development_dependency "rake"
23
+ end
File without changes
@@ -0,0 +1,88 @@
1
+ <!DOCTYPE html>
2
+ <html>
3
+ <head>
4
+ <meta http-equiv="content-type" content="text/html;charset=utf-8">
5
+ <title>aphro.rb</title>
6
+ <link rel="stylesheet" href="http://jashkenas.github.com/docco/resources/docco.css">
7
+ </head>
8
+ <body>
9
+ <div id='container'>
10
+ <div id="background"></div>
11
+ <div id="jump_to">
12
+ Jump To &hellip;
13
+ <div id="jump_wrapper">
14
+ <div id="jump_page">
15
+ <a class="source" href="aphro.html">aphro.rb</a>
16
+ <a class="source" href="aphro/version.html">version.rb</a>
17
+ </div>
18
+ </div>
19
+ </div>
20
+ <table cellspacing=0 cellpadding=0>
21
+ <thead>
22
+ <tr>
23
+ <th class=docs><h1>aphro.rb</h1></th>
24
+ <th class=code></th>
25
+ </tr>
26
+ </thead>
27
+ <tbody>
28
+ <tr id='section-1'>
29
+ <td class=docs>
30
+ <div class="pilwrap">
31
+ <a class="pilcrow" href="#section-1">&#182;</a>
32
+ </div>
33
+ <p>This is the source code for APHRO, a gem to help
34
+ you build client libraries for hypertext APIs. This
35
+ code is NOT ready for any kind of production, and is
36
+ frankly terrible. It&rsquo;s like two hours of work. Please
37
+ don&rsquo;t hate me.</p>
38
+
39
+ </td>
40
+ <td class=code>
41
+ <div class='highlight'><pre><span class="nb">require</span> <span class="s1">&#39;aphro/version&#39;</span>
42
+ <span class="nb">require</span> <span class="s1">&#39;open-uri&#39;</span>
43
+ <span class="nb">require</span> <span class="s1">&#39;mechanize&#39;</span>
44
+
45
+ <span class="k">module</span> <span class="nn">Aphro</span>
46
+ <span class="k">class</span> <span class="o">&lt;&lt;</span> <span class="nb">self</span>
47
+ <span class="kp">attr_accessor</span> <span class="ss">:rel_namespace</span>
48
+ <span class="kp">attr_writer</span> <span class="ss">:base_uri</span><span class="p">,</span> <span class="ss">:current_state</span>
49
+
50
+ <span class="k">def</span> <span class="nf">base_uri</span>
51
+ <span class="k">if</span> <span class="no">ENV</span><span class="o">[</span><span class="s1">&#39;APHRO_ENV&#39;</span><span class="o">]</span> <span class="o">==</span> <span class="s2">&quot;development&quot;</span>
52
+ <span class="n">development_base_uri</span> <span class="o">||</span> <span class="s2">&quot;</span><span class="si">#{</span><span class="vi">@base_uri</span><span class="si">}</span><span class="s2">.dev&quot;</span>
53
+ <span class="k">else</span>
54
+ <span class="vi">@base_uri</span>
55
+ <span class="k">end</span>
56
+ <span class="k">end</span>
57
+
58
+ <span class="k">def</span> <span class="nf">current_page</span>
59
+ <span class="n">current_state</span><span class="o">.</span><span class="n">body</span>
60
+ <span class="k">end</span>
61
+
62
+ <span class="k">def</span> <span class="nf">current_state</span>
63
+ <span class="vi">@current_state</span> <span class="o">||=</span> <span class="n">user_agent</span><span class="o">.</span><span class="n">get</span><span class="p">(</span><span class="n">base_uri</span><span class="p">)</span>
64
+ <span class="k">end</span>
65
+
66
+ <span class="k">def</span> <span class="nf">user_agent</span>
67
+ <span class="vi">@user_agent</span> <span class="o">||=</span> <span class="no">Mechanize</span><span class="o">.</span><span class="n">new</span>
68
+ <span class="k">end</span>
69
+
70
+ <span class="k">end</span>
71
+
72
+ <span class="k">module</span> <span class="nn">DSL</span>
73
+ <span class="kp">extend</span> <span class="nb">self</span>
74
+
75
+ <span class="k">def</span> <span class="nf">click_link</span><span class="p">(</span><span class="n">rel</span><span class="p">)</span>
76
+ <span class="k">if</span> <span class="no">Aphro</span><span class="o">.</span><span class="n">rel_namespace</span>
77
+ <span class="n">rel</span> <span class="o">=</span> <span class="s2">&quot;/</span><span class="si">#{</span><span class="no">Aphro</span><span class="o">.</span><span class="n">rel_namespace</span><span class="si">}</span><span class="s2">/</span><span class="si">#{</span><span class="n">rel</span><span class="si">}</span><span class="s2">&quot;</span>
78
+ <span class="k">end</span>
79
+
80
+ <span class="no">Aphro</span><span class="o">.</span><span class="n">current_state</span> <span class="o">=</span> <span class="no">Aphro</span><span class="o">.</span><span class="n">user_agent</span><span class="o">.</span><span class="n">click</span><span class="p">(</span><span class="no">Aphro</span><span class="o">.</span><span class="n">current_state</span><span class="o">.</span><span class="n">links</span><span class="o">.</span><span class="n">find</span><span class="p">{</span><span class="o">|</span><span class="n">l</span><span class="o">|</span> <span class="n">l</span><span class="o">.</span><span class="n">attributes</span><span class="o">[</span><span class="s2">&quot;rel&quot;</span><span class="o">]</span> <span class="o">==</span> <span class="n">rel</span> <span class="p">})</span>
81
+ <span class="k">end</span>
82
+ <span class="k">end</span>
83
+ <span class="k">end</span></pre></div>
84
+ </td>
85
+ </tr>
86
+ </table>
87
+ </div>
88
+ </body>