iated 0.0.1

Sign up to get free protection for your applications and to get access to all the features.
Files changed (54) hide show
  1. data/.gitignore +6 -0
  2. data/Gemfile +3 -0
  3. data/Guardfile +24 -0
  4. data/LICENSE +23 -0
  5. data/Makefile +72 -0
  6. data/README.md +87 -0
  7. data/Rakefile +10 -0
  8. data/bin/iated +13 -0
  9. data/config.ru +5 -0
  10. data/extensions/chrome/background.html +5 -0
  11. data/extensions/chrome/background.js +105 -0
  12. data/extensions/chrome/contentscript.css +0 -0
  13. data/extensions/chrome/contentscript.js +110 -0
  14. data/extensions/chrome/jquery-ui.js +45 -0
  15. data/extensions/chrome/jquery.js +16 -0
  16. data/extensions/chrome/jquery.updater.js +46 -0
  17. data/extensions/chrome/manifest.json +19 -0
  18. data/extensions/chrome/yaml.js +489 -0
  19. data/extensions/tests/simple.html +23 -0
  20. data/features/extension_authenticates.feature +30 -0
  21. data/features/extension_edits.feature +45 -0
  22. data/features/step_definitions/extension_steps.rb +134 -0
  23. data/features/support/env.rb +47 -0
  24. data/features/support/hooks.rb +11 -0
  25. data/iated.gemspec +48 -0
  26. data/lib/iated.rb +111 -0
  27. data/lib/iated/browser_token_db.rb +76 -0
  28. data/lib/iated/edit_session.rb +221 -0
  29. data/lib/iated/helpers.rb +9 -0
  30. data/lib/iated/mcp.rb +144 -0
  31. data/lib/iated/page_helpers.rb +33 -0
  32. data/lib/iated/public/jquery-ui.js +101 -0
  33. data/lib/iated/public/jquery.js +2 -0
  34. data/lib/iated/public/robots.txt +5 -0
  35. data/lib/iated/server.rb +162 -0
  36. data/lib/iated/sys_pref.rb +201 -0
  37. data/lib/iated/version.rb +3 -0
  38. data/lib/iated/views/hello.haml +13 -0
  39. data/lib/iated/views/preferences.haml +27 -0
  40. data/lib/iated/views/reference.coffee +79 -0
  41. data/lib/iated/views/reference.haml +94 -0
  42. data/lib/iated/views/reference.scss +36 -0
  43. data/lib/iated/views/root.haml +13 -0
  44. data/spec/lib/iated/browser_token_db_spec.rb +68 -0
  45. data/spec/lib/iated/edit_session_spec.rb +157 -0
  46. data/spec/lib/iated/mcp_spec.rb +86 -0
  47. data/spec/lib/iated/sys_pref_spec.rb +40 -0
  48. data/spec/protocol/edit_spec.rb +88 -0
  49. data/spec/protocol/hello_spec.rb +18 -0
  50. data/spec/protocol/notfound_spec.rb +11 -0
  51. data/spec/protocol/ping_spec.rb +10 -0
  52. data/spec/protocol/preferences_spec.rb +35 -0
  53. data/spec/spec_helper.rb +21 -0
  54. metadata +460 -0
