xing-backend 0.0.10

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 (69) hide show
  1. checksums.yaml +7 -0
  2. data/lib/deprecated_classes.rb +28 -0
  3. data/lib/xing/controllers/base.rb +40 -0
  4. data/lib/xing/controllers/root_resources_controller.rb +12 -0
  5. data/lib/xing/engine.rb +27 -0
  6. data/lib/xing/mappers/base.rb +135 -0
  7. data/lib/xing/mappers.rb +6 -0
  8. data/lib/xing/serializers/base.rb +50 -0
  9. data/lib/xing/serializers/root_resources.rb +9 -0
  10. data/lib/xing/serializers.rb +7 -0
  11. data/lib/xing/services/error_converter.rb +47 -0
  12. data/lib/xing/services/json_tree_lister.rb +56 -0
  13. data/lib/xing/services/snapshot_fetcher.rb +33 -0
  14. data/lib/xing/services/snapshot_writer.rb +19 -0
  15. data/lib/xing/services.rb +9 -0
  16. data/lib/xing-backend.rb +34 -0
  17. data/spec/deprecated_classes/active_model_error_converter_spec.rb +11 -0
  18. data/spec/deprecated_classes/base_serializer_spec.rb +11 -0
  19. data/spec/deprecated_classes/hypermedia_json_mapper_spec.rb +11 -0
  20. data/spec/deprecated_classes/json_tree_lister_spec.rb +14 -0
  21. data/spec/deprecated_classes/remote_snapshot_fetcher_spec.rb +8 -0
  22. data/spec/deprecated_classes/resources_serializer_spec.rb +12 -0
  23. data/spec/xing/controllers/base_spec.rb +7 -0
  24. data/spec/xing/controllers/root_resources_controller_spec.rb +8 -0
  25. data/spec/xing/mappers/base_spec.rb +56 -0
  26. data/spec/xing/serializers/base_spec.rb +32 -0
  27. data/spec/xing/serializers/root_resources_spec.rb +20 -0
  28. data/spec/xing/services/error_converter_spec.rb +61 -0
  29. data/spec/xing/services/json_tree_lister_spec.rb +109 -0
  30. data/spec/xing/services/snapshot_fetcher_spec.rb +75 -0
  31. data/spec/xing_spec.rb +7 -0
  32. data/spec_help/dummy/README.rdoc +28 -0
  33. data/spec_help/dummy/Rakefile +6 -0
  34. data/spec_help/dummy/app/assets/javascripts/application.js +13 -0
  35. data/spec_help/dummy/app/assets/stylesheets/application.css +15 -0
  36. data/spec_help/dummy/app/controllers/application_controller.rb +5 -0
  37. data/spec_help/dummy/app/helpers/application_helper.rb +2 -0
  38. data/spec_help/dummy/app/views/layouts/application.html.erb +14 -0
  39. data/spec_help/dummy/bin/bundle +3 -0
  40. data/spec_help/dummy/bin/rails +4 -0
  41. data/spec_help/dummy/bin/rake +4 -0
  42. data/spec_help/dummy/bin/setup +29 -0
  43. data/spec_help/dummy/config/application.rb +25 -0
  44. data/spec_help/dummy/config/boot.rb +5 -0
  45. data/spec_help/dummy/config/database.yml +25 -0
  46. data/spec_help/dummy/config/environment.rb +5 -0
  47. data/spec_help/dummy/config/environments/development.rb +41 -0
  48. data/spec_help/dummy/config/environments/production.rb +79 -0
  49. data/spec_help/dummy/config/environments/test.rb +42 -0
  50. data/spec_help/dummy/config/initializers/assets.rb +11 -0
  51. data/spec_help/dummy/config/initializers/backtrace_silencers.rb +7 -0
  52. data/spec_help/dummy/config/initializers/cookies_serializer.rb +3 -0
  53. data/spec_help/dummy/config/initializers/filter_parameter_logging.rb +4 -0
  54. data/spec_help/dummy/config/initializers/inflections.rb +16 -0
  55. data/spec_help/dummy/config/initializers/mime_types.rb +4 -0
  56. data/spec_help/dummy/config/initializers/session_store.rb +3 -0
  57. data/spec_help/dummy/config/initializers/wrap_parameters.rb +14 -0
  58. data/spec_help/dummy/config/locales/en.yml +23 -0
  59. data/spec_help/dummy/config/routes.rb +56 -0
  60. data/spec_help/dummy/config/secrets.yml +22 -0
  61. data/spec_help/dummy/config.ru +4 -0
  62. data/spec_help/dummy/db/test.sqlite3 +0 -0
  63. data/spec_help/dummy/log/test.log +48 -0
  64. data/spec_help/dummy/public/404.html +67 -0
  65. data/spec_help/dummy/public/422.html +67 -0
  66. data/spec_help/dummy/public/500.html +66 -0
  67. data/spec_help/dummy/public/favicon.ico +0 -0
  68. data/spec_help/spec_helper.rb +29 -0
  69. metadata +222 -0
