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.
Files changed (61) hide show
  1. checksums.yaml +4 -4
  2. data/app/models/role.rb +24 -0
  3. data/config/locales/json.yml +29 -0
  4. data/config/routes.rb +3 -0
  5. data/db/migrate/20140828011806_initial.rb +45 -0
  6. data/db/migrate/20140914030703_devise_token_auth_add_token_info_to_users.rb +20 -0
  7. data/db/migrate/20140929192921_remove_login_from_users.rb +5 -0
  8. data/lib/xing-backend.rb +1 -0
  9. data/lib/xing/engine.rb +4 -0
  10. data/lib/xing/nominal/database_config_validator.rb +31 -0
  11. data/lib/xing/nominal/dependency_utils.rb +26 -0
  12. data/lib/xing/nominal/secrets_validator.rb +50 -0
  13. data/lib/xing/nominal/yaml_config_validator.rb +44 -0
  14. data/lib/xing/services.rb +0 -2
  15. data/lib/xing/services/class_registry.rb +31 -0
  16. data/lib/xing/services/{page_wrapper.rb → paged_wrapper.rb} +2 -2
  17. data/lib/xing/snapshot.rb +4 -0
  18. data/lib/xing/snapshot/domain_helpers.rb +24 -0
  19. data/lib/xing/snapshot/fetcher.rb +35 -0
  20. data/lib/xing/snapshot/local_site_snapshot.rb +37 -0
  21. data/lib/xing/snapshot/remote_site_snapshot.rb +16 -0
  22. data/lib/xing/snapshot/site_page_set.rb +29 -0
  23. data/lib/xing/snapshot/site_snapshot.rb +41 -0
  24. data/lib/xing/snapshot/sitemap.rb +68 -0
  25. data/lib/xing/snapshot/writer.rb +15 -0
  26. data/lib/xing/spec_helpers.rb +7 -0
  27. data/lib/xing/spec_helpers/api_response_matchers.rb +26 -0
  28. data/lib/xing/spec_helpers/ci_support.rb +6 -0
  29. data/lib/xing/spec_helpers/dom_equiv.rb +26 -0
  30. data/lib/xing/spec_helpers/json_requests.rb +58 -0
  31. data/lib/xing/spec_helpers/routing_spec_patch.rb +28 -0
  32. data/lib/xing/spec_helpers/split_servers.rb +15 -0
  33. data/lib/xing/spec_helpers/test_url_helpers.rb +5 -0
  34. data/lib/xing/static.rb +1 -0
  35. data/lib/xing/static/backend_url_cookie.rb +16 -0
  36. data/lib/xing/static/goto_param.rb +30 -0
  37. data/lib/xing/static/logger.rb +11 -0
  38. data/lib/xing/static/rack_app.rb +40 -0
  39. data/lib/xing/tasks/all.rake +4 -0
  40. data/lib/xing/tasks/db_recycle.rake +4 -0
  41. data/lib/xing/tasks/dependencies_common.rake +49 -0
  42. data/lib/xing/tasks/sample_data.rake +49 -0
  43. data/lib/xing/tasks/take_snapshot.rake +4 -0
  44. data/spec/xing/builders/list_builder_spec.rb +0 -2
  45. data/spec/xing/builders/ordered_list_builder_spec.rb +0 -2
  46. data/spec/xing/nominal/database_config_validator_spec.rb +98 -0
  47. data/spec/xing/nominal/secrets_validator_spec.rb +78 -0
  48. data/spec/xing/serializers/list_spec.rb +116 -0
  49. data/spec/xing/serializers/paged_index_spec.rb +2 -2
  50. data/spec/xing/serializers/paged_list_spec.rb +1 -1
  51. data/spec/xing/services/error_converter_spec.rb +1 -3
  52. data/spec/xing/services/paged_wrapper_spec.rb +30 -0
  53. data/spec/xing/snapshot/remote_snapshot_fetcher_spec.rb +81 -0
  54. data/spec/xing/{services → snapshot}/snapshot_fetcher_spec.rb +6 -4
  55. data/spec_help/dummy/db/test.sqlite3 +0 -0
  56. data/spec_help/dummy/log/test.log +143 -0
  57. data/spec_help/file-sandbox.rb +164 -0
  58. data/spec_help/spec_helper.rb +0 -2
  59. metadata +152 -12
  60. data/lib/xing/services/snapshot_fetcher.rb +0 -33
  61. data/lib/xing/services/snapshot_writer.rb +0 -19
@@ -0,0 +1,11 @@
1
+ module Xing
2
+ module Static
3
+ require 'logger'
4
+ class Logger < ::Logger
5
+ # needed to use stdlib logger with Rack < 1.6.0
6
+ def write(msg)
7
+ self.<<(msg)
8
+ end
9
+ end
10
+ end
11
+ end
@@ -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,4 @@
1
+ load 'xing/tasks/db_recycle.rake'
2
+ load 'xing/tasks/dependencies_common.rake'
3
+ load 'xing/tasks/sample_data.rake'
4
+ load 'xing/tasks/take_snapshot.rake'
@@ -0,0 +1,4 @@
1
+ namespace :db do
2
+ desc "Destroy and reload the whole DB"
3
+ task :recycle => [ :drop, :create, :migrate, :seed ]
4
+ 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,4 @@
1
+ task :take_snapshot => :environment do
2
+ # is there anything else to this? I don't think so -- just set this up in a cron job
3
+ RemoteSiteSnapshot.create!(Rails.application.secrets.sitemap_base_url)
4
+ end
@@ -1,5 +1,3 @@
1
- require 'spec_helper'
2
-
3
1
  describe Xing::Builders::ListBuilder do
4
2
 
5
3
  let :builder do
@@ -1,5 +1,3 @@
1
- require 'spec_helper'
2
-
3
1
  describe Xing::Builders::OrderedListBuilder do
4
2
 
5
3
  let :builder do
@@ -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