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.
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