xing-backend 0.0.19 → 0.0.20
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +4 -4
- data/app/models/role.rb +24 -0
- data/config/locales/json.yml +29 -0
- data/config/routes.rb +3 -0
- data/db/migrate/20140828011806_initial.rb +45 -0
- data/db/migrate/20140914030703_devise_token_auth_add_token_info_to_users.rb +20 -0
- data/db/migrate/20140929192921_remove_login_from_users.rb +5 -0
- data/lib/xing-backend.rb +1 -0
- data/lib/xing/engine.rb +4 -0
- data/lib/xing/nominal/database_config_validator.rb +31 -0
- data/lib/xing/nominal/dependency_utils.rb +26 -0
- data/lib/xing/nominal/secrets_validator.rb +50 -0
- data/lib/xing/nominal/yaml_config_validator.rb +44 -0
- data/lib/xing/services.rb +0 -2
- data/lib/xing/services/class_registry.rb +31 -0
- data/lib/xing/services/{page_wrapper.rb → paged_wrapper.rb} +2 -2
- data/lib/xing/snapshot.rb +4 -0
- data/lib/xing/snapshot/domain_helpers.rb +24 -0
- data/lib/xing/snapshot/fetcher.rb +35 -0
- data/lib/xing/snapshot/local_site_snapshot.rb +37 -0
- data/lib/xing/snapshot/remote_site_snapshot.rb +16 -0
- data/lib/xing/snapshot/site_page_set.rb +29 -0
- data/lib/xing/snapshot/site_snapshot.rb +41 -0
- data/lib/xing/snapshot/sitemap.rb +68 -0
- data/lib/xing/snapshot/writer.rb +15 -0
- data/lib/xing/spec_helpers.rb +7 -0
- data/lib/xing/spec_helpers/api_response_matchers.rb +26 -0
- data/lib/xing/spec_helpers/ci_support.rb +6 -0
- data/lib/xing/spec_helpers/dom_equiv.rb +26 -0
- data/lib/xing/spec_helpers/json_requests.rb +58 -0
- data/lib/xing/spec_helpers/routing_spec_patch.rb +28 -0
- data/lib/xing/spec_helpers/split_servers.rb +15 -0
- data/lib/xing/spec_helpers/test_url_helpers.rb +5 -0
- data/lib/xing/static.rb +1 -0
- data/lib/xing/static/backend_url_cookie.rb +16 -0
- data/lib/xing/static/goto_param.rb +30 -0
- data/lib/xing/static/logger.rb +11 -0
- data/lib/xing/static/rack_app.rb +40 -0
- data/lib/xing/tasks/all.rake +4 -0
- data/lib/xing/tasks/db_recycle.rake +4 -0
- data/lib/xing/tasks/dependencies_common.rake +49 -0
- data/lib/xing/tasks/sample_data.rake +49 -0
- data/lib/xing/tasks/take_snapshot.rake +4 -0
- data/spec/xing/builders/list_builder_spec.rb +0 -2
- data/spec/xing/builders/ordered_list_builder_spec.rb +0 -2
- data/spec/xing/nominal/database_config_validator_spec.rb +98 -0
- data/spec/xing/nominal/secrets_validator_spec.rb +78 -0
- data/spec/xing/serializers/list_spec.rb +116 -0
- data/spec/xing/serializers/paged_index_spec.rb +2 -2
- data/spec/xing/serializers/paged_list_spec.rb +1 -1
- data/spec/xing/services/error_converter_spec.rb +1 -3
- data/spec/xing/services/paged_wrapper_spec.rb +30 -0
- data/spec/xing/snapshot/remote_snapshot_fetcher_spec.rb +81 -0
- data/spec/xing/{services → snapshot}/snapshot_fetcher_spec.rb +6 -4
- data/spec_help/dummy/db/test.sqlite3 +0 -0
- data/spec_help/dummy/log/test.log +143 -0
- data/spec_help/file-sandbox.rb +164 -0
- data/spec_help/spec_helper.rb +0 -2
- metadata +152 -12
- data/lib/xing/services/snapshot_fetcher.rb +0 -33
- data/lib/xing/services/snapshot_writer.rb +0 -19
@@ -0,0 +1,40 @@
|
|
1
|
+
require 'xing/static/backend_url_cookie'
|
2
|
+
require 'xing/static/goto_param'
|
3
|
+
require 'xing/static/logger'
|
4
|
+
|
5
|
+
module Xing
|
6
|
+
module Static
|
7
|
+
class RackApp
|
8
|
+
# Should be override by client app. Ironically, override with exactly
|
9
|
+
# this definition will usually work.
|
10
|
+
def self.log_root
|
11
|
+
File.expand_path("../../log", __FILE__)
|
12
|
+
end
|
13
|
+
|
14
|
+
def self.logpath_for_env(env)
|
15
|
+
File.join( log_root, "#{env}_static.log")
|
16
|
+
end
|
17
|
+
|
18
|
+
def self.build(root_path, backend_port)
|
19
|
+
backend_url = "http://localhost:#{backend_port}/"
|
20
|
+
env = ENV['RAILS_ENV'] || 'development'
|
21
|
+
logger = Logger.new(logpath_for_env(env))
|
22
|
+
|
23
|
+
Rack::Builder.new do
|
24
|
+
use BackendUrlCookie, backend_url
|
25
|
+
use GotoParam
|
26
|
+
use Rack::CommonLogger, logger
|
27
|
+
use Rack::Static, {
|
28
|
+
:urls => [""],
|
29
|
+
:root => root_path,
|
30
|
+
:index => "index.html",
|
31
|
+
:header_rules => {
|
32
|
+
:all => {"Cache-Control" => "no-cache, max-age=0" } #no caching development assets
|
33
|
+
}
|
34
|
+
}
|
35
|
+
run proc{}
|
36
|
+
end
|
37
|
+
end
|
38
|
+
end
|
39
|
+
end
|
40
|
+
end
|
@@ -0,0 +1,49 @@
|
|
1
|
+
require 'xing/nominal/secrets_validator'
|
2
|
+
require 'xing/nominal/database_config_validator'
|
3
|
+
|
4
|
+
namespace :dependencies do
|
5
|
+
include Xing::Nominal::DependencyUtils
|
6
|
+
|
7
|
+
task :check => [
|
8
|
+
:secrets,
|
9
|
+
:database_config,
|
10
|
+
'redis:running'
|
11
|
+
]
|
12
|
+
namespace :check do
|
13
|
+
task :tmux => 'dependencies:check' do
|
14
|
+
sh_or_fail "which tmux", "tmux is not installed."
|
15
|
+
sh_or_fail "which realpath", "realpath is not installed. Mac users see: http://j.mp/mac_realpath"
|
16
|
+
end
|
17
|
+
end
|
18
|
+
|
19
|
+
task :secrets do
|
20
|
+
env = ENV['RAILS_ENV'] || 'development'
|
21
|
+
validator = Xing::Nominal::SecretsValidator.new
|
22
|
+
validator.assert_existence
|
23
|
+
validator.validate(env)
|
24
|
+
validator.report!
|
25
|
+
end
|
26
|
+
|
27
|
+
task :database_config do
|
28
|
+
env = ENV['RAILS_ENV'] || 'development'
|
29
|
+
validator = Xing::Nominal::DatabaseConfigValidator.new
|
30
|
+
validator.assert_existence
|
31
|
+
validator.validate(env)
|
32
|
+
validator.report!
|
33
|
+
end
|
34
|
+
|
35
|
+
task :api_host do
|
36
|
+
# TODO - if in development, check that the API host resolves
|
37
|
+
end
|
38
|
+
|
39
|
+
namespace :redis do
|
40
|
+
task :running => :installed do
|
41
|
+
sh_or_fail "redis-cli ping", "Redis is not running."
|
42
|
+
end
|
43
|
+
task :installed do
|
44
|
+
sh_or_fail "which redis-cli", "Redis is not installed."
|
45
|
+
end
|
46
|
+
end
|
47
|
+
|
48
|
+
|
49
|
+
end
|
@@ -0,0 +1,49 @@
|
|
1
|
+
# NOTE: This task exists to create fake data for the purposes of
|
2
|
+
# demonstrating the site to a client during development. So whatever
|
3
|
+
# scaffolds we create should get a method in here to generate some
|
4
|
+
# fake entries. Most of it should be lipsum.
|
5
|
+
|
6
|
+
# IT SHOULD NOT CONTAIN any data absolutely required for the site to work,
|
7
|
+
# especially that we might need in testing. For example, groups for 'users'
|
8
|
+
# and 'admins' if we are using an authorization system. Such things should
|
9
|
+
# go in seeds.rb.
|
10
|
+
#
|
11
|
+
# Once the client has real data ... i.e. an initial set of pages and/or
|
12
|
+
# a menu/location tree, those should replace the lorem data.
|
13
|
+
|
14
|
+
class Array
|
15
|
+
# If +number+ is greater than the size of the array, the method
|
16
|
+
# will simply return the array itself sorted randomly
|
17
|
+
# defaults to picking one item
|
18
|
+
def pick(number = 1)
|
19
|
+
if (number == 1)
|
20
|
+
sort_by{ rand }[0]
|
21
|
+
else
|
22
|
+
sort_by{ rand }.slice(0...number)
|
23
|
+
end
|
24
|
+
end
|
25
|
+
end
|
26
|
+
|
27
|
+
# Do something sometimes (with probability p).
|
28
|
+
def sometimes(p, &block)
|
29
|
+
yield(block) if rand <= p
|
30
|
+
end
|
31
|
+
|
32
|
+
namespace :db do
|
33
|
+
namespace :sample_data do
|
34
|
+
|
35
|
+
desc "Wipe the database and reload"
|
36
|
+
task :reload => [ :wipe, 'db:seed', :load]
|
37
|
+
|
38
|
+
desc "Destroy the database and reload sample data"
|
39
|
+
task :recycle => [ 'db:recycle', :load ]
|
40
|
+
|
41
|
+
task :wipe => [ :environment ] do
|
42
|
+
end
|
43
|
+
|
44
|
+
desc "Fill the database with sample data for demo purposes"
|
45
|
+
task :load => [ :environment, :seed ]
|
46
|
+
end
|
47
|
+
end
|
48
|
+
|
49
|
+
Dir[Rails.root.join("db/sample_data/**/*.{rb,rake}")].each {|f| load f}
|
@@ -0,0 +1,98 @@
|
|
1
|
+
require 'xing/nominal/database_config_validator'
|
2
|
+
|
3
|
+
describe Xing::Nominal::DatabaseConfigValidator do
|
4
|
+
|
5
|
+
before do
|
6
|
+
expect(File).to receive(:open).and_return(nil)
|
7
|
+
end
|
8
|
+
|
9
|
+
let :dev_yaml do
|
10
|
+
YAML.load(DEV_FIXTURE)
|
11
|
+
end
|
12
|
+
|
13
|
+
let :prod_yaml do
|
14
|
+
YAML.load(PROD_FIXTURE)
|
15
|
+
end
|
16
|
+
|
17
|
+
it "when development has a missing key" do
|
18
|
+
expect(YAML).to receive(:load).and_return(dev_yaml.deep_merge({'development' => { 'database' => nil }}))
|
19
|
+
validator = Xing::Nominal::DatabaseConfigValidator.new
|
20
|
+
validator.validate('development')
|
21
|
+
expect(validator.errors['development']).not_to be_blank
|
22
|
+
expect(validator.errors['test']).to be_blank
|
23
|
+
end
|
24
|
+
|
25
|
+
it "succeeds when development has no missing keys" do
|
26
|
+
expect(YAML).to receive(:load).and_return(dev_yaml)
|
27
|
+
validator = Xing::Nominal::DatabaseConfigValidator.new
|
28
|
+
validator.validate('development')
|
29
|
+
expect(validator.errors['development']).to be_blank
|
30
|
+
expect(validator.errors['test']).to be_blank
|
31
|
+
end
|
32
|
+
|
33
|
+
it "when development has a missing key and test has a misformatted value " do
|
34
|
+
expect(YAML).to receive(:load).and_return(dev_yaml.deep_merge({
|
35
|
+
'development' => { 'adapter' => nil },
|
36
|
+
'test' => { 'database' => 1000 }})
|
37
|
+
)
|
38
|
+
validator = Xing::Nominal::DatabaseConfigValidator.new
|
39
|
+
validator.validate('development')
|
40
|
+
expect(validator.errors['development']['adapter']).not_to be_blank
|
41
|
+
expect(validator.errors['test']['database']).not_to be_blank
|
42
|
+
end
|
43
|
+
|
44
|
+
context "production" do
|
45
|
+
it "succeeds when all values are present" do
|
46
|
+
expect(YAML).to receive(:load).and_return(prod_yaml)
|
47
|
+
validator = Xing::Nominal::DatabaseConfigValidator.new
|
48
|
+
validator.validate('production')
|
49
|
+
expect(validator.errors).to be_blank
|
50
|
+
end
|
51
|
+
|
52
|
+
it "requires username" do
|
53
|
+
expect(YAML).to receive(:load).and_return(prod_yaml.deep_merge({'production' => { 'username' => nil }}))
|
54
|
+
validator = Xing::Nominal::DatabaseConfigValidator.new
|
55
|
+
validator.validate('production')
|
56
|
+
expect(validator.errors['production']['username']).not_to be_blank
|
57
|
+
end
|
58
|
+
|
59
|
+
it "requires password" do
|
60
|
+
expect(YAML).to receive(:load).and_return(prod_yaml.deep_merge({'production' => { 'password' => nil }}))
|
61
|
+
validator = Xing::Nominal::DatabaseConfigValidator.new
|
62
|
+
validator.validate('production')
|
63
|
+
expect(validator.errors['production']['password']).not_to be_blank
|
64
|
+
end
|
65
|
+
|
66
|
+
it "requires host" do
|
67
|
+
expect(YAML).to receive(:load).and_return(prod_yaml.deep_merge({'production' => { 'host' => nil }}))
|
68
|
+
validator = Xing::Nominal::DatabaseConfigValidator.new
|
69
|
+
validator.validate('production')
|
70
|
+
expect(validator.errors['production']['host']).not_to be_blank
|
71
|
+
end
|
72
|
+
end
|
73
|
+
|
74
|
+
|
75
|
+
DEV_FIXTURE = <<EOS
|
76
|
+
development:
|
77
|
+
adapter: postgresql
|
78
|
+
encoding: unicode
|
79
|
+
database: cms2_dev
|
80
|
+
|
81
|
+
test:
|
82
|
+
adapter: postgresql
|
83
|
+
encoding: unicode
|
84
|
+
database: cms2_dev
|
85
|
+
EOS
|
86
|
+
|
87
|
+
PROD_FIXTURE = <<EOS
|
88
|
+
|
89
|
+
production:
|
90
|
+
adapter: postgresql
|
91
|
+
encoding: unicode
|
92
|
+
database: cms2_dev
|
93
|
+
username: a_user
|
94
|
+
password: abcdef
|
95
|
+
host: 127.0.0.1
|
96
|
+
EOS
|
97
|
+
|
98
|
+
end
|
@@ -0,0 +1,78 @@
|
|
1
|
+
require 'xing/nominal/secrets_validator'
|
2
|
+
|
3
|
+
describe Xing::Nominal::SecretsValidator do
|
4
|
+
before do
|
5
|
+
expect(File).to receive(:open).and_return(nil)
|
6
|
+
end
|
7
|
+
|
8
|
+
let :dev_yaml do
|
9
|
+
YAML.load(FIXTURE)
|
10
|
+
end
|
11
|
+
|
12
|
+
it "when development has a missing key" do
|
13
|
+
expect(YAML).to receive(:load).and_return(dev_yaml.deep_merge({'development' => { 'email' => nil }}))
|
14
|
+
validator = Xing::Nominal::SecretsValidator.new
|
15
|
+
validator.validate('development')
|
16
|
+
expect(validator.errors['development']).not_to be_blank
|
17
|
+
expect(validator.errors['test']).to be_blank
|
18
|
+
end
|
19
|
+
|
20
|
+
it "when development has no missing keys" do
|
21
|
+
expect(YAML).to receive(:load).and_return(dev_yaml)
|
22
|
+
validator = Xing::Nominal::SecretsValidator.new
|
23
|
+
validator.validate('development')
|
24
|
+
expect(validator.errors).to be_blank
|
25
|
+
end
|
26
|
+
|
27
|
+
it "when development has a missing key and test has a misformatted value " do
|
28
|
+
expect(YAML).to receive(:load).and_return(dev_yaml.deep_merge({
|
29
|
+
'development' => { 'email' => nil },
|
30
|
+
'test' => { 'email' => { 'from' => 'not_an_email' } }})
|
31
|
+
)
|
32
|
+
validator = Xing::Nominal::SecretsValidator.new
|
33
|
+
validator.validate('development')
|
34
|
+
expect(validator.errors['development']['email']) .not_to be_blank
|
35
|
+
expect(validator.errors['test']['email']['from']).not_to be_blank
|
36
|
+
end
|
37
|
+
|
38
|
+
FIXTURE = <<EOS
|
39
|
+
development:
|
40
|
+
secret_key_base: xxxkey
|
41
|
+
smtp:
|
42
|
+
address: smtp.gmail.com
|
43
|
+
port: 587
|
44
|
+
domain: lrdesign.com
|
45
|
+
user_name: quentin@lrdesign.com
|
46
|
+
password: xxxxxxxx
|
47
|
+
email:
|
48
|
+
from: quentin@lrdesign.com
|
49
|
+
reply_to: quentin@lrdesign.com
|
50
|
+
from_domain: lrdesign.com
|
51
|
+
test: test@lrdesign.com
|
52
|
+
snapshot_server:
|
53
|
+
url: https://www.notaserver.com
|
54
|
+
user: user
|
55
|
+
password: password
|
56
|
+
sitemap_base_url: http://localhost:3000/
|
57
|
+
|
58
|
+
test:
|
59
|
+
secret_key_base: xxkey
|
60
|
+
smtp:
|
61
|
+
address: smtp.gmail.com
|
62
|
+
port: 587
|
63
|
+
domain: equibid.com
|
64
|
+
user_name: sysquentin@equibid.com
|
65
|
+
password: xxxxxxxx
|
66
|
+
email:
|
67
|
+
from: quentin@example.com
|
68
|
+
reply_to: quentin@example.com
|
69
|
+
from_domain: lrdesign.com
|
70
|
+
test: test@lrdesign.com
|
71
|
+
snapshot_server:
|
72
|
+
url: https://www.notaserver.com
|
73
|
+
user: user
|
74
|
+
password: password
|
75
|
+
sitemap_base_url: http://localhost:3000/
|
76
|
+
EOS
|
77
|
+
|
78
|
+
end
|
@@ -0,0 +1,116 @@
|
|
1
|
+
require 'xing/serializers/list'
|
2
|
+
require 'json_spec'
|
3
|
+
|
4
|
+
describe Xing::Serializers::List do
|
5
|
+
include JsonSpec::Matchers
|
6
|
+
|
7
|
+
class ItemSerializer < Xing::Serializers::Base
|
8
|
+
attributes :name, :position
|
9
|
+
def links
|
10
|
+
{ :self => 'url_for_this_model' }
|
11
|
+
end
|
12
|
+
end
|
13
|
+
|
14
|
+
let :list do
|
15
|
+
(1..3).map do |index|
|
16
|
+
double("a mock activemodel").tap do |model|
|
17
|
+
allow(model).to receive(:read_attribute_for_serialization).with(:name).and_return("Name #{index}!")
|
18
|
+
allow(model).to receive(:read_attribute_for_serialization).with(:position).and_return(index)
|
19
|
+
end
|
20
|
+
end
|
21
|
+
end
|
22
|
+
|
23
|
+
let :total_pages do 3 end
|
24
|
+
|
25
|
+
let :serializer do
|
26
|
+
ListSerializer.new(list)
|
27
|
+
end
|
28
|
+
|
29
|
+
let :json do
|
30
|
+
serializer.to_json
|
31
|
+
end
|
32
|
+
|
33
|
+
describe 'with template link' do
|
34
|
+
class ListSerializer < Xing::Serializers::List
|
35
|
+
def item_serializer_class
|
36
|
+
ItemSerializer
|
37
|
+
end
|
38
|
+
|
39
|
+
def template_link
|
40
|
+
"template_url_for_list"
|
41
|
+
end
|
42
|
+
|
43
|
+
def self_link
|
44
|
+
"url_for_list"
|
45
|
+
end
|
46
|
+
end
|
47
|
+
|
48
|
+
it "should have the correct structure" do
|
49
|
+
expect(json).to have_json_path('links/self')
|
50
|
+
expect(json).to have_json_path('links/template')
|
51
|
+
expect(json).to have_json_path('data/')
|
52
|
+
end
|
53
|
+
|
54
|
+
it "should generate a JSON with the proper links and self" do
|
55
|
+
expect(json).to be_json_eql('"url_for_list"').at_path('links/self')
|
56
|
+
expect(json).to be_json_eql('"template_url_for_list"').at_path('links/template')
|
57
|
+
expect(json).to be_json_eql('"Name 2!"').at_path('data/1/data/name')
|
58
|
+
expect(json).to be_json_eql('2').at_path('data/1/data/position')
|
59
|
+
expect(json).to be_json_eql('"url_for_this_model"').at_path('data/1/links/self')
|
60
|
+
end
|
61
|
+
|
62
|
+
it "should call the video response serializer" do
|
63
|
+
expect(ItemSerializer).to receive(:new).with(list[0], anything)
|
64
|
+
expect(ItemSerializer).to receive(:new).with(list[1], anything)
|
65
|
+
expect(ItemSerializer).to receive(:new).with(list[2], anything)
|
66
|
+
expect(json).to have_json_size(3).at_path('data')
|
67
|
+
end
|
68
|
+
end
|
69
|
+
|
70
|
+
describe 'without template link' do
|
71
|
+
class ListSerializerWithoutTemplateLink < Xing::Serializers::List
|
72
|
+
def item_serializer_class
|
73
|
+
ItemSerializer
|
74
|
+
end
|
75
|
+
|
76
|
+
def self_link
|
77
|
+
"url_for_list"
|
78
|
+
end
|
79
|
+
end
|
80
|
+
|
81
|
+
let :serializer do
|
82
|
+
ListSerializerWithoutTemplateLink.new(list)
|
83
|
+
end
|
84
|
+
|
85
|
+
it "should have the correct structure" do
|
86
|
+
expect(json).to have_json_path('links/self')
|
87
|
+
expect(json).to_not have_json_path('links/template')
|
88
|
+
expect(json).to have_json_path('data/')
|
89
|
+
end
|
90
|
+
|
91
|
+
it "should generate a JSON with the proper links and self" do
|
92
|
+
expect(json).to be_json_eql('"url_for_list"').at_path('links/self')
|
93
|
+
expect(json).to be_json_eql('"Name 2!"').at_path('data/1/data/name')
|
94
|
+
expect(json).to be_json_eql('2').at_path('data/1/data/position')
|
95
|
+
expect(json).to be_json_eql('"url_for_this_model"').at_path('data/1/links/self')
|
96
|
+
end
|
97
|
+
end
|
98
|
+
|
99
|
+
describe 'a badly formed subclass' do
|
100
|
+
class BrokenListSerializer < Xing::Serializers::List
|
101
|
+
def item_serializer_class
|
102
|
+
ItemSerializer
|
103
|
+
end
|
104
|
+
end
|
105
|
+
|
106
|
+
let :serializer do
|
107
|
+
BrokenListSerializer.new(list)
|
108
|
+
end
|
109
|
+
|
110
|
+
it "should fail" do
|
111
|
+
expect do
|
112
|
+
json
|
113
|
+
end.to raise_error(NotImplementedError)
|
114
|
+
end
|
115
|
+
end
|
116
|
+
end
|