checksums.yaml ADDED
@@ -0,0 +1,7 @@
1
+ ---
2
+ SHA1:
3
+ metadata.gz: 81e5fbc0eab9a8177181d26d407b447a7bde5e78
4
+ data.tar.gz: 5e55cf12697cab2d64f021c4e918643382867212
5
+ SHA512:
6
+ metadata.gz: ae67633934b7e3b807aca8f178895a52d1751188b04c4cebfd051abf5076280eb9c9063ab612cc8c12137bf70c02ebb64bd4999339c1451d953f9a932c0a6316
7
+ data.tar.gz: 0ea48e465a1e35135b46c093161ddd836c8117b3568a2765cb223044f9a53a3dfb745d4d12306fc6b9a320c7c6c5ae4def2ca7d32d68b8c4bef478b03a5219a6
@@ -0,0 +1,28 @@
1
+ require 'xing-backend'
2
+ require 'active_support/deprecation'
3
+
4
+ module Xing
5
+ DEPRECATED_CLASSES = {
6
+ :HypermediaJSONMapper => Xing::Mappers::Base,
7
+ :BaseSerializer => Xing::Serializers::Base,
8
+ :ResourcesSerializer => Xing::Serializers::RootResources,
9
+ :JsonTreeLister => Xing::Services::JsonTreeLister,
10
+ :ActiveModelErrorConverter => Xing::Services::ErrorConverter,
11
+ :RemoteSnapshotFetcher => Xing::Services::SnapshotFetcher
12
+ }
13
+ end
14
+
15
+ #Xing::DEPRECATED_CLASSES.each do |old, new|
16
+
17
+ ## with great power comes great responsibility
18
+ #Object.const_set(old, ActiveSupport::Deprecation::DeprecatedConstantProxy.new(old, new))
19
+ #end
20
+
21
+ def Object.const_missing(name)
22
+ if (klass = ::Xing::DEPRECATED_CLASSES[name.to_sym])
23
+ warn "[DEPRECATION] #{name} is deprecated. Please use #{klass.to_s} instead."
24
+ klass
25
+ else
26
+ super
27
+ end
28
+ end
@@ -0,0 +1,40 @@
1
+ require 'devise_token_auth'
2
+
3
+ module Xing
4
+ module Controllers
5
+ class Base < ActionController::Base
6
+ include DeviseTokenAuth::Concerns::SetUserByToken
7
+
8
+ respond_to :json
9
+
10
+ protect_from_forgery
11
+ before_filter :check_format
12
+
13
+ def check_format
14
+ if request.subdomains.include? Xing.backend_subdomain
15
+ if request.headers["Accept"] =~ /json/
16
+ params[:format] = :json
17
+ else
18
+ render :nothing => true, :status => 406
19
+ end
20
+ end
21
+ end
22
+
23
+ def json_body
24
+ @json_body ||= request.body.read
25
+ end
26
+
27
+ def parse_json
28
+ @parsed_json ||= JSON.parse(json_body)
29
+ end
30
+
31
+ def failed_to_process(error_document)
32
+ render :status => 422, :json => error_document
33
+ end
34
+
35
+ def successful_create(new_resource_path)
36
+ render :status => 201, :json => {}, :location => new_resource_path
37
+ end
38
+ end
39
+ end
40
+ end
@@ -0,0 +1,12 @@
1
+ require 'rails/rfc6570'
2
+
3
+ module Xing
4
+ module Controllers
5
+ class RootResourcesController < ::Xing::Controllers::Base
6
+ def index
7
+ @resources = rfc6570_routes(ignore: %w(format), path_only: true)
8
+ render :json => Xing::Serializers::RootResources.new(@resources)
9
+ end
10
+ end
11
+ end
12
+ end
@@ -0,0 +1,27 @@
1
+ module Xing
2
+
3
+ class Engine < ::Rails::Engine
4
+ isolate_namespace Xing
5
+
6
+ config.autoload_paths += Dir[File.join(__FILE__, '../controllers/**/')]
7
+
8
+ config.generators do |g|
9
+ g.test_framework :rspec
10
+ end
11
+
12
+ # The ErrorConverter leverages (abuses?) the I18n mechanism to translate
13
+ # ActiveModel validation errors into Xing JSON resource errors. Here we
14
+ # need to make sure the locales file for language 'json' is loaded for
15
+ # I18n.
16
+ initializer 'xing errors locales path' do
17
+ I18n.load_path += Dir[File.join(File.dirname(__FILE__), '..', 'config', 'locales', '*.{rb,yml}')]
18
+ end
19
+
20
+ # Set the backend subdomain if it hasn't been configured by the user.
21
+ initializer 'set subdomain' do
22
+ Xing.configure do |xng_config|
23
+ xng_config.backend_subdomain ||= 'api'
24
+ end
25
+ end
26
+ end
27
+ end
@@ -0,0 +1,135 @@
1
+ # This code is only used in the context of Rails apps, and depends pretty
2
+ # heavily on rails core extensions, so we are requiring them here.
3
+ require 'active_support'
4
+ require 'active_support/core_ext'
5
+
6
+ module Xing
7
+ module Mappers
8
+ class Base
9
+ class MissingLinkException < Exception; end
10
+
11
+ # Subclasses must define:
12
+ # aliases -- for self.record
13
+ # record_class -- if the mapper maps to an AR object
14
+ #
15
+ # Subclasses should usually define:
16
+ # assign_values -- move values from JSON into the mapped AR record
17
+ #
18
+ # Subclasses may also want to define:
19
+ # find_existing_record -- for locating the underlying AR record
20
+ # build_new_record -- for for instantiating a new underlying AR record
21
+ # map_nested_models
22
+ # build_errors -- if simply copying AR errors is insufficient
23
+ # save -- if they need to save more than 1 AR record
24
+
25
+ # When updating records, pass the locator (e.g. DB id, url_slug, or other
26
+ # unique resource extracted from the resource path) as the second argument.
27
+ def initialize(json, locator = nil)
28
+ @source_json = json
29
+ if @source_json.is_a? String
30
+ @source_hash = JSON.parse(json).with_indifferent_access
31
+ else
32
+ @source_hash = @source_json
33
+ end
34
+ @locator = locator
35
+ end
36
+ attr_accessor :locator, :error_data
37
+ attr_writer :record
38
+
39
+ def router
40
+ Rails.application.routes
41
+ end
42
+
43
+ def normalize_path(path)
44
+ path = "/#{path}"
45
+ path.squeeze!('/')
46
+ path.sub!(%r{/+\Z}, '')
47
+ path.gsub!(/(%[a-f0-9]{2})/) { $1.upcase }
48
+ path = '/' if path == ''
49
+ path
50
+ end
51
+
52
+ # This helper is used to deconstruct a URL for the purpose of extracting
53
+ # components. For example, menu_item_mapper uses it to extract the url_slug
54
+ # component of a page route. We are here abusing recognize_path, which isn't
55
+ # supposed to be used outside of tests (see
56
+ # https://github.com/rails/rails/issues/2656), but we don't know what the
57
+ # alternative is.
58
+ def route_to(path)
59
+ path = "http://#{BACKEND_SUBDOMAIN}.example.com#{normalize_path(path)}";
60
+ router.recognize_path(path)
61
+ end
62
+
63
+ # Default save - subclasses might override
64
+ def save
65
+ perform_mapping
66
+ unless self.errors[:data].present?
67
+ self.record.save
68
+ end
69
+ end
70
+
71
+ # Default for finding an existing record - override this *or* define
72
+ # #record_class (e.g. `return Page`
73
+ def find_existing_record
74
+ @record = record_class.find(@locator)
75
+ end
76
+
77
+ # Default for building a new record - override this *or* define #record_class
78
+ # (e.g. `return Page`
79
+ def build_new_record
80
+ @record = record_class.new
81
+ end
82
+
83
+ def perform_mapping
84
+ data = unwrap_data(@source_hash)
85
+ self.error_data = Hash.new { |hash, key| hash[key] = {} }
86
+
87
+ assign_values(data)
88
+ map_nested_models
89
+ build_errors
90
+ end
91
+
92
+ def unwrap_data(hash)
93
+ hash['data'].with_indifferent_access
94
+ end
95
+
96
+ def wrap_data(hash)
97
+ {
98
+ data: hash
99
+ }
100
+ end
101
+
102
+ def record
103
+ @record ||= if !locator.nil?
104
+ find_existing_record
105
+ else
106
+ build_new_record
107
+ end
108
+ end
109
+
110
+ def assign_values(data_hash)
111
+ # Override in subclasses to assign needed values here
112
+ record # force loading or creation of the underlying DB record
113
+ update_record
114
+ end
115
+
116
+ # Do nothing if there are no nested models
117
+ # Override this method in subclass if necessary
118
+ def map_nested_models
119
+ end
120
+
121
+ def build_errors
122
+ self.add_ar_arrors(self.record)
123
+ end
124
+
125
+ def errors
126
+ wrap_data(error_data)
127
+ end
128
+
129
+ def add_ar_arrors(object)
130
+ object_errors = ActiveModelErrorConverter.new(object).convert
131
+ error_data.deep_merge!(object_errors)
132
+ end
133
+ end
134
+ end
135
+ end
@@ -0,0 +1,6 @@
1
+ module Xing
2
+ module Mappers
3
+ end
4
+ end
5
+
6
+ require 'xing/mappers/base'
@@ -0,0 +1,50 @@
1
+ require 'rails'
2
+ require 'active_model_serializers'
3
+
4
+ module Xing
5
+ module Serializers
6
+
7
+ # The base class for all Xing serializers that produce
8
+ # Xing Hypermedia JSON resources. In general, subclasses
9
+ # of Xing::Serializers::Base should:
10
+ #
11
+ # * Define a links method that returns a hash of hypermedia links to
12
+ # related resources
13
+ #
14
+ # * Specify the attributes (via the ActiveModel::Serializers 'attributes'
15
+ # class method) that will be copied into the data: block
16
+ # of the generated resource.
17
+ #
18
+ # * Define methods for any attributes that do not exist as plain attributes
19
+ # in the ActiveModel being serialized. Note that this may (and often will) include
20
+ # calling other serializers on related resources or other data, in order to
21
+ # generate embedded resources.
22
+ #
23
+ # Xing serializers descend from ActiveModel::Serializer and are typically
24
+ # instantiated with an instance of an ActiveModel model in the usual way.
25
+ #
26
+ class Base < ActiveModel::Serializer
27
+
28
+ def routes
29
+ Rails.application.routes.url_helpers
30
+ end
31
+
32
+ def root
33
+ false
34
+ end
35
+
36
+ def as_json_with_wrap(options={})
37
+ {
38
+ :links => links,
39
+ :data => as_json_without_wrap
40
+ }
41
+ end
42
+
43
+ def links
44
+ {}
45
+ end
46
+
47
+ alias_method_chain :as_json, :wrap
48
+ end
49
+ end
50
+ end
@@ -0,0 +1,9 @@
1
+ module Xing
2
+ module Serializers
3
+ class RootResources < Base
4
+ def links
5
+ object
6
+ end
7
+ end
8
+ end
9
+ end
@@ -0,0 +1,7 @@
1
+ module Xing
2
+ module Serializers
3
+ end
4
+ end
5
+
6
+ require 'xing/serializers/base'
7
+ require 'xing/serializers/root_resources'
@@ -0,0 +1,47 @@
1
+ module Xing
2
+ module Services
3
+ class ErrorConverter
4
+
5
+
6
+ def initialize(am_object)
7
+ @am_object = am_object
8
+ end
9
+ attr_reader :am_object
10
+
11
+ # This is a terrible hack to preserve the semantic meaning of
12
+ # different error types -- neccesary because ActiveModel::Errors
13
+ # frustratingly translates semantic error messages through i18n
14
+ # as soon as it gets them
15
+ def json_errors
16
+ @json_errors ||= begin
17
+ old_locale = I18n.locale
18
+ I18n.locale = "json"
19
+ am_object.valid?
20
+ error_hash = am_object.errors.to_hash.deep_dup
21
+ I18n.locale = old_locale
22
+ error_hash
23
+ end
24
+ end
25
+
26
+ def regular_errors
27
+ @regular_errors ||= begin
28
+ am_object.valid?
29
+ am_object.errors.to_hash.deep_dup
30
+ end
31
+ end
32
+
33
+ def convert
34
+ final_errors = {}
35
+ json_errors.each_key do |key|
36
+ final_errors[key] = {
37
+ :type => json_errors[key][0],
38
+ :message => regular_errors[key][0]
39
+ }
40
+ end
41
+ final_errors
42
+ end
43
+
44
+
45
+ end
46
+ end
47
+ end
@@ -0,0 +1,56 @@
1
+ require 'active_model_serializers'
2
+ require 'active_support/core_ext'
3
+
4
+ module Xing
5
+ module Services
6
+ class JsonTreeLister
7
+
8
+ class TreeNode
9
+ include ActiveModel::SerializerSupport
10
+
11
+ def initialize(node, children)
12
+ @node = node
13
+ @children = children
14
+ end
15
+
16
+ attr_reader :node, :children
17
+ end
18
+
19
+ def initialize(nodes, node_serializer)
20
+ @nodes = nodes
21
+ @node_serializer = node_serializer
22
+ @stack = [[]]
23
+ @path = []
24
+ end
25
+
26
+ def render_node(node, children)
27
+ @node_serializer.new(TreeNode.new(node, children)).as_json
28
+ end
29
+
30
+ def pop_level
31
+ children = @stack.pop
32
+ @stack.last << render_node(@path.pop, children)
33
+ end
34
+
35
+ def render
36
+ (@nodes + [nil]).each_cons(2) do |this, after|
37
+ until @path.empty? or @path.last.is_ancestor_of?(this)
38
+ pop_level
39
+ end
40
+ if after.nil? or !this.is_ancestor_of?(after)
41
+ @stack.last << render_node(this, [])
42
+ else
43
+ @path << this
44
+ @stack << []
45
+ end
46
+ end
47
+ until @path.empty?
48
+ pop_level
49
+ end
50
+ return @stack.last.first
51
+ end
52
+
53
+
54
+ end
55
+ end
56
+ end
@@ -0,0 +1,33 @@
1
+ require 'typhoeus'
2
+ require 'addressable/uri'
3
+ require 'xing/services/snapshot_writer'
4
+ require 'sidekiq/worker'
5
+
6
+ module Xing::Services
7
+ class SnapshotFetcher
8
+ include Sidekiq::Worker
9
+ include SnapshotWriter
10
+
11
+ def perform(url, path)
12
+ admin_server = Rails.application.secrets.snapshot_server['url']
13
+ user_password = "#{Rails.application.secrets.snapshot_server['user']}:#{Rails.application.secrets.snapshot_server['password']}"
14
+ snapshot_url = Addressable::URI.join(url,path).to_s
15
+ request = Typhoeus::Request.new(admin_server, userpwd: user_password, params: { url: snapshot_url })
16
+
17
+ hydra = Typhoeus::Hydra.new
18
+ hydra.queue(request)
19
+ hydra.run
20
+
21
+ response = request.response
22
+
23
+ if response.success?
24
+ html = response.body
25
+ write(path, html)
26
+ else
27
+ logger.warn response.status_message
28
+ logger.warn response.body
29
+ raise "Query to #{admin_server} for #{path} failed!"
30
+ end
31
+ end
32
+ end
33
+ end
@@ -0,0 +1,19 @@
1
+ module Xing::Services
2
+ module SnapshotWriter
3
+ def write(path, html)
4
+ if Rails.env.test?
5
+ snapshot_file = "#{ Rails.root }/spec/fixtures/sitemap_scratch/#{path.present? ? path : 'index'}.html"
6
+ else
7
+ snapshot_file = "#{ Rails.root }/public/frontend_snapshots/#{path.present? ? path : 'index'}.html"
8
+ end
9
+ dirname = File.dirname(snapshot_file)
10
+ unless File.directory?(dirname)
11
+ FileUtils.mkdir_p(dirname)
12
+ end
13
+
14
+ File.open(snapshot_file, "w+:ASCII-8BIT:UTF-8") do |f|
15
+ f.write(html)
16
+ end
17
+ end
18
+ end
19
+ end
@@ -0,0 +1,9 @@
1
+ module Xing
2
+ module Services
3
+ end
4
+ end
5
+
6
+ require 'xing/services/json_tree_lister'
7
+ require 'xing/services/error_converter'
8
+ require 'xing/services/snapshot_fetcher'
9
+ require 'xing/services/snapshot_writer'
@@ -0,0 +1,34 @@
1
+ require 'rails'
2
+ require 'xing_backend_token_auth'
3
+ require 'rails/rfc6570'
4
+ require 'sidekiq'
5
+
6
+ module Xing
7
+ mattr_accessor :backend_subdomain
8
+
9
+ # Configure xing via pattern similar to Rails:
10
+ #
11
+ # Xing.configure do |config|
12
+ # config.setting = 'value'
13
+ # end
14
+ #
15
+ # Supported settings right now are:
16
+ # * backend_subdomain (default: 'api')
17
+ def self.configure(&block)
18
+ yield self
19
+ end
20
+
21
+ module Controllers
22
+ autoload :Base, 'xing/controllers/base'
23
+
24
+ # NOTE: The rails router expects the the controller to have "Controller" as
25
+ # the suffix of the name, or it complains.
26
+ autoload :RootResourcesController, 'xing/controllers/root_resources_controller'
27
+ end
28
+ end
29
+
30
+ require 'xing/mappers'
31
+ require 'xing/engine'
32
+ require 'xing/serializers'
33
+ require 'xing/services'
34
+ require 'deprecated_classes'
@@ -0,0 +1,11 @@
1
+ require 'deprecated_classes'
2
+
3
+ describe ActiveModelErrorConverter, :type => :deprecation do
4
+ let :resource do
5
+ double('resource')
6
+ end
7
+
8
+ it "should be the correct class" do
9
+ expect(ActiveModelErrorConverter.new(resource)).to be_a(Xing::Services::ErrorConverter)
10
+ end
11
+ end
@@ -0,0 +1,11 @@
1
+ require 'deprecated_classes'
2
+
3
+ describe BaseSerializer, :type => :deprecation do
4
+ let :resource do
5
+ double('resource')
6
+ end
7
+
8
+ it "should be the correct class" do
9
+ expect(BaseSerializer.new(resource)).to be_a(Xing::Serializers::Base)
10
+ end
11
+ end
@@ -0,0 +1,11 @@
1
+ require 'deprecated_classes'
2
+
3
+ describe HypermediaJSONMapper, :type => :deprecation do
4
+ let :json do
5
+ double('json')
6
+ end
7
+
8
+ it "should be the correct class" do
9
+ expect(HypermediaJSONMapper.new(json)).to be_a(Xing::Mappers::Base)
10
+ end
11
+ end
@@ -0,0 +1,14 @@
1
+ require 'deprecated_classes'
2
+
3
+ describe JsonTreeLister, :type => :deprecation do
4
+ let :models do
5
+ double('models')
6
+ end
7
+ let :serializer do
8
+ double('serializer')
9
+ end
10
+
11
+ it "should be the correct class" do
12
+ expect(JsonTreeLister.new(models, serializer)).to be_a(Xing::Services::JsonTreeLister)
13
+ end
14
+ end
@@ -0,0 +1,8 @@
1
+
2
+ require 'deprecated_classes'
3
+
4
+ describe RemoteSnapshotFetcher do
5
+ it "should be the correct class" do
6
+ expect(RemoteSnapshotFetcher.new()).to be_a(Xing::Services::SnapshotFetcher)
7
+ end
8
+ end
@@ -0,0 +1,12 @@
1
+
2
+ require 'deprecated_classes'
3
+
4
+ describe ResourcesSerializer do
5
+ let :resource do
6
+ double('hash_of_links')
7
+ end
8
+
9
+ it "should be the correct class" do
10
+ expect(ResourcesSerializer.new(resource)).to be_a(Xing::Serializers::RootResources)
11
+ end
12
+ end
@@ -0,0 +1,7 @@
1
+ require 'xing-backend'
2
+
3
+ describe Xing::Controllers::Base, :type => :controller do
4
+ it "should exist" do
5
+ expect(Xing::Controllers::Base).not_to be_nil
6
+ end
7
+ end
@@ -0,0 +1,8 @@
1
+ require 'xing-backend'
2
+
3
+ describe Xing::Controllers::RootResourcesController, :type => :controller do
4
+ it "should exist" do
5
+ expect(Xing::Controllers::RootResourcesController).not_to be_nil
6
+ end
7
+
8
+ end