Skip to content

HTTPS clone URL

Subversion checkout URL

You can clone with
or
.
Download ZIP
Browse files

Updated plugin repo (from BB's codebase)

  • Loading branch information...
commit fea97e1bff273cb92bc821ae5c1951ced2d64a2e 1 parent f59f8fb
DanielVartanov authored
Showing with 546 additions and 364 deletions.
  1. +2 −2 Rakefile
  2. +5 −4 init.rb
  3. +2 −2 lib/s_data.rb
  4. +1 −1  lib/s_data/active_record_extensions/base.rb
  5. +13 −17 lib/s_data/active_record_extensions/mixin.rb
  6. +33 −34 lib/s_data/active_record_extensions/sdata_uuid_mixin.rb
  7. +0 −11 lib/s_data/controller_mixin.rb
  8. +26 −27 lib/s_data/controller_mixin/actions.rb
  9. +5 −8 lib/s_data/controller_mixin/collection_scope.rb
  10. +8 −8 lib/s_data/controller_mixin/s_data_feed.rb
  11. +5 −5 lib/s_data/controller_mixin/s_data_instance.rb
  12. +2 −0  lib/s_data/diagnosis/diagnosis_mapper.rb
  13. +4 −0 lib/s_data/exceptions.rb
  14. +5 −5 lib/s_data/payload.rb
  15. +4 −3 lib/s_data/payload_map/payload_map.rb
  16. +34 −0 lib/s_data/resource.rb
  17. +111 −0 lib/s_data/sync/actions.rb
  18. +0 −122 lib/s_data/sync/controller_mixin.rb
  19. +3 −3 lib/s_data/sync/{sdata_syncing_mixin.rb → resource_mixin.rb}
  20. +18 −0 lib/s_data/trait.rb
  21. +132 −100 lib/s_data/virtual_base.rb
  22. +2 −0  spec/active_record_mixin/payload_spec.rb
  23. +6 −1 spec/class_stubs/address.rb
  24. +8 −0 spec/class_stubs/contact.rb
  25. +8 −4 spec/class_stubs/customer.rb
  26. +2 −2 spec/class_stubs/model_base.rb
  27. +8 −0 spec/class_stubs/user.rb
  28. +2 −2 spec/controller_mixin/sdata_scope/linked_model_spec.rb
  29. +1 −1  spec/controller_mixin/sdata_scope/scoping_in_config_spec.rb
  30. +1 −1  spec/payload_map/payload_map_spec.rb
  31. +58 −0 spec/resource_spec.rb
  32. +1 −1  spec/router_mixin/routes_spec.rb
  33. +36 −0 spec/trait_spec.rb
View
4 Rakefile
@@ -1,4 +1,4 @@
-require 'rake'
+conrequire 'rake'
begin
require 'jeweler'
@@ -16,4 +16,4 @@ begin
Jeweler::GemcutterTasks.new
rescue LoadError
puts "Jeweler not available. Install it with: gem install jeweler"
-end
+end
View
9 init.rb
@@ -7,8 +7,6 @@
require File.join(__DIR__, 'lib', 's_data', 'exceptions')
require File.join(__DIR__, 'lib', 's_data', 'formatting.rb')
require File.join(__DIR__, 'lib', 's_data', 'router_mixin.rb')
-require File.join(__DIR__, 'lib', 's_data', 'sync', 'sdata_syncing_mixin.rb')
-require File.join(__DIR__, 'lib', 's_data', 'sync', 'controller_mixin.rb')
require File.join(__DIR__, 'lib', 's_data', 'payload_map.rb')
require File.join(__DIR__, 'lib', 's_data', 'controller_mixin', 's_data_instance.rb')
require File.join(__DIR__, 'lib', 's_data', 'controller_mixin', 's_data_feed.rb')
@@ -26,11 +24,14 @@
require File.join(__DIR__, 'lib', 's_data', 'active_record_extensions', 'mixin.rb')
require File.join(__DIR__, 'lib', 's_data', 'active_record_extensions', 'sdata_uuid_mixin.rb')
require File.join(__DIR__, 'lib', 's_data', 'active_record_extensions', 'base.rb')
+require File.join(__DIR__, 'lib', 's_data', 'trait.rb')
+require File.join(__DIR__, 'lib', 's_data', 'sync', 'resource_mixin.rb')
require File.join(__DIR__, 'lib', 's_data', 'virtual_base.rb')
+require File.join(__DIR__, 'lib', 's_data', 'resource.rb')
+require File.join(__DIR__, 'lib', 's_data', 'sync', 'actions.rb')
require File.join(__DIR__, 'lib', 's_data', 'conditions_builder.rb')
-require File.join(__DIR__, 'lib', 's_data', 'controller_mixin.rb')
require File.join(__DIR__, 'lib', 's_data', 'payload_map', 'payload_map_hash.rb')
require File.join(__DIR__, 'lib', 's_data', 'payload_map', 'payload_map.rb')
require File.join(__DIR__, 'lib', 's_data', 'diagnosis', 'diagnosis.rb')
require File.join(__DIR__, 'lib', 's_data', 'diagnosis', 'application_controller_mixin.rb')
-require File.join(__DIR__, 'lib', 's_data', 'diagnosis', 'diagnosis_mapper.rb')
+require File.join(__DIR__, 'lib', 's_data', 'diagnosis', 'diagnosis_mapper.rb')
View
4 lib/s_data.rb
@@ -6,7 +6,7 @@ module SData
class << self
def sdata_name(klass)
case klass
- when SData::VirtualBase
+ when SData::Resource
klass.sdata_name
when Class
klass.respond_to?(:sdata_name) ? klass.sdata_name : nil
@@ -27,7 +27,7 @@ def config
unless @config
@config = YAML.load_file(File.join(File.dirname(__FILE__), '..', 'config','sdata.yml'))
app_config_file = ENV['SDATA_CONFIG_FILE']
- app_config_file ||= File.join(RAILS_ROOT, 'config','sdata.yml') if defined?(RAILS_ROOT)
+ app_config_file ||= File.join(RAILS_ROOT, 'sdata', 'config', 'sdata.yml') if defined?(RAILS_ROOT)
@config = @config.deep_merge(YAML.load_file(app_config_file)) unless app_config_file.nil?
@config = @config.with_indifferent_access
@config[:contracts] ||= []
View
2  lib/s_data/active_record_extensions/base.rb
@@ -1,4 +1,4 @@
-module SData
+module SData
module ActiveRecordExtensions
class Base < ::ActiveRecord::Base
self.abstract_class = true
View
30 lib/s_data/active_record_extensions/mixin.rb
@@ -10,22 +10,22 @@ def acts_as_sdata(options={})
self.__send__ :extend, ClassMethods
end
- def find_by_sdata_instance_id(value)
- attribute = self.sdata_options[:instance_id]
-
- attribute.nil? ?
- self.find(value.to_i) :
- self.first(:conditions => { attribute => value })
- end
-
module ClassMethods
- def sdata_node_name(entity=self)
- self.name.demodulize.camelize(:lower)
+ def find_by_sdata_instance_id(value)
+ attribute = self.sdata_options[:instance_id]
+
+ attribute.nil? ?
+ self.find(value.to_i) :
+ self.first(:conditions => { attribute => value })
+ end
+
+ def sdata_node_name
+ @sdata_node_name ||= self.name.demodulize.camelize(:lower)
end
def sdata_contract_name
- SData.sdata_contract_name(self.name)
+ @sdata_contract_name ||= SData.sdata_contract_name(self.name)
end
def sdata_resource_kind_url(dataset)
@@ -95,8 +95,8 @@ def sdata_name
self.class.name.demodulize
end
- def sdata_node_name(entity=self.class)
- self.class.sdata_node_name(entity)
+ def sdata_node_name
+ self.class.sdata_node_name
end
def sdata_resource_url(dataset)
@@ -149,9 +149,5 @@ def clean_params(params)
end
end
end
-# Extension of ActiveRecord removed due to refactoring. Now SData::VirtualBase is extended instead.
-# Not sure yet if there is a case where we DO need to extend ActiveRecord directly.
-# Might considering merging those two classes otherwise.
-# RADAR: Tested refactoring, but it could still be buggy if I missed something! Watch out.
View
67 lib/s_data/active_record_extensions/sdata_uuid_mixin.rb
@@ -9,12 +9,9 @@ def acts_as_sdata_uuid
module UuidClassMethods
- def find_by_virtual_model_and_uuid(virtual_model, uuid)
- sd_uuids = SData::SdUuid.find(:all, :conditions => {:sd_class => virtual_model.sdata_name, :bb_model_type => virtual_model.baze_class_name, :uuid => uuid})
-
- sd_uuid = enforce_uniqueness(sd_uuids, virtual_model.sdata_name, virtual_model.baze_class_name)
-
- raise "#{virtual_model.sdata_name} with UUID '#{uuid}' not found" unless sd_uuid
+ def find_by_virtual_model_owner_and_uuid(virtual_model, owner, uuid)
+ sd_uuid = find_uuid_record_for_virtual_model_owner_and_uuid(virtual_model, owner, uuid)
+ raise SData::Exceptions::SdUuid::NotFound.new("#{virtual_model.sdata_name} with UUID '#{uuid}' not found") unless sd_uuid
virtual_model.build_for(sd_uuid.bb_model)
end
@@ -22,12 +19,12 @@ def find_by_virtual_model_and_uuid(virtual_model, uuid)
# stores it's uuid for the resource on the other provider (http://interop.sage.com/daisy/sdataSync/Link/525-DSY.html)
# At a later date sdata will provide an algorithm for uuid propagation; for the time being we assume the last stored
# is the correct one.
- def find_for_virtual_instance(virtual_instance, baze=nil)
- baze ||= virtual_instance.baze
+ def find_for_virtual_instance(virtual_instance)
SData::SdUuid.first(:conditions => {:sd_class => virtual_instance.sdata_name,
- :bb_model_type => baze.class.name.demodulize,
- :bb_model_id => baze.id},
- :order => "updated_at DESC" )
+ :bb_model_type => virtual_instance.baze_class_name,
+ :bb_model_id => virtual_instance.baze.id,
+ :user_id => virtual_instance.owner_id},
+ :order => "updated_at DESC" )
end
def enforce_uniqueness(sd_uuids, sd_class, baze_class_name)
@@ -38,15 +35,23 @@ def enforce_uniqueness(sd_uuids, sd_class, baze_class_name)
end
sd_uuids.first
end
+
+ def find_uuid_record_for_virtual_model_owner_and_uuid(virtual_model, owner, uuid)
+ sd_uuids = SData::SdUuid.find(:all, :conditions => {:sd_class => virtual_model.sdata_name,
+ :bb_model_type => virtual_model.baze_class_name,
+ :uuid => uuid,
+ :user_id => owner.id})
+ sd_uuid = enforce_uniqueness(sd_uuids, virtual_model.sdata_name, virtual_model.baze_class_name)
+ sd_uuid
+ end
# This method is used to respond to a PUT to $linked('uuid'), to change what bb_model instance a uuid points to
# TODO this method makes no attempt to handle multiple baze_classes
# RADAR this method also has a RACE, but which should only happen when the provider/linking engine
# is doing something fubarred
- def reassign_uuid!(uuid, virtual_model, new_id)
+ def reassign_uuid!(uuid, virtual_model, owner, new_id)
raise "Cannot edit uuid for virtual_model (#{virtual_model.name}) with no fixed baze_class." if virtual_model.baze_class.nil?
- sd_uuids = SData::SdUuid.find(:all, :conditions => {:sd_class => virtual_model.sdata_name, :bb_model_type => virtual_model.baze_class_name, :uuid => uuid})
- sd_uuid = enforce_uniqueness(sd_uuids, virtual_model.sdata_name, virtual_model.baze_class_name)
+ sd_uuid = find_uuid_record_for_virtual_model_owner_and_uuid(virtual_model, owner, uuid)
sd_uuid.update_attributes!({:bb_model_id => new_id})
end
@@ -66,9 +71,10 @@ def create_or_update_uuid_for(virtual_instance, uuid)
sd_uuid = SData::SdUuid.find_for_virtual_instance(virtual_instance)
params = {:sd_class => virtual_instance.sdata_name,
- :bb_model_type => virtual_instance.baze_class_name,
- :bb_model_id => virtual_instance.baze.id,
- :uuid => uuid}
+ :bb_model_type => virtual_instance.baze_class_name,
+ :bb_model_id => virtual_instance.baze.id,
+ :user_id => virtual_instance.owner_id,
+ :uuid => uuid}
if sd_uuid
sd_uuid.update_attributes!(params)
else
@@ -79,22 +85,6 @@ def create_or_update_uuid_for(virtual_instance, uuid)
end
end
end
-
- # return all the bb_records which form the basis of this resource and which have been linked
- # MJ TODO -- check if there is a way to use the bb_model polymorphic assoc to autmatically create this query
- def linked_bb_records(endpoint=nil)
- klasses = baze_classes || [self]
- records = {}
- klasses.each do |klass|
- klassname = klass.name.demodulize
- tablename = klassname.tableize
- conditions = { :bb_model_type => klassname }
- # conditions[:endpoint] = endpoint # we don't need this now
- records[klassname.to_sym] = klass.all(:joins => "INNER JOIN sd_uuids ON sd_uuids.bb_model_id = #{tablename}.id",
- :conditions => {:sd_uuids => conditions})
- end
- records
- end
end
end
@@ -114,7 +104,16 @@ def uuid
# RADAR: This finds the most recently updated of potentially many sd_uuids -- see
# http://interop.sage.com/daisy/sdataSync/Link/525-DSY.html, linking scenario 3
def sd_uuid
- SData::SdUuid.find_for_virtual_instance(self)
+ result = SData::SdUuid.find_for_virtual_instance(self)
+ if result.nil? && (uuid = self.sdata_uuid_for_record)
+ if !SData::SdUuid.find_uuid_record_for_virtual_model_owner_and_uuid(self.class, self.owner, uuid)
+ result = create_or_update_uuid!(uuid)
+ else
+ SData::SdUuid.reassign_uuid!(uuid, self.class, self.owner, self.baze.id)
+ result = self
+ end
+ end
+ result
end
def create_or_update_uuid!(value)
View
11 lib/s_data/controller_mixin.rb
@@ -1,11 +0,0 @@
-module SData
- module ControllerMixin
- def acts_as_sdata(options)
- cattr_accessor :sdata_options
- self.sdata_options = options
- include Actions
- end
- end
-end
-
-ActionController::Base.extend SData::ControllerMixin
View
53 lib/s_data/controller_mixin/actions.rb
@@ -20,20 +20,16 @@ def sdata_collection
end
populate_open_search_for(collection)
build_feed_links_for(collection)
- render :xml => collection, :content_type => "application/atom+xml; type=feed"
- rescue Exception => e
- handle_exception(e)
+ content_type 'application/atom+xml; type=feed'
+ collection.to_xml
end
end
def sdata_show_instance
- begin
- instance = sdata_instance
- assert_access_to instance
- render :xml => instance.to_atom(params), :content_type => "application/atom+xml; type=entry"
- rescue Exception => e
- handle_exception(e)
- end
+ instance = sdata_instance
+ assert_access_to instance
+ content_type "application/atom+xml; type=entry"
+ instance.to_atom(params).to_xml
end
def sdata_create_instance
@@ -45,38 +41,41 @@ def sdata_update_instance
end
def sdata_create_link
- begin
- payload_xml = params['entry'].sdata_payload.raw_xml
+ payload_xml = params[:entry].sdata_payload.raw_xml
payload = Nokogiri::XML(payload_xml).root
id = payload.attributes['key'].value.to_i
uuid = payload.attributes['uuid'].value
- instance = model_class.find(id)
+ instance = sdata_resource.find(id)
assert_access_to instance
instance.create_or_update_uuid! uuid
- render :xml => instance.to_atom(params), :content_type => "application/atom+xml; type=entry", :status => "201"
- rescue Exception => e
- handle_exception(e)
- end
+ content_type "application/atom+xml; type=entry"
+ status 201
+ instance.to_atom(params).to_xml
end
- protected
-
- def model_class
- self.class.sdata_options[:model]
- end
+ protected
def assert_access_to(instance)
raise "Unauthenticated" unless logged_in?
# Not returning Access Denied on purpose so that users cannot fish for existence of emails or other data.
# As far as user should be concerned, all requests are scoped to his/her own data.
- # Data which is found but which belongs to someone else should be as good as data that doesn't exist.
- raise Sage::BusinessLogic::Exception::IncompatibleDataException, "Conditions scope must contain exactly one entry" if (instance.owner != target_user)
+ # Data which is found but which belongs to someone else should
+ # be as good as data that doesn't exist.
+ raise Sage::BusinessLogic::Exception::IncompatibleDataException, "Conditions scope must contain exactly one entry" unless accessible?(instance)
+ end
+
+ def accessible?(instance)
+ instance.owner == current_user or instance.owner.biller.bookkeepers.include?(current_user.bookkeeper)
end
-
def handle_exception(exception)
diagnosis = SData::Diagnosis::DiagnosisMapper.map(exception)
- render :xml => diagnosis.to_xml(:root), :status => diagnosis.http_status_code || 500
+
+ status diagnosis.http_status_code || 500
+ content_type 'application/xml'
+
+ diagnosis.to_xml(:root)
+ exception.to_s
end
include SDataInstance
@@ -84,4 +83,4 @@ def handle_exception(exception)
include CollectionScope
end
end
-end
+end
View
13 lib/s_data/controller_mixin/collection_scope.rb
@@ -18,7 +18,7 @@ def sdata_scope
options = {}
if where_clause?
- predicate = SData::Predicate.parse(model_class.payload_map.baze_fields, where_clause)
+ predicate = SData::Predicate.parse(sdata_resource.payload_map.baze_fields, where_clause)
options[:conditions] = predicate.to_conditions
end
@@ -35,23 +35,20 @@ def sdata_scope
if params.key? :condition
options[:conditions] ||= []
if params[:condition] == "$linked"
- virtual_class = sdata_options[:model].to_s.demodulize
- baze_class = sdata_options[:model].baze_class.name.demodulize
+ virtual_class = sdata_resource.name.demodulize
+ baze_class = sdata_resource.baze_class.name.demodulize
condition = "id IN (SELECT bb_model_id FROM sd_uuids WHERE bb_model_type = '#{baze_class}' and sd_class = '#{virtual_class}')"
options[:conditions][0] = [options[:conditions].to_a[0], condition].compact.join(' and ')
end
end
-
#FIXME: this is an unoptimized solution that may be a bottleneck for large number of matches
#if user has hundreds of records but requests first 10, we shouldnt load them all into memory
#but use sql query to count how many exist in total, and then load the first 10 only
-
- #FIXME: do not return records deleted through acts_as_paranoid!
- results = sdata_options[:model].all(options)
+ results = (params[:condition] == "$linked") ? sdata_resource.all_with_deleted(options) : sdata_resource.all(options)
@total_results = results.count
paginated_results = results[zero_based_start_index, records_to_return]
paginated_results.to_a
end
end
end
-end
+end
View
16 lib/s_data/controller_mixin/s_data_feed.rb
@@ -5,37 +5,37 @@ module SDataFeed
protected
def resource_url
- sdata_options[:model].sdata_resource_kind_url(params[:dataset])
+ sdata_resource.sdata_resource_kind_url(params[:dataset])
end
def build_feed_links_for(feed)
feed.links << Atom::Link.new(
:rel => 'self',
- :href => (resource_url + "?#{request.query_parameters.to_param}".chomp('?')),
+ :href => (resource_url + "?#{request.params.to_param}".chomp('?')),
:type => 'application/atom+xml; type=feed',
:title => 'Refresh')
if (records_to_return > 0) && (@total_results > records_to_return)
feed.links << Atom::Link.new(
:rel => 'first',
- :href => (resource_url + "?#{request.query_parameters.merge(:startIndex => '1').to_param}"),
+ :href => (resource_url + "?#{request.params.merge(:startIndex => '1').to_param}"),
:type => 'application/atom+xml; type=feed',
:title => 'First Page')
feed.links << Atom::Link.new(
:rel => 'last',
- :href => (resource_url + "?#{request.query_parameters.merge(:startIndex => [1,(@last=(((@total_results-zero_based_start_index - 1) / records_to_return * records_to_return) + zero_based_start_index + 1))].max).to_param}"),
+ :href => (resource_url + "?#{request.params.merge(:startIndex => [1,(@last=(((@total_results-zero_based_start_index - 1) / records_to_return * records_to_return) + zero_based_start_index + 1))].max).to_param}"),
:type => 'application/atom+xml; type=feed',
:title => 'Last Page')
if (one_based_start_index+records_to_return) <= @total_results
feed.links << Atom::Link.new(
:rel => 'next',
- :href => (resource_url + "?#{request.query_parameters.merge(:startIndex => [1,[@last, (one_based_start_index+records_to_return)].min].max.to_s).to_param}"),
+ :href => (resource_url + "?#{request.params.merge(:startIndex => [1,[@last, (one_based_start_index+records_to_return)].min].max.to_s).to_param}"),
:type => 'application/atom+xml; type=feed',
:title => 'Next Page')
end
if (one_based_start_index > 1)
feed.links << Atom::Link.new(
:rel => 'previous',
- :href => (resource_url + "?#{request.query_parameters.merge(:startIndex => [1,[@last, (one_based_start_index-records_to_return)].min].max.to_s).to_param}"),
+ :href => (resource_url + "?#{request.params.merge(:startIndex => [1,[@last, (one_based_start_index-records_to_return)].min].max.to_s).to_param}"),
:type => 'application/atom+xml; type=feed',
:title => 'Previous Page')
end
@@ -80,8 +80,8 @@ def populate_open_search_for(feed)
end
def category_term
- self.sdata_options[:model].name.demodulize.camelize(:lower).pluralize
+ self.sdata_resource.name.demodulize.camelize(:lower).pluralize
end
end
end
-end
+end
View
10 lib/s_data/controller_mixin/s_data_instance.rb
@@ -15,8 +15,8 @@ def sdata_instance
end
def sdata_instance_by_predicate
- predicate = SData::Predicate.parse(model_class.payload_map.baze_fields, params[:predicate])
- scope = model_class.all(:conditions => predicate.to_conditions)
+ predicate = SData::Predicate.parse(sdata_resource.payload_map.baze_fields, params[:predicate])
+ scope = sdata_resource.all(:conditions => predicate.to_conditions)
if scope.count != 1
raise Sage::BusinessLogic::Exception::IncompatibleDataException, "Conditions scope must contain exactly one entry"
end
@@ -24,12 +24,12 @@ def sdata_instance_by_predicate
end
def linked_sdata_instance
- SData::SdUuid.find_by_virtual_model_and_uuid(model_class, Predicate.strip_quotes(params[:instance_id]))
+ SData::SdUuid.find_by_virtual_model_owner_and_uuid(sdata_resource, target_user, Predicate.strip_quotes(params[:instance_id]))
end
def non_linked_sdata_instance
- model_class.find_by_sdata_instance_id(Predicate.strip_quotes(params[:instance_id]))
+ sdata_resource.find_by_sdata_instance_id(Predicate.strip_quotes(params[:instance_id]))
end
end
end
-end
+end
View
2  lib/s_data/diagnosis/diagnosis_mapper.rb
@@ -29,6 +29,8 @@ def self.map(exception, request_path='')
SData::ApplicationDiagnosis.new(:exception => exception, :http_status_code => '401')
when 'IncompatibleDataException'
SData::BadWhereSyntax.new(:exception => exception, :http_status_code => '409')
+ when 'NotFound'
+ SData::ResourceKindNotFound.new(:exception => exception, :http_status_code => '404')
else
SData::ApplicationDiagnosis.new(:exception => exception, :http_status_code => '500')
end
View
4 lib/s_data/exceptions.rb
@@ -1,5 +1,9 @@
module SData
module Exceptions
+ module SdUuid
+ class NotFound < ArgumentError
+ end
+ end
module VirtualBase
class InvalidSDataAttribute < ArgumentError
end
View
10 lib/s_data/payload.rb
@@ -67,7 +67,7 @@ def construct_from_sdata_model(node_name, node_value, expand, element_precedence
attributes.each_pair do |key,value|
node[key] = value.to_s
end
- if (node_name == self.root_node_name) || (expand != :none) || included.include?(formatted(node_name))
+ if (node_name == self.root_node_name) || (expand != :none) || included.include?(node_name)
expand = :none if (expand == :immediate_children)
node_value.payload_map.each_pair do |child_node_name, child_node_data|
if child_node_data[:type] == :association
@@ -75,9 +75,9 @@ def construct_from_sdata_model(node_name, node_value, expand, element_precedence
else
child_expand = (is_sync? ? :all_children : expand)
end
- collection = ({:parent => node_value, :url => formatted(child_node_name), :type => node_value.payload_map[child_node_name][:type]})
+ collection = ({:parent => node_value, :url => child_node_data[:sdata_node_name], :type => node_value.payload_map[child_node_name][:type]})
attribute_method_name = sdata_attribute_method(child_node_data)
- node << generate(formatted(child_node_name), node_value.send(attribute_method_name), child_expand, child_node_data[:precedence], collection)
+ node << generate(child_node_data[:sdata_node_name], node_value.send(attribute_method_name), child_expand, child_node_data[:precedence], collection)
end
end
node
@@ -111,7 +111,7 @@ def construct_from_non_sdata_array(node_name, node_value, expand, element_preced
expand = :none if (expand == :immediate_children)
node = XML::Node.new(qualified(node_name))
node_value.each do |item|
- node << generate(formatted(node_name).singularize, item, expand, element_precedence, (item.is_a?(Hash) ? item[:resource_collection] : nil))
+ node << generate(node_name.singularize, item, expand, element_precedence, (item.is_a?(Hash) ? item[:resource_collection] : nil))
end
node
end
@@ -136,7 +136,7 @@ def construct_from_string(node_name, node_value, expand, element_precedence, res
end
def qualified(node_name)
- "#{contract}:#{(formatted(node_name))}"
+ "#{contract}:#{node_name}"
end
def formatted(node_name)
View
7 lib/s_data/payload_map/payload_map.rb
@@ -8,6 +8,7 @@ def define_payload_map(map)
self.payload_map = PayloadMapHash.new(map)
self.payload_map.each do |name, opts|
has_sdata_attr(name, opts)
+ opts[:sdata_node_name] = name.to_s.camelize(:lower)
end
end
@@ -78,8 +79,8 @@ def associations_with_deleted(expand=:all_children)
yield child_node_data.merge(:name => child_node_name), child if block_given?
case child
when Array
- child.each{ |grandchild| grandchild.associations_with_deleted(expand) if child.is_a?(SData::VirtualBase)}
- when SData::VirtualBase
+ child.each{ |grandchild| grandchild.associations_with_deleted(expand) if child.is_a?(SData::Resource)}
+ when SData::Resource
associations_with_deleted(expand)
end
end
@@ -133,4 +134,4 @@ def define_proc_with_deleted_caller(method_name, block)
end
end
-SData::VirtualBase.extend SData::PayloadMap
+SData::Resource.extend SData::PayloadMap
View
34 lib/s_data/resource.rb
@@ -0,0 +1,34 @@
+module SData
+ class Resource
+ cattr_accessor :registered_resources
+
+ def self.inherited(child)
+ class << child
+ include ClassMethods
+ end
+
+ child.register_resource
+ child.define_own_sdata_options
+
+ child.__send__ :include, SData::Traits::VirtualBase
+ end
+
+ def self.has_sdata_options(options)
+ self.sdata_options = options
+ end
+
+ module ClassMethods
+ def register_resource
+ key = self.name.demodulize.underscore.to_sym
+ (self.registered_resources ||= {})[key] = self
+ end
+
+ def define_own_sdata_options
+ class << self
+ attr_accessor :sdata_options
+ end
+ self.sdata_options = {}
+ end
+ end
+ end
+end
View
111 lib/s_data/sync/actions.rb
@@ -0,0 +1,111 @@
+module SData
+ module Sync
+ module Actions
+ # TODO: error handling
+ def sdata_collection_sync_feed
+ # get the target digest
+ payload = params[:entry].sdata_payload
+
+ raise "no payload!" if payload.blank?
+
+ sd_sync_run = SData::SdSyncRun.find_by_tracking_id(tracking_id)
+ raise Sage::BusinessLogic::Exception::AccessDeniedException, "Access denied" unless sd_sync_run.nil?
+
+ # prepare a sync run to hold our state
+ sd_sync_run = SData::SdSyncRun.new( :tracking_id => tracking_id,
+ :run_name => params[:runName],
+ # :created_at => params[:runStamp],
+ :sd_class => sdata_resource.sdata_name,
+ :created_by => target_user)
+ sd_sync_run.target_digest = payload.sync_digest
+
+ sd_sync_run.start!
+ sd_sync_run.process!
+
+ headers 'Location' => sync_source_url(sd_sync_run.tracking_id)
+ content_type "application/xml"
+ status 202
+ sd_sync_run.to_xml.to_s
+ end
+
+ def sdata_collection_sync_feed_status
+ sd_sync_run = SData::SdSyncRun.find_by_tracking_id(tracking_id)
+ raise Sage::BusinessLogic::Exception::AccessDeniedException, "Access denied" if sd_sync_run.nil?
+ assert_access_to sd_sync_run
+
+ if sd_sync_run.error?
+ if sd_sync_run.objects && sd_sync_run.objects.is_a?(Exception)
+ raise sd_sync_run.objects
+ else
+ raise "Unknown Error in Sync"
+ end
+ end
+
+ if sd_sync_run.finished?
+ @total_results = sd_sync_run.total_results
+ feed = build_sdata_feed(:feed => {:title => "#{sdata_resource.sdata_name} synchronization feed #{params[:trackingID]}"})
+ feed[SData.config[:schemas]['sync'], 'syncMode'] << "catchUp"
+
+ feed.sync_digest = sd_sync_run.source_digest
+
+ atom_entries = []
+ errors = []
+ sd_sync_run.objects[zero_based_start_index,records_to_return].each do |obj|
+ begin
+ # RADAR: this (the (params) part) will allow user to insert a different dataset (and possibly
+ # other data) than that which was synchronized during the run. If this is somehow a security
+ # problem, need to freeze that data during synctime. an be also solved by freezing username
+ # in feed and matching dataset against it at accestime.
+ # RADAR: children (e.g. line items) will be embedded in parent (e.g. invoice) during this
+ # request, but from live data rather than syncronized one, is this a potential problem?
+ feed.entries << obj.to_atom(params.merge(:sync => true)){|entry| entry.sync_syncState = obj.sd_sync_state }
+ rescue Exception => e
+ errors << ApplicationDiagnosis.new(:exception => e).to_xml(:feed)
+ end
+ end
+ #TODO: syntactic sugar if possible (such as diagnosing_errors(&block) which does the dirty work)
+ errors.each do |error|
+ feed[SData.config[:schemas]['sdata'], 'diagnosis'] << error
+ end
+ populate_open_search_for(feed)
+ build_feed_links_for(feed)
+ content_type "application/atom+xml; type=feed"
+ feed.to_xml
+ else
+ content_type "application/atom+xml; type=feed"
+ headers 'Location' => sync_source_url(sd_sync_run.tracking_id)
+ status 202
+ sd_sync_run.to_xml.to_s
+ end
+ end
+
+ def sdata_collection_sync_feed_delete
+ sd_sync_run = SData::SdSyncRun.find_by_tracking_id(tracking_id)
+ sd_sync_run.destroy
+ "OK"
+ end
+
+ def sdata_collection_sync_results
+ "Not implemented!"
+ end
+
+ protected
+ def syncing?
+ params.key? :trackingID
+ end
+
+ def resource_url
+ syncing? ? super + "/$syncSource('#{tracking_id}')" : super
+ end
+
+ def sync_source_url(track)
+ endpoint = target_user.endpoint
+ "#{endpoint}/#{SData.sdata_collection_url_component(sdata_resource)}/$syncSource('#{track}')"
+ end
+
+ def tracking_id
+ CGI::unescape(params[:trackingID]).gsub(/^'/,'').gsub(/'$/,'')
+ end
+ end
+ end
+end
View
122 lib/s_data/sync/controller_mixin.rb
@@ -1,122 +0,0 @@
-module SData
- module Sync
- module ControllerMixin
- def syncs_sdata(options={})
- raise "cannot sync sdata unless controller first acts_as_sdata" if self.sdata_options.nil?
- cattr_accessor :sdata_sync_options
- self.sdata_sync_options = options
-
- include InstanceMethods
- end
-
- module InstanceMethods
- module Actions
- # TODO: error handling
- def sdata_collection_sync_feed
- # get the target digest
- payload = params[:entry].sdata_payload
-
- raise "no payload!" if payload.blank?
-
- sd_sync_run = SData::SdSyncRun.find_by_tracking_id(tracking_id)
- raise Sage::BusinessLogic::Exception::AccessDeniedException, "Access denied" unless sd_sync_run.nil?
-
- # prepare a sync run to hold our state
- sd_sync_run = SData::SdSyncRun.new( :tracking_id => tracking_id,
- :run_name => params[:runName],
- # :created_at => params[:runStamp],
- :sd_class => model_class.sdata_name,
- :created_by => target_user)
- sd_sync_run.target_digest = payload.sync_digest
-
- sd_sync_run.start!
- sd_sync_run.process!
- xml = sd_sync_run.to_xml.to_s
- render :xml => xml, :content_type => "application/xml", :location => sync_source_url(sd_sync_run.tracking_id), :status => 202
- end
-
- def sdata_collection_sync_feed_status
- sd_sync_run = SData::SdSyncRun.find_by_tracking_id(tracking_id)
- raise Sage::BusinessLogic::Exception::AccessDeniedException, "Access denied" if sd_sync_run.nil?
- assert_access_to sd_sync_run
- if sd_sync_run.finished?
- @total_results = sd_sync_run.total_results
- feed = build_sdata_feed(:feed => {:title => "#{model_class.sdata_name} synchronization feed #{params[:trackingID]}"})
- feed[SData.config[:schemas]['sync'], 'syncMode'] << "catchUp"
-
- feed.sync_digest = sd_sync_run.source_digest
-
- atom_entries = []
- errors = []
- sd_sync_run.objects[zero_based_start_index,records_to_return].each do |obj|
- begin
- # RADAR: this (the (params) part) will allow user to insert a different dataset (and possibly
- # other data) than that which was synchronized during the run. If this is somehow a security
- # problem, need to freeze that data during synctime. an be also solved by freezing username
- # in feed and matching dataset against it at accestime.
- # RADAR: children (e.g. line items) will be embedded in parent (e.g. invoice) during this
- # request, but from live data rather than syncronized one, is this a potential problem?
- feed.entries << obj.to_atom(params.merge(:sync => true)){|entry| entry.sync_syncState = obj.sd_sync_state }
- rescue Exception => e
- errors << ApplicationDiagnosis.new(:exception => e).to_xml(:feed)
- end
- end
- #TODO: syntactic sugar if possible (such as diagnosing_errors(&block) which does the dirty work)
- errors.each do |error|
- feed[SData.config[:schemas]['sdata'], 'diagnosis'] << error
- end
- populate_open_search_for(feed)
- build_feed_links_for(feed)
- render :xml => feed, :content_type => "application/atom+xml; type=feed"
- else
- render :xml => sd_sync_run.to_xml.to_s,
- :content_type => "application/atom+xml; type=feed",
- :location => sync_source_url(sd_sync_run.tracking_id),
- :status => 202
- end
- end
-
- def sdata_collection_sync_feed_delete
- sd_sync_run = SData::SdSyncRun.find_by_tracking_id(tracking_id)
- sd_sync_run.destroy
- render :text => "OK"
- end
-
- def sdata_collection_sync_results
- render :text => "Not implemented!"
- end
- end
- module AuxiliaryMethods
- protected
-
- # TODO: sort out profusion of urls endpoints and config classes!
- def resource_url
- case self.action_name
- when 'sdata_collection_sync_feed_status',
- 'sdata_collection_sync_feed_status',
- 'sdata_collection_sync_feed_delete'
- super + "/$syncSource('#{tracking_id}')"
- else
- super
- end
- end
-
- def sync_source_url(track)
- endpoint = target_user.endpoint
- "#{endpoint}/#{SData.sdata_collection_url_component(model_class)}/$syncSource('#{track}')"
- end
-
- def tracking_id
- CGI::unescape(params[:trackingID]).gsub(/^'/,'').gsub(/'$/,'')
- end
-
-
- end
- include Actions
- include AuxiliaryMethods
- end
- end
- end
-end
-ActionController::Base.extend SData::Sync::ControllerMixin
-# Atom::Entry.elements "sync:digest", :class => SData::SdDigest::AtomWrapper
View
6 lib/s_data/sync/sdata_syncing_mixin.rb → lib/s_data/sync/resource_mixin.rb
@@ -1,14 +1,14 @@
module SData
module Sync
- module SdataSyncingMixin
+ module ResourceMixin
def sd_digest
SData::SdDigest.find_or_create(self.owner, self.sdata_name)
end
-
+
def sd_sync_state
@sd_sync_state ||= SdSyncState.find(:first, :conditions => {:sd_uuid_id => self.sd_uuid, :sd_digest_id => self.sd_digest})
end
-
+
def sd_sync_state=(s)
@sd_sync_state = s
end
View
18 lib/s_data/trait.rb
@@ -0,0 +1,18 @@
+class Trait < Module
+ def self.new(&block)
+ mod = super {}
+ class << mod
+ include ClassMethods
+ end
+ mod.deferred_block = block
+ mod
+ end
+
+ module ClassMethods
+ attr_accessor :deferred_block
+
+ def included(base)
+ base.class_eval &self.deferred_block
+ end
+ end
+end
View
232 lib/s_data/virtual_base.rb
@@ -1,114 +1,146 @@
module SData
- class VirtualBase
- extend Forwardable
- include SData::Sync::SdataSyncingMixin
-
- class << self
- attr_accessor :baze_class
- end
-
- class_inheritable_accessor :owner_key
- self.owner_key = 'created_by_id'
-
- attr_accessor :baze
- def_delegators :baze, :id, :created_at, :updated_at, :save, :save!, :update_attributes, :update_attributes!, :created_by, :reload
-
- def initialize(the_baze, the_type=nil)
- @uuid_class = SData::SdUuid
- self.baze = the_baze
- if self.respond_to?('sdata_type') && the_type
- self.sdata_type = the_type
- end
- super()
- end
+ module Traits
+ VirtualBase = Trait.new do
+ extend Forwardable
+ include SData::Sync::ResourceMixin
- # temporary
- def method_missing(meth, *args, &block)
- if @payload
- @payload.send(meth, *args, &block)
- else
- super
- end
- end
+ cattr_accessor :baze_class
- def baze_class_name
- self.class.baze_class_name
- end
-
- def reference
- # GCRM requires reference to be present and unique to each GCRM model. Decided to use baze id, but
- # alone it is insufficient because some GCRM models (e.g. Address) can have the same baze id for different
- # bazes (e.g. customer vs user). So using the form below.
- # Adding GCRM model name is fine but unneeded. GCRM name _only_, without BB model name, is insufficient.
- "#{self.baze.class.name.demodulize}_#{self.baze.id}"
- end
-
- def sd_class
- self.class.name
- end
-
- def self.build_for(data, the_type=nil)
- if data.is_a? Array
- data.map {|item| virtual_base_for_object(item, the_type) }.compact
- else
- virtual_base_for_object(data, the_type)
+ cattr_accessor :owner_key
+ self.owner_key = 'created_by_id'
+
+ attr_accessor :baze
+ def_delegators :baze, :id, :created_at, :updated_at, :save, :save!, :update_attributes, :update_attributes!, :created_by, :reload
+
+ def initialize(the_baze=nil, the_type=nil)
+ self.baze = the_baze
+ self.baze ||= self
+ if self.respond_to?('sdata_type') && the_type
+ self.sdata_type = the_type
+ end
+ super()
end
- end
-
- def self.virtual_base_for_object(obj, the_type=nil)
- if obj
- vb = self.new(obj, the_type)
- vb = DeletedObjectProxy.from_virtual_base(vb) if obj.class.paranoid? && obj.deleted?
- vb
- else
- nil
+
+ # temporary
+ def method_missing(meth, *args, &block)
+ if @payload
+ @payload.send(meth, *args, &block)
+ else
+ super
+ end
end
- end
-
- def owner
- raise "Security problem: owner not defined in subclass!"
- end
- # TODO -- should return all bb models that are used to composite the virtual model (or at least the subset whose modifications must be tracked)
+ def baze_class_name
+ @baze_class_name ||= self.class.baze_class_name
+ @baze_class_name ||= self.baze_class_name_from_baze_instance
+ end
- def self.baze_classes
- [self.baze_class]
- end
-
- def self.baze_class_name
- baze_class.nil? ? nil : baze_class.name.demodulize
- end
-
- def self.sdata_name
- name.demodulize
- end
+ # TODO: I don't think we should demodulize here. We may be screwing up consumer's use of namespaces. But taking it out will require a lot of test fixing
+ def baze_class_name_from_baze_instance
+ klass = baze.class
+ while(!klass.descends_from_active_record?) do; klass = klass.superclass; end
+ klass.name.demodulize
+ end
- def self.find(*params)
- self.new(self.baze_class.find(*params))
- end
+ def reference
+ # GCRM requires reference to be present and unique to each GCRM model. Decided to use baze id, but
+ # alone it is insufficient because some GCRM models (e.g. Address) can have the same baze id for different
+ # bazes (e.g. customer vs user). So using the form below.
+ # Adding GCRM model name is fine but unneeded. GCRM name _only_, without BB model name, is insufficient.
+ "#{self.baze_class_name}_#{self.baze.id}"
+ end
- def self.first(*params)
- self.new(self.baze_class.first(*params))
- end
-
- def self.all(*params)
- self.collection(self.baze_class.all(*params))
- end
-
- def self.collection(arr)
- arr.map do |obj|
- case obj
- when VirtualBase, DeletedObjectProxy
- obj
- when ActiveRecord::Base
- self.new(obj)
+ def sd_class
+ self.class.name
+ end
+
+ # Override to provide automatic creation of sd_uuids. Currently, this automatic creation will only happen
+ # when records are requested by id or as a collection.
+ def sdata_uuid_for_record
+ nil
+ end
+
+ def self.build_for(data, the_type=nil)
+ if data.is_a? Array
+ data.map {|item| virtual_base_for_object(item, the_type) }.compact
else
- obj
+ virtual_base_for_object(data, the_type)
end
end
+
+ def self.virtual_base_for_object(obj, the_type=nil)
+ if obj
+ vb = self.new(obj, the_type)
+ vb = DeletedObjectProxy.from_virtual_base(vb) if obj.class.paranoid? && obj.deleted?
+ vb
+ else
+ nil
+ end
+ end
+
+ def owner
+ raise "Security problem: owner not defined in subclass!"
+ end
+
+ # database foreign_key for owner. Redefine in subclasses if necessary
+ def owner_id
+ owner.nil? ? nil : owner.id
+ end
+
+ # TODO -- should return all bb models that are used to composite the virtual model (or at least the subset whose modifications must be tracked)
+ def self.baze_classes
+ @baze_classes ||= [self.baze_class]
+ end
+
+ # TODO: I don't think we should demodulize here. We may be screwing up consumer's use of namespaces. But taking it out will require a lot of test fixing
+ def self.baze_class_names
+ @baze_class_names ||= [self.baze_class.name.demodulize]
+ end
+
+ # TODO: I don't think we should demodulize here. We may be screwing up consumer's use of namespaces. But taking it out will require a lot of test fixing
+ def self.baze_class_name
+ @baze_class_name = (baze_class.nil? ? nil : baze_class.name.demodulize)
+ end
+
+ def self.sdata_name
+ name.demodulize
+ end
+
+ def self.find(*params)
+ self.new(self.baze_class.find(*params))
+ end
+
+ def self.find_with_deleted(*params)
+ self.new(self.baze_class.find_with_deleted(*params))
+ end
+
+ def self.first(*params)
+ self.new(self.baze_class.first(*params))
+ end
+
+ def self.all(*params)
+ self.collection(self.baze_class.all(*params))
+ end
+
+ def self.all_with_deleted(*params)
+ self.collection(self.baze_class.find_with_deleted(:all, *params))
+ end
+
+ def self.collection(arr)
+ arr.map do |obj|
+ case obj
+ when VirtualBase, DeletedObjectProxy
+ obj
+ when ActiveRecord::Base
+ self.virtual_base_for_object(obj)
+ else
+ obj
+ end
+ end
+ end
+
+ extend SData::ActiveRecordExtensions::Mixin
+ extend SData::ActiveRecordExtensions::SdataUuidableMixin
end
-
end
- VirtualBase.extend SData::ActiveRecordExtensions::Mixin
- VirtualBase.extend SData::ActiveRecordExtensions::SdataUuidableMixin
-end
+end
View
2  spec/active_record_mixin/payload_spec.rb
@@ -41,7 +41,9 @@ def payload_header_assertions(payload)
@customer.contacts[1].name = "Second Contact Name"
@customer.contacts.each do |contact|
contact.populate_defaults
+ contact.customer = @customer
end
+ @customer.created_by = User.new( :id => 123)
@customer.address.populate_defaults
end
View
7 spec/class_stubs/address.rb
@@ -3,7 +3,12 @@
class Address < SData::VirtualBase
- attr_writer :city, :created_at, :updated_at, :owner
+ def self.descends_from_active_record?
+ true
+ end
+
+ attr_writer :city, :created_at, :updated_at
+ attr_accessor :owner
def populate_defaults
self.city = @city || "Vancouver"
self.created_at = @created_at || Time.now-2.days
View
8 spec/class_stubs/contact.rb
@@ -2,6 +2,10 @@ class Contact < ModelBase
attr_writer :customer, :name, :created_at, :updated_at
+ def self.descends_from_active_record?
+ true
+ end
+
def baze
self
end
@@ -14,6 +18,10 @@ def populate_defaults
self
end
+ def owner
+ @customer.owner
+ end
+
def sdata_content
"Contact ##{self.id}: #{self.name}"
end
View
12 spec/class_stubs/customer.rb
@@ -2,14 +2,14 @@ class Customer < ModelBase
attr_writer :created_by, :name, :number, :contacts, :created_at, :updated_at, :address
- def self.is_a?(value)
- # Don't really like doing this but don't see a quick better way
- return true if value == SData::VirtualBase
- super
+ def self.descends_from_active_record?
+ true
end
+
def baze
self
end
+
def populate_defaults
self.id = @id || object_id.abs
self.name = @name || "Customer Name"
@@ -33,6 +33,10 @@ def created_by_id
@created_by ? @created_by.id : nil
end
+ def owner
+ @created_by
+ end
+
def sdata_content
"Customer ##{self.id}: #{self.name}"
end
View
4 spec/class_stubs/model_base.rb
@@ -1,5 +1,4 @@
-class ModelBase
- extend SData::PayloadMap
+class ModelBase < SData::VirtualBase
attr_accessor :id
def self.name
@@ -14,4 +13,5 @@ def attributes
def sdata_options
{}
end
+
end
View
8 spec/class_stubs/user.rb
@@ -1,5 +1,9 @@
class User < ModelBase
+ def self.descends_from_active_record?
+ true
+ end
+
attr_writer :name, :password, :customers, :created_at, :updated_at
def populate_defaults
self.id = @id || object_id.abs
@@ -11,6 +15,10 @@ def populate_defaults
self
end
+ def owner
+ self
+ end
+
def record_id
@record ? @record.id : nil
end
View
4 spec/controller_mixin/sdata_scope/linked_model_spec.rb
@@ -38,7 +38,7 @@ class Controller < ActionController::Base
end
it "should apply to SData::Predicate for conditions and append requirement for simply guid" do
- Model.should_receive(:all).with :conditions => ['"born_at" > ? and id IN (SELECT bb_model_id FROM sd_uuids WHERE bb_model_type = \'BaseModel\' and sd_class = \'Model\')', '1900']
+ BaseModel.should_receive(:find_with_deleted).with(:all, {:conditions => ['"born_at" > ? and id IN (SELECT bb_model_id FROM sd_uuids WHERE bb_model_type = \'BaseModel\' and sd_class = \'Model\')', '1900']}).and_return([])
@controller.send :sdata_scope
end
end
@@ -49,7 +49,7 @@ class Controller < ActionController::Base
end
it "should return all entity records with simply guid" do
- Model.should_receive(:all).with :conditions => ['id IN (SELECT bb_model_id FROM sd_uuids WHERE bb_model_type = \'BaseModel\' and sd_class = \'Model\')']
+ BaseModel.should_receive(:find_with_deleted).with(:all, {:conditions => ['id IN (SELECT bb_model_id FROM sd_uuids WHERE bb_model_type = \'BaseModel\' and sd_class = \'Model\')']}).and_return([])
@controller.send :sdata_scope
end
end
View
2  spec/controller_mixin/sdata_scope/scoping_in_config_spec.rb
@@ -55,7 +55,7 @@ class Controller < ActionController::Base
end
it "should return all entity records with created_by, predicate, and link scope" do
- Model.should_receive(:all).with :conditions => ['"born_at" > ? and created_by_id = ? and id IN (SELECT bb_model_id FROM sd_uuids WHERE bb_model_type = \'BaseModel\' and sd_class = \'Model\')', '1900', @user.id.to_s]
+ BaseModel.should_receive(:find_with_deleted).with(:all, {:conditions => ['"born_at" > ? and created_by_id = ? and id IN (SELECT bb_model_id FROM sd_uuids WHERE bb_model_type = \'BaseModel\' and sd_class = \'Model\')', '1900', @user.id.to_s]}).and_return([])
@controller.send :sdata_scope
end
end
View
2  spec/payload_map/payload_map_spec.rb
@@ -46,7 +46,7 @@ def method_missing(meth, *args, &block)
describe "#payload_map" do
it "should store it correctly" do
- subject.payload_map[:tax_reference].should == { :static_value => 'Some static tax reference', :method_name => :tax_reference, :method_name_with_deleted=>:tax_reference}
+ subject.payload_map[:tax_reference].should == { :static_value => 'Some static tax reference', :method_name => :tax_reference, :method_name_with_deleted=>:tax_reference, :sdata_node_name=>"taxReference"}
end
end
end
View
58 spec/resource_spec.rb
@@ -0,0 +1,58 @@
+require File.join(File.dirname(__FILE__), 'spec_helper')
+
+describe SData::Resource do
+ describe "#registered_resources" do
+ context "when I inherit couple of classes" do
+ before :each do
+ class TradingAccount < SData::Resource; end
+ class SalesInvoice < SData::Resource; end
+ end
+
+ it "should give access to children by symbol" do
+ SData::Resource.registered_resources[:trading_account].should == TradingAccount
+ SData::Resource.registered_resources[:sales_invoice].should == SalesInvoice
+ end
+
+ context "when child classes are in namespaces" do
+ before :each do
+ module SData
+ module Contracts
+ module CrmErp
+ class PostalAddress < SData::Resource; end
+ end
+ end
+ end
+ end
+
+ # ??? perhaps namespace should be taken into account and be extracted from the URL (billingboss/crmErp/-/...)
+ it "should give access to these children by keys without namespace" do
+ SData::Resource.registered_resources[:postal_address].should == SData::Contracts::CrmErp::PostalAddress
+ end
+ end
+ end
+ end
+
+ describe "#has_sdata_options" do
+ context "when two SData resources with different options" do
+ before :each do
+ class TradingAccount < SData::Resource;
+ has_sdata_options :value => 'TradingAccount'
+ end
+
+ class SalesInvoice < SData::Resource
+ has_sdata_options :value => 'SalesInvoice'
+ end
+ end
+
+ it "should respond to #sdata_options" do
+ TradingAccount.should respond_to(:sdata_options)
+ SalesInvoice.should respond_to(:sdata_options)
+ end
+
+ it "should return correspondent value" do
+ TradingAccount.sdata_options.should == { :value => 'TradingAccount' }
+ SalesInvoice.sdata_options.should == { :value => 'SalesInvoice' }
+ end
+ end
+ end
+end
View
2  spec/router_mixin/routes_spec.rb
@@ -26,7 +26,7 @@ def mock_request(path, method, params)
before :all do
TestController = Class.new
@router = Usher::Interface.for(:rails23)
- @router.draw(:delimiters => ['/', '.', '!', '\(', '\)' ]) do |map|
+ @router.draw(:delimiters => ['/', '.', '\(', '\)' ]) do |map|
map.test '/test', :controller => 'test', :action => 'test_action'
map.sdata_resource :items
View
36 spec/trait_spec.rb
@@ -0,0 +1,36 @@
+require File.join(File.dirname(__FILE__), 'spec_helper')
+
+describe Trait do
+ context "when initialized" do
+ it "should not raise any errors" do
+ Trait.new { blah blah blah }
+ end
+
+ it "should not evaluate the code" do
+ object = mock()
+ Trait.new { object.touch }
+ object.should_not_receive(:touch)
+ end
+ end
+
+ context "given a trait which potentially does a lot" do
+ before :all do
+ MightyTrait = Trait.new do
+ acts_as_sdata :option => :value
+
+ def field; end
+ def self.class_field; end
+ end
+ end
+
+ it "should do his stuff when included" do
+ TraitContainer = Class.new
+ TraitContainer.should_receive(:acts_as_sdata)
+
+ TraitContainer.send :include, MightyTrait
+
+ TraitContainer.new.should respond_to(:field)
+ TraitContainer.should respond_to(:class_field)
+ end
+ end
+end
Please sign in to comment.
Something went wrong with that request. Please try again.