iated 0.0.1
Sign up to get free protection for your applications and to get access to all the features.
- data/.gitignore +6 -0
- data/Gemfile +3 -0
- data/Guardfile +24 -0
- data/LICENSE +23 -0
- data/Makefile +72 -0
- data/README.md +87 -0
- data/Rakefile +10 -0
- data/bin/iated +13 -0
- data/config.ru +5 -0
- data/extensions/chrome/background.html +5 -0
- data/extensions/chrome/background.js +105 -0
- data/extensions/chrome/contentscript.css +0 -0
- data/extensions/chrome/contentscript.js +110 -0
- data/extensions/chrome/jquery-ui.js +45 -0
- data/extensions/chrome/jquery.js +16 -0
- data/extensions/chrome/jquery.updater.js +46 -0
- data/extensions/chrome/manifest.json +19 -0
- data/extensions/chrome/yaml.js +489 -0
- data/extensions/tests/simple.html +23 -0
- data/features/extension_authenticates.feature +30 -0
- data/features/extension_edits.feature +45 -0
- data/features/step_definitions/extension_steps.rb +134 -0
- data/features/support/env.rb +47 -0
- data/features/support/hooks.rb +11 -0
- data/iated.gemspec +48 -0
- data/lib/iated.rb +111 -0
- data/lib/iated/browser_token_db.rb +76 -0
- data/lib/iated/edit_session.rb +221 -0
- data/lib/iated/helpers.rb +9 -0
- data/lib/iated/mcp.rb +144 -0
- data/lib/iated/page_helpers.rb +33 -0
- data/lib/iated/public/jquery-ui.js +101 -0
- data/lib/iated/public/jquery.js +2 -0
- data/lib/iated/public/robots.txt +5 -0
- data/lib/iated/server.rb +162 -0
- data/lib/iated/sys_pref.rb +201 -0
- data/lib/iated/version.rb +3 -0
- data/lib/iated/views/hello.haml +13 -0
- data/lib/iated/views/preferences.haml +27 -0
- data/lib/iated/views/reference.coffee +79 -0
- data/lib/iated/views/reference.haml +94 -0
- data/lib/iated/views/reference.scss +36 -0
- data/lib/iated/views/root.haml +13 -0
- data/spec/lib/iated/browser_token_db_spec.rb +68 -0
- data/spec/lib/iated/edit_session_spec.rb +157 -0
- data/spec/lib/iated/mcp_spec.rb +86 -0
- data/spec/lib/iated/sys_pref_spec.rb +40 -0
- data/spec/protocol/edit_spec.rb +88 -0
- data/spec/protocol/hello_spec.rb +18 -0
- data/spec/protocol/notfound_spec.rb +11 -0
- data/spec/protocol/ping_spec.rb +10 -0
- data/spec/protocol/preferences_spec.rb +35 -0
- data/spec/spec_helper.rb +21 -0
- 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
|
data/iated.gemspec
ADDED
@@ -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
|
data/lib/iated.rb
ADDED
@@ -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
|
+
|