xing-backend 0.0.19 → 0.0.20
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- 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
|