@@ -0,0 +1,23 @@
1
+ <!DOCTYPE html>
2
+ <title>IAT Testing Page</title>
3
+ <script src="jquery.js"></script>
4
+ <body>
5
+ <h1>
6
+ IAT Testing Page
7
+ </h1>
8
+
9
+ <section>
10
+ <p>
11
+ A Textarea:
12
+ </p>
13
+ <textarea>Some text to play with.</textarea>
14
+ </section>
15
+
16
+ <section>
17
+ <p>
18
+ Another Textarea:
19
+ </p>
20
+ <textarea>Some more text to play with.</textarea>
21
+ </section>
22
+
23
+ </body>
@@ -0,0 +1,30 @@
1
+ Feature: Extension Authenticates
2
+
3
+ As a browser extension
4
+ I want to authenticate
5
+ So that I can send requests
6
+
7
+ The token can't be a cookie, or any page sent from the browser could access
8
+ features like editing, etc. That would allow a malicious page to open a million
9
+ editors or change preferences.
10
+
11
+ Scenario: Extension says hello
12
+ Given I have not authenticated
13
+ When I GET /hello without a secret
14
+ Then the user should be shown the secret
15
+ And the page should be "text/json"
16
+ And the status should be "ok"
17
+
18
+ @wip
19
+ Scenario: Extension posts secret
20
+ Given I have a secret
21
+ When I POST /hello with the secret
22
+ Then I should be sent a response with a token
23
+ And the page should be "text/json"
24
+ And the token should be registered
25
+
26
+ Scenario: Extension pings with secret
27
+ Given I have a token
28
+ When I POST /ping with the token
29
+ Then the page should say "pong"
30
+ And the page should be "text/plain"
@@ -0,0 +1,45 @@
1
+ Feature: Extension edits files
2
+
3
+ As a browser extension
4
+ I want to open text in an editor
5
+ So that I can allow users to edit textareas more elegantly.
6
+
7
+ Scenario: Sending text to edit
8
+ Given I have a token
9
+ When I have a textarea to edit
10
+ And the textarea has the id of "foobar"
11
+ And the textarea has a url of "http://example.com/foobarpage.html"
12
+ And I request an extension of ".txt"
13
+ And I POST an /edit request
14
+ Then I expect a valid session id
15
+ And I expect the editor file to have an extension of ".txt"
16
+ And I expect the editor file to have "example.com" in it
17
+ And I expect the editor file to have "foobarpage" in it
18
+ And I expect an editor to be opened
19
+
20
+ Scenario: First check of text
21
+ Given I have a new session
22
+ When I GET /edit/<session id>/0
23
+ Then I expect a change-count of 0
24
+ And I expect no text to be sent
25
+
26
+ Scenario: Checking unchanged text
27
+ Given I have a new session
28
+ And the session has been edited 2 times
29
+ When I GET /edit/<session id>/2
30
+ Then I expect a change-count of 2
31
+ And I expect no text to be sent
32
+
33
+ Scenario: Checking once changed text
34
+ Given I have a new session
35
+ And the session has been edited 2 times
36
+ When I GET /edit/<session id>/1
37
+ Then I expect a change-count of 2
38
+ And I expect the text to be sent
39
+
40
+ Scenario: Checking twice changed text
41
+ Given I have a new session
42
+ And the session has been edited 3 times
43
+ When I GET /edit/<session id>/1
44
+ Then I expect a change-count of 3
45
+ And I expect the text to be sent
@@ -0,0 +1,134 @@
1
+ ## Givens
2
+ Given /^I have not authenticated$/ do
3
+ @secret.should be_nil
4
+ @token.should be_nil
5
+ end
6
+
7
+ Given /^I have a secret$/ do
8
+ @secret.should be_nil
9
+ @secret = Iated::mcp.generate_secret
10
+ @secret.should_not be_nil
11
+ end
12
+
13
+ Given /^I have a token$/ do
14
+ @token.should be_nil
15
+ @token = Iated::mcp.generate_token "cucumber testing user agent"
16
+ @token.should_not be_nil
17
+ end
18
+
19
+ Given /^I have a new session$/ do
20
+ @textarea = {}
21
+ @textarea[:text] = "Lorem ipsum dolor sit amet, consectetur adipiscing elit."
22
+ @textarea[:url] = "http://example.com/cucumber"
23
+ @session = Iated::EditSession.new @textarea
24
+ @sid = @session.sid
25
+ @session.change_id.should == 0
26
+ end
27
+
28
+ Given /^the session has been edited (\d+) times$/ do |count|
29
+ count.to_i.times.each do
30
+ # This fakes that the file has been edited.
31
+ @session.increment_change_id
32
+ end
33
+ end
34
+
35
+ ## Whens
36
+ When /^I GET (\/[a-z]+)( without a secret|)$/ do |url, junk|
37
+ get url
38
+ end
39
+
40
+ When /^the page should be "([^"]*)"$/ do |content_type| #"
41
+ last_response.content_type.should =~ Regexp.new("^#{Regexp.quote content_type}(;.*|)$")
42
+ end
43
+
44
+ When /^the page should say "([^"]*)"$/ do |body| #"
45
+ last_response.body.should == body
46
+ end
47
+
48
+ When /^I POST (\/[a-z]+) with the secret$/ do |url|
49
+ post url, :secret => @secret
50
+ last_response.status.should_not == 500
51
+ end
52
+
53
+ When /^I POST (\/[a-z]+) with the token$/ do |url|
54
+ post url, :token => @token
55
+ end
56
+
57
+ When /^I have a textarea to edit$/ do
58
+ @textarea = {}
59
+ @textarea[:text] = "Lorem ipsum dolor sit amet, consectetur adipiscing elit. Quisque ullamcorper metus lacinia risus congue sagittis. Integer enim libero, semper ut viverra eu, ullamcorper vitae urna. Quisque sit amet nibh urna, eget posuere nisi. Ut feugiat fermentum lacinia. Lorem ipsum dolor sit amet, consectetur adipiscing elit. Nullam vitae congue turpis. Sed orci mi, eleifend ut tristique ac, vehicula ac erat. Integer nisl quam, dictum vel posuere non, semper adipiscing massa. Ut in lectus id arcu adipiscing interdum. Aliquam orci lectus, semper vitae varius eget, rutrum et ante. Aenean sagittis scelerisque augue, consectetur blandit elit sodales id. Proin nibh nunc, aliquam id porttitor quis, facilisis id ante. Morbi aliquet suscipit augue. Integer molestie nisi ut libero consectetur vel blandit erat sagittis."
60
+ end
61
+
62
+ When /^the textarea has the id of "([^"]*)"$/ do |tid| #"
63
+ @textarea[:id] = tid
64
+ end
65
+
66
+ When /^the textarea has a url of "([^"]*)"$/ do |url| #"
67
+ @textarea[:url] = url
68
+ end
69
+
70
+ When /^I request an extension of "(\.[a-z]+)"$/ do |extension|
71
+ @textarea[:extension] = extension
72
+ end
73
+
74
+ When /^I POST an \/edit request$/ do
75
+ @textarea[:token] = @token
76
+ post "/edit", @textarea
77
+ end
78
+
79
+ When /^I GET \/edit\/<session id>\/(\d+)$/ do |change_id|
80
+ get "/edit/#{@sid}/#{change_id}"
81
+ end
82
+
83
+ ## Thens
84
+ Then /^the user should be shown the secret$/ do
85
+ Iated::mcp.should be_showing_secret
86
+ end
87
+
88
+ Then /^I should be sent a response with a token$/ do
89
+ last_response.status.should == 200
90
+ last_json["token"].should_not be_nil
91
+ @token = last_json["token"]
92
+ end
93
+
94
+ Then /^the token should be registered$/ do
95
+ Iated::mcp.is_token_valid?(@token).should be_true
96
+ # FIXME This is here because I'm having a hard time aggregating rcov reports.
97
+ Iated::mcp.cucumber_coverage_check
98
+ end
99
+
100
+ Then /^I expect the text to be sent$/ do
101
+ last_json["text"].should == @textarea[:text]
102
+ end
103
+
104
+ Then /^I expect a valid session id$/ do
105
+ last_response.status.should == 200
106
+ @sid = last_json["sid"]
107
+ @sid.should_not be_nil
108
+ @session = Iated::sessions[@sid]
109
+ @session.should_not be_nil
110
+ end
111
+
112
+ Then /^I expect an editor to be opened$/ do
113
+ @session.should be_running
114
+ end
115
+
116
+ Then /^the status should be "([a-z0-9]+)"$/ do |string|
117
+ last_json["status"].should be == string
118
+ end
119
+
120
+ Then /^I expect the editor file to have an extension of "(\.[a-z]+)"$/ do |ext|
121
+ @session.extension.should == @textarea[:extension]
122
+ end
123
+
124
+ Then /^I expect the editor file to have "([a-z0-9._-]+)" in it$/ do |string|
125
+ @session.filename.to_s.should =~ Regexp.new(Regexp.quote(string))
126
+ end
127
+
128
+ Then /^I expect a change\-count of (\d+)$/ do |change_count|
129
+ last_json["change_id"].should == change_count.to_i
130
+ end
131
+
132
+ Then /^I expect no text to be sent$/ do
133
+ last_json["text"].should be_nil
134
+ end
@@ -0,0 +1,47 @@
1
+ require 'rubygems'
2
+ require 'bundler/setup'
3
+
4
+ require 'pathname'
5
+ $: << (Pathname.new(__FILE__).dirname.dirname.dirname + 'src' + 'lib').to_s
6
+
7
+ require 'yaml'
8
+ require 'json'
9
+ require 'iated/server'
10
+
11
+ ## Force the application name because polyglot breaks the auto-detection logic.
12
+ #Sinatra::Application.app_file = app_file
13
+
14
+ set :environment, :test
15
+ Iated::environment = :test
16
+
17
+ require 'rspec/expectations'
18
+ require 'rack/test'
19
+ require 'webrat'
20
+
21
+ Webrat.configure do |config|
22
+ config.mode = :rack
23
+ end
24
+
25
+ class MyWorld
26
+ include Rack::Test::Methods
27
+ include Webrat::Methods
28
+ include Webrat::Matchers
29
+
30
+ Webrat::Methods.delegate_to_session :response_code, :response_body
31
+
32
+ def app
33
+ Iated::Server
34
+ end
35
+
36
+ def last_yaml
37
+ YAML::load(last_response.body)
38
+ end
39
+
40
+ def last_json
41
+ JSON::load(last_response.body)
42
+ end
43
+ end
44
+
45
+ World do
46
+ MyWorld.new
47
+ end
@@ -0,0 +1,11 @@
1
+
2
+ # Before each scenerio
3
+ Before do
4
+ Iated::reset
5
+ Iated::mcp.ui = :test
6
+ end
7
+
8
+ After do
9
+ # This cleans up test directories.
10
+ Iated::reset
11
+ end
@@ -0,0 +1,48 @@
1
+ # -*- encoding: utf-8 -*-
2
+ require File.expand_path('../lib/iated/version', __FILE__)
3
+
4
+ Gem::Specification.new do |gem|
5
+ gem.authors = ["Christian Höltje"]
6
+ gem.email = ["docwhat@gerf.org"]
7
+ gem.description = %q{The It's All Text! Editor Daemon}
8
+ gem.summary = <<-EOF
9
+ This is the core IAT Editor Daemon functionality as a gem.
10
+
11
+ It comes with the command line version of `iated` that
12
+ gives you all the functionality of the GUI versions.
13
+ EOF
14
+ gem.homepage = "http://github.com/docwhat/iated"
15
+
16
+ gem.executables = `git ls-files -- bin/*`.split("\n").map{ |f| File.basename(f) }
17
+ gem.files = `git ls-files`.split("\n")
18
+ gem.test_files = `git ls-files -- {test,spec,features}/*`.split("\n")
19
+ gem.name = "iated"
20
+ gem.require_paths = ["lib"]
21
+ gem.license = 'MIT'
22
+ gem.version = Iated::VERSION
23
+
24
+ gem.add_runtime_dependency "sinatra", "~> 1.3.3"
25
+ gem.add_runtime_dependency "haml", "~> 3.1.7"
26
+ gem.add_runtime_dependency "json", "~> 1.1"
27
+ gem.add_runtime_dependency "addressable", "~> 2.3.2"
28
+ gem.add_runtime_dependency "coffee-script"
29
+ gem.add_runtime_dependency "sass"
30
+
31
+ gem.add_development_dependency "rake", "~> 0.9.2"
32
+ gem.add_development_dependency "rspec", "> 2.0"
33
+ gem.add_development_dependency "yard"
34
+ gem.add_development_dependency "kramdown"
35
+
36
+ gem.add_development_dependency "cucumber", ">= 1.1.0"
37
+ gem.add_development_dependency "webrat"
38
+ gem.add_development_dependency "syntax"
39
+ gem.add_development_dependency "rack-test"
40
+
41
+ gem.add_development_dependency "guard", ">= 1.3.0"
42
+ gem.add_development_dependency "guard-bundler"
43
+ gem.add_development_dependency "guard-cucumber"
44
+ gem.add_development_dependency "guard-rspec"
45
+ gem.add_development_dependency "rb-fsevent"
46
+ gem.add_development_dependency "terminal-notifier-guard"
47
+ gem.add_development_dependency "growl"
48
+ end
@@ -0,0 +1,111 @@
1
+ require 'pathname'
2
+ # Load all handlers in the iated/ directory
3
+ Pathname.glob(Pathname.new(__FILE__).dirname + 'iated' + 'pages' + '*.rb').each do |path|
4
+ require path.to_s
5
+ end
6
+
7
+ require 'sinatra'
8
+ require 'haml'
9
+ require 'iated/mcp'
10
+ require 'iated/edit_session'
11
+ require 'iated/browser_token_db'
12
+ require 'optparse'
13
+
14
+ if RUBY_ENGINE == "jruby"
15
+ # Load all the jars in the lib directory.
16
+ Dir[File.join(File.dirname(__FILE__), '*.jar')].each do |jar|
17
+ require jar
18
+ end
19
+ end
20
+
21
+
22
+ module Iated
23
+ class Application
24
+
25
+ #:nocov:
26
+ def initialize
27
+ @mcp = nil
28
+ @optparse = OptionParser.new do |opts|
29
+ opts.banner = "Usage: #{opts.program_name} [OPTIONS]"
30
+
31
+ opts.on('-p', '--port PORT', Integer,
32
+ "The port number to run the server on (default: #{Iated.mcp.prefs.port}).") do |p|
33
+ Iated.mcp.prefs.port = p
34
+ end
35
+
36
+ opts.on('-e', '--editor EDITOR', "Set editor (default #{Iated.mcp.prefs.editor}).") do |editor|
37
+ Iated.mcp.prefs.editor = editor
38
+ end
39
+
40
+ opts.on('-u', '--ui UI', "Set the UI to be 'gui' or 'text' (default #{Iated.mcp.ui}).") do |ui|
41
+ Iated.mcp.ui = ui.to_sym
42
+ end
43
+
44
+ opts.on('-d', '--debug', "Turn on debugging mode.") do
45
+ Iated.mcp.debug = true
46
+ end
47
+
48
+ opts.on_tail('-h', '--help', 'Show this help.') do
49
+ puts opts
50
+ exit
51
+ end
52
+ end
53
+
54
+ end
55
+ #:nocov:
56
+
57
+ #:nocov:
58
+ def run
59
+ @optparse.parse!
60
+ Iated.mcp.begin!
61
+ end
62
+ #:nocov:
63
+
64
+ end
65
+
66
+ # @return [Hash] Sessions
67
+ def self.sessions
68
+ # TODO This needs to be replaced with a real persistant data store.
69
+ @sessions ||= {}
70
+ end
71
+
72
+ # The current environment
73
+ # @return [Symbol] Returns one of: `:test`, `:development`, `:production`
74
+ def self.environment
75
+ @environment ||= :development
76
+ end
77
+
78
+ # Set the current environment
79
+ # @return [Symbol] The symbol set.
80
+ def self.environment= env
81
+ if [:test, :development, :production].include? env
82
+ @environment = env
83
+ @mcp.prefs.reset unless @mcp.nil?
84
+ else
85
+ raise "Invalid Iated::environment specified: #{env.inspect}"
86
+ end
87
+ end
88
+
89
+ ## Resets the Master Control Program, Preferences, and Sessions
90
+ # @return [nil]
91
+ def self.reset
92
+ @mcp.prefs.reset unless @mcp.nil?
93
+ @mcp = nil
94
+ @sessions = nil
95
+ end
96
+
97
+ ## Deletes all state from the system
98
+ # @return [nil]
99
+ def self.purge
100
+ dir = self.mcp.prefs.config_dir
101
+ dir.rmtree if dir.directory?
102
+ end
103
+
104
+ ## Access to the Master Control Program
105
+ # @return [Iated::MCP]
106
+ def self.mcp
107
+ @mcp ||= Iated::MCP.new
108
+ end
109
+
110
+ end
111
+