copycopter_client 1.0.0.beta1
Sign up to get free protection for your applications and to get access to all the features.
- data/MIT-LICENSE +20 -0
- data/README.textile +71 -0
- data/Rakefile +38 -0
- data/features/rails.feature +267 -0
- data/features/step_definitions/copycopter_server_steps.rb +65 -0
- data/features/step_definitions/rails_steps.rb +134 -0
- data/features/support/env.rb +8 -0
- data/features/support/rails_server.rb +118 -0
- data/init.rb +2 -0
- data/lib/copycopter_client/client.rb +117 -0
- data/lib/copycopter_client/configuration.rb +197 -0
- data/lib/copycopter_client/errors.rb +13 -0
- data/lib/copycopter_client/helper.rb +40 -0
- data/lib/copycopter_client/i18n_backend.rb +100 -0
- data/lib/copycopter_client/prefixed_logger.rb +41 -0
- data/lib/copycopter_client/rails.rb +31 -0
- data/lib/copycopter_client/railtie.rb +13 -0
- data/lib/copycopter_client/sync.rb +145 -0
- data/lib/copycopter_client/version.rb +8 -0
- data/lib/copycopter_client.rb +58 -0
- data/lib/tasks/copycopter_client_tasks.rake +6 -0
- data/spec/copycopter_client/client_spec.rb +208 -0
- data/spec/copycopter_client/configuration_spec.rb +252 -0
- data/spec/copycopter_client/helper_spec.rb +86 -0
- data/spec/copycopter_client/i18n_backend_spec.rb +133 -0
- data/spec/copycopter_client/prefixed_logger_spec.rb +25 -0
- data/spec/copycopter_client/sync_spec.rb +295 -0
- data/spec/spec.opts +2 -0
- data/spec/spec_helper.rb +30 -0
- data/spec/support/client_spec_helpers.rb +9 -0
- data/spec/support/defines_constants.rb +38 -0
- data/spec/support/fake_client.rb +42 -0
- data/spec/support/fake_copycopter_app.rb +136 -0
- data/spec/support/fake_html_safe_string.rb +20 -0
- data/spec/support/fake_logger.rb +68 -0
- data/spec/support/fake_passenger.rb +27 -0
- data/spec/support/fake_unicorn.rb +14 -0
- metadata +121 -0
@@ -0,0 +1,136 @@
|
|
1
|
+
require 'sinatra/base'
|
2
|
+
require 'json'
|
3
|
+
require 'thin'
|
4
|
+
|
5
|
+
class FakeCopycopterApp < Sinatra::Base
|
6
|
+
def self.start
|
7
|
+
Thread.new do
|
8
|
+
Thin::Logging.silent = true
|
9
|
+
Rack::Handler::Thin.run(self, :Port => port)
|
10
|
+
end
|
11
|
+
end
|
12
|
+
|
13
|
+
def self.port
|
14
|
+
(ENV['COPYCOPTER_PORT'] || 3002).to_i
|
15
|
+
end
|
16
|
+
|
17
|
+
def self.add_project(api_key)
|
18
|
+
Project.create(api_key)
|
19
|
+
end
|
20
|
+
|
21
|
+
def self.reset
|
22
|
+
Project.delete_all
|
23
|
+
end
|
24
|
+
|
25
|
+
def self.project(api_key)
|
26
|
+
Project.find(api_key)
|
27
|
+
end
|
28
|
+
|
29
|
+
def with_project(api_key)
|
30
|
+
if api_key == 'raise_error'
|
31
|
+
halt 500, { :error => "Blah ha" }.to_json
|
32
|
+
elsif project = Project.find(api_key)
|
33
|
+
yield(project)
|
34
|
+
else
|
35
|
+
halt 404, { :error => "No such project" }.to_json
|
36
|
+
end
|
37
|
+
end
|
38
|
+
|
39
|
+
get "/api/v2/projects/:api_key/published_blurbs" do |api_key|
|
40
|
+
with_project(api_key) { |project| project.published.to_json }
|
41
|
+
end
|
42
|
+
|
43
|
+
get "/api/v2/projects/:api_key/draft_blurbs" do |api_key|
|
44
|
+
with_project(api_key) { |project| project.draft.to_json }
|
45
|
+
end
|
46
|
+
|
47
|
+
post "/api/v2/projects/:api_key/draft_blurbs" do |api_key|
|
48
|
+
with_project(api_key) do |project|
|
49
|
+
data = JSON.parse(request.body.read)
|
50
|
+
project.update('draft' => data)
|
51
|
+
201
|
52
|
+
end
|
53
|
+
end
|
54
|
+
|
55
|
+
post "/api/v2/projects/:api_key/deploys" do |api_key|
|
56
|
+
with_project(api_key) do |project|
|
57
|
+
project.deploy
|
58
|
+
201
|
59
|
+
end
|
60
|
+
end
|
61
|
+
|
62
|
+
class Project
|
63
|
+
attr_reader :draft, :published, :api_key
|
64
|
+
|
65
|
+
def initialize(attrs)
|
66
|
+
@api_key = attrs['api_key']
|
67
|
+
@draft = attrs['draft'] || {}
|
68
|
+
@published = attrs['published'] || {}
|
69
|
+
end
|
70
|
+
|
71
|
+
def to_hash
|
72
|
+
{ 'api_key' => @api_key,
|
73
|
+
'draft' => @draft,
|
74
|
+
'published' => @published }
|
75
|
+
end
|
76
|
+
|
77
|
+
def update(attrs)
|
78
|
+
@draft. update(attrs['draft']) if attrs['draft']
|
79
|
+
@published.update(attrs['published']) if attrs['published']
|
80
|
+
self.class.save(self)
|
81
|
+
end
|
82
|
+
|
83
|
+
def reload
|
84
|
+
self.class.find(api_key)
|
85
|
+
end
|
86
|
+
|
87
|
+
def deploy
|
88
|
+
@published.update(@draft)
|
89
|
+
self.class.save(self)
|
90
|
+
end
|
91
|
+
|
92
|
+
def self.create(api_key)
|
93
|
+
project = Project.new('api_key' => api_key)
|
94
|
+
save(project)
|
95
|
+
project
|
96
|
+
end
|
97
|
+
|
98
|
+
def self.find(api_key)
|
99
|
+
open_project_data do |data|
|
100
|
+
if project_hash = data[api_key]
|
101
|
+
Project.new(project_hash.dup)
|
102
|
+
else
|
103
|
+
nil
|
104
|
+
end
|
105
|
+
end
|
106
|
+
end
|
107
|
+
|
108
|
+
def self.delete_all
|
109
|
+
open_project_data do |data|
|
110
|
+
data.clear
|
111
|
+
end
|
112
|
+
end
|
113
|
+
|
114
|
+
def self.save(project)
|
115
|
+
open_project_data do |data|
|
116
|
+
data[project.api_key] = project.to_hash
|
117
|
+
end
|
118
|
+
end
|
119
|
+
|
120
|
+
def self.open_project_data
|
121
|
+
project_file = File.expand_path('/../../../tmp/projects.json', __FILE__)
|
122
|
+
if File.exist?(project_file)
|
123
|
+
data = JSON.parse(IO.read(project_file))
|
124
|
+
else
|
125
|
+
data = {}
|
126
|
+
end
|
127
|
+
|
128
|
+
result = yield(data)
|
129
|
+
|
130
|
+
File.open(project_file, "w") { |file| file.write(data.to_json) }
|
131
|
+
|
132
|
+
result
|
133
|
+
end
|
134
|
+
end
|
135
|
+
end
|
136
|
+
|
@@ -0,0 +1,20 @@
|
|
1
|
+
class FakeHtmlSafeString < String
|
2
|
+
def initialize(*args)
|
3
|
+
super(*args)
|
4
|
+
@html_safe = false
|
5
|
+
end
|
6
|
+
|
7
|
+
def html_safe
|
8
|
+
dup.html_safe!
|
9
|
+
end
|
10
|
+
|
11
|
+
def html_safe!
|
12
|
+
@html_safe = true
|
13
|
+
self
|
14
|
+
end
|
15
|
+
|
16
|
+
def html_safe?
|
17
|
+
@html_safe
|
18
|
+
end
|
19
|
+
end
|
20
|
+
|
@@ -0,0 +1,68 @@
|
|
1
|
+
class FakeLogger
|
2
|
+
def initialize
|
3
|
+
@entries = {
|
4
|
+
:info => [],
|
5
|
+
:debug => [],
|
6
|
+
:warn => [],
|
7
|
+
:error => [],
|
8
|
+
:fatal => [],
|
9
|
+
}
|
10
|
+
end
|
11
|
+
|
12
|
+
def info(message = nil, &block)
|
13
|
+
log(:info, message, &block)
|
14
|
+
end
|
15
|
+
|
16
|
+
def debug(message = nil, &block)
|
17
|
+
log(:debug, message, &block)
|
18
|
+
end
|
19
|
+
|
20
|
+
def warn(message = nil, &block)
|
21
|
+
log(:warn, message, &block)
|
22
|
+
end
|
23
|
+
|
24
|
+
def error(message = nil, &block)
|
25
|
+
log(:error, message, &block)
|
26
|
+
end
|
27
|
+
|
28
|
+
def fatal(message = nil, &block)
|
29
|
+
log(:fatal, message, &block)
|
30
|
+
end
|
31
|
+
|
32
|
+
def log(severity, message = nil, &block)
|
33
|
+
message ||= block.call
|
34
|
+
@entries[severity] << message
|
35
|
+
end
|
36
|
+
|
37
|
+
def has_entry?(level, expected_entry)
|
38
|
+
@entries[level].any? { |actual_entry| actual_entry.include?(expected_entry) }
|
39
|
+
end
|
40
|
+
|
41
|
+
attr_reader :entries
|
42
|
+
end
|
43
|
+
|
44
|
+
Spec::Matchers.define :have_entry do |severity, entry|
|
45
|
+
match do |logger|
|
46
|
+
@logger = logger
|
47
|
+
logger.has_entry?(severity, entry)
|
48
|
+
end
|
49
|
+
|
50
|
+
failure_message_for_should do
|
51
|
+
"Expected #{severity}(#{entry.inspect}); got entries:\n\n#{entries}"
|
52
|
+
end
|
53
|
+
|
54
|
+
failure_message_for_should_not do
|
55
|
+
"Unexpected #{severity}(#{entry.inspect}); got entries:\n\n#{entries}"
|
56
|
+
end
|
57
|
+
|
58
|
+
def entries
|
59
|
+
lines = @logger.entries.inject([]) do |result, (severity, entries)|
|
60
|
+
if entries.empty?
|
61
|
+
result
|
62
|
+
else
|
63
|
+
result << "#{severity}:\n#{entries.join("\n")}"
|
64
|
+
end
|
65
|
+
end
|
66
|
+
lines.join("\n\n")
|
67
|
+
end
|
68
|
+
end
|
@@ -0,0 +1,27 @@
|
|
1
|
+
class FakePassenger
|
2
|
+
def initialize
|
3
|
+
@handlers = {}
|
4
|
+
end
|
5
|
+
|
6
|
+
def on_event(name, &handler)
|
7
|
+
@handlers[name] ||= []
|
8
|
+
@handlers[name] << handler
|
9
|
+
end
|
10
|
+
|
11
|
+
def call_event(name, *args)
|
12
|
+
if @handlers[name]
|
13
|
+
@handlers[name].each do |handler|
|
14
|
+
handler.call(*args)
|
15
|
+
end
|
16
|
+
end
|
17
|
+
end
|
18
|
+
|
19
|
+
def become_master
|
20
|
+
$0 = "PassengerApplicationSpawner"
|
21
|
+
end
|
22
|
+
|
23
|
+
def spawn
|
24
|
+
$0 = "PassengerFork"
|
25
|
+
call_event(:starting_worker_process, true)
|
26
|
+
end
|
27
|
+
end
|
metadata
ADDED
@@ -0,0 +1,121 @@
|
|
1
|
+
--- !ruby/object:Gem::Specification
|
2
|
+
name: copycopter_client
|
3
|
+
version: !ruby/object:Gem::Version
|
4
|
+
hash: 62196353
|
5
|
+
prerelease: 6
|
6
|
+
segments:
|
7
|
+
- 1
|
8
|
+
- 0
|
9
|
+
- 0
|
10
|
+
- beta
|
11
|
+
- 1
|
12
|
+
version: 1.0.0.beta1
|
13
|
+
platform: ruby
|
14
|
+
authors:
|
15
|
+
- thoughtbot
|
16
|
+
autorequire:
|
17
|
+
bindir: bin
|
18
|
+
cert_chain: []
|
19
|
+
|
20
|
+
date: 2010-06-04 00:00:00 -04:00
|
21
|
+
default_executable:
|
22
|
+
dependencies:
|
23
|
+
- !ruby/object:Gem::Dependency
|
24
|
+
name: i18n
|
25
|
+
prerelease: false
|
26
|
+
requirement: &id001 !ruby/object:Gem::Requirement
|
27
|
+
none: false
|
28
|
+
requirements:
|
29
|
+
- - ">="
|
30
|
+
- !ruby/object:Gem::Version
|
31
|
+
hash: 3
|
32
|
+
segments:
|
33
|
+
- 0
|
34
|
+
version: "0"
|
35
|
+
type: :runtime
|
36
|
+
version_requirements: *id001
|
37
|
+
description:
|
38
|
+
email: support@thoughtbot.com
|
39
|
+
executables: []
|
40
|
+
|
41
|
+
extensions: []
|
42
|
+
|
43
|
+
extra_rdoc_files:
|
44
|
+
- README.textile
|
45
|
+
files:
|
46
|
+
- MIT-LICENSE
|
47
|
+
- Rakefile
|
48
|
+
- init.rb
|
49
|
+
- lib/copycopter_client/client.rb
|
50
|
+
- lib/copycopter_client/configuration.rb
|
51
|
+
- lib/copycopter_client/errors.rb
|
52
|
+
- lib/copycopter_client/helper.rb
|
53
|
+
- lib/copycopter_client/i18n_backend.rb
|
54
|
+
- lib/copycopter_client/prefixed_logger.rb
|
55
|
+
- lib/copycopter_client/rails.rb
|
56
|
+
- lib/copycopter_client/railtie.rb
|
57
|
+
- lib/copycopter_client/sync.rb
|
58
|
+
- lib/copycopter_client/version.rb
|
59
|
+
- lib/copycopter_client.rb
|
60
|
+
- lib/tasks/copycopter_client_tasks.rake
|
61
|
+
- spec/copycopter_client/client_spec.rb
|
62
|
+
- spec/copycopter_client/configuration_spec.rb
|
63
|
+
- spec/copycopter_client/helper_spec.rb
|
64
|
+
- spec/copycopter_client/i18n_backend_spec.rb
|
65
|
+
- spec/copycopter_client/prefixed_logger_spec.rb
|
66
|
+
- spec/copycopter_client/sync_spec.rb
|
67
|
+
- spec/spec.opts
|
68
|
+
- spec/spec_helper.rb
|
69
|
+
- spec/support/client_spec_helpers.rb
|
70
|
+
- spec/support/defines_constants.rb
|
71
|
+
- spec/support/fake_client.rb
|
72
|
+
- spec/support/fake_copycopter_app.rb
|
73
|
+
- spec/support/fake_html_safe_string.rb
|
74
|
+
- spec/support/fake_logger.rb
|
75
|
+
- spec/support/fake_passenger.rb
|
76
|
+
- spec/support/fake_unicorn.rb
|
77
|
+
- features/rails.feature
|
78
|
+
- features/step_definitions/copycopter_server_steps.rb
|
79
|
+
- features/step_definitions/rails_steps.rb
|
80
|
+
- features/support/env.rb
|
81
|
+
- features/support/rails_server.rb
|
82
|
+
- README.textile
|
83
|
+
has_rdoc: true
|
84
|
+
homepage: http://github.com/thoughtbot/copycopter_client
|
85
|
+
licenses: []
|
86
|
+
|
87
|
+
post_install_message:
|
88
|
+
rdoc_options:
|
89
|
+
- --line-numbers
|
90
|
+
- --main
|
91
|
+
require_paths:
|
92
|
+
- lib
|
93
|
+
required_ruby_version: !ruby/object:Gem::Requirement
|
94
|
+
none: false
|
95
|
+
requirements:
|
96
|
+
- - ">="
|
97
|
+
- !ruby/object:Gem::Version
|
98
|
+
hash: 3
|
99
|
+
segments:
|
100
|
+
- 0
|
101
|
+
version: "0"
|
102
|
+
required_rubygems_version: !ruby/object:Gem::Requirement
|
103
|
+
none: false
|
104
|
+
requirements:
|
105
|
+
- - ">"
|
106
|
+
- !ruby/object:Gem::Version
|
107
|
+
hash: 25
|
108
|
+
segments:
|
109
|
+
- 1
|
110
|
+
- 3
|
111
|
+
- 1
|
112
|
+
version: 1.3.1
|
113
|
+
requirements: []
|
114
|
+
|
115
|
+
rubyforge_project: copycopter_client
|
116
|
+
rubygems_version: 1.4.1
|
117
|
+
signing_key:
|
118
|
+
specification_version: 3
|
119
|
+
summary: Client for the Copycopter content management service
|
120
|
+
test_files: []
|
121
|
+
|