Skip to content

Commit

Permalink
ONE BIG COMMIT (it is bad, very bad)
Browse files Browse the repository at this point in the history
  • Loading branch information
DanielVartanov committed May 20, 2010
1 parent eecbdcc commit 83f815d
Show file tree
Hide file tree
Showing 11 changed files with 649 additions and 114 deletions.
123 changes: 37 additions & 86 deletions lib/active_record_mixin.rb
Expand Up @@ -19,45 +19,51 @@ def to_atom(params=nil)
params ||= {} params ||= {}
maximum_precedence = (!params[:precedence].blank? ? params[:precedence].to_i : 100) maximum_precedence = (!params[:precedence].blank? ? params[:precedence].to_i : 100)
included = params[:include].to_s.split(',') included = params[:include].to_s.split(',')
expand = (included.include?('$children') ? :all : :immediate_children) selected = params[:select].to_s.split(',') + ['_root']
expand = (included.include?('$children') ? :all_children : :immediate_children)
returning Atom::Entry.new do |entry| returning Atom::Entry.new do |entry|

entry.id = self.sdata_resource_url
entry.id = self.sdata_resource_url entry.title = entry_title
entry.title = entry_title entry.updated = self.updated_at
entry.updated = self.updated_at entry.authors << Atom::Person.new(:name => self.respond_to?('author') ? self.author : sdata_default_author)
entry.authors << Atom::Person.new(:name => self.respond_to?('author') ? self.author : sdata_default_author) entry.links << Atom::Link.new(:rel => 'self',
entry.links << Atom::Link.new(:rel => 'self', :href => self.sdata_resource_url,
:href => self.sdata_resource_url, :type => 'applicaton/atom+xml; type=entry',
:type => 'applicaton/atom+xml; type=entry', :title => 'Refresh')
:title => 'Refresh') entry.categories << Atom::Category.new(:scheme => 'http://schemas.sage.com/sdata/categories',
entry.categories << Atom::Category.new(:scheme => 'http://schemas.sage.com/sdata/categories',
:term => 'resource', :term => 'resource',
:label => 'Resource') :label => 'Resource')

if maximum_precedence > 0
if maximum_precedence > 0 begin
#raise 'entry-level error' entry.payload = Atom::Content::Payload.new(Payload.generate(self.sdata_node_name, self, expand, included, selected, 1, maximum_precedence, nil))
begin rescue Exception => e
entry.payload = Atom::Content::Payload.new(self.payload(self.sdata_node_name, self, expand, included, 1, maximum_precedence)) entry.diagnosis = Atom::Content::Diagnosis.new(ApplicationDiagnosis.new(:exception => e).to_xml(:entry))
rescue Exception => e
entry.diagnosis = Atom::Content::Diagnosis.new(ApplicationDiagnosis.new(:exception => e).to_xml(:entry))
end
end end
entry.content = sdata_content end

entry.content = sdata_content
end end
end end


protected def sdata_node_name(entity=self.class)
entity.to_s.demodulize.camelize(:lower)
end


def sdata_default_author def resource_header_attributes(resource, included)
"Billing Boss" hash = {}
hash.merge!({"xlmns:sdata:key" => resource.id, "xlmns:sdata:url" => resource.sdata_resource_url}) if resource.id
hash.merge!("xlmns:sdata:descriptor" => resource.entry_content) if included.include?("$descriptor")
hash.merge!("xlmns:sdata:uuid" => resource.uuid.to_s) if resource.respond_to?("uuid") && !resource.uuid.blank?
hash
end end
def sdata_resource_url()
def sdata_resource_url
$APPLICATION_URL + $SDATA_STORE_PATH + sdata_node_name.pluralize + "('#{self.id}')" $APPLICATION_URL + $SDATA_STORE_PATH + sdata_node_name.pluralize + "('#{self.id}')"
end end


def sdata_collection_url(parent, id, child) protected
$APPLICATION_URL + $SDATA_STORE_PATH + parent.camelize(:lower) + "('#{id}')/" + child
def sdata_default_author
"Billing Boss"
end end


def entry_title def entry_title
Expand All @@ -77,66 +83,11 @@ def entry_content
def default_entry_content def default_entry_content
self.class.name self.class.name
end end

def xmlns_qualifier_for(element)
"xmlns:crmErp:#{element}"
end

def sdata_node_name(entity=self.class)
entity.to_s.demodulize.camelize(:lower)
end

def resource_header_attributes(resource, included)
hash = {"xlmns:sdata:key" => resource.id, "xlmns:sdata:url" => resource.sdata_resource_url}
hash.merge!("xlmns:sdata:descriptor" => resource.entry_content) if included.include?("$descriptor")
hash.merge!("xlmns:sdata:uuid" => resource.uuid.to_s) if resource.respond_to?("uuid") && !resource.uuid.blank?
hash
end


def payload(node_name, node_value, expand, included, element_precedence, maximum_precedence, resource_collection=nil)
return "" if element_precedence > maximum_precedence
builder = Builder::XmlMarkup.new
if node_value.respond_to?('payload_map')
builder.__send__(node_value.xmlns_qualifier_for(node_name), resource_header_attributes(node_value, included)) do |element|
if (expand != :none) || included.include?(node_name.to_s.camelize(:lower))
node_value.payload_map.each_pair do |child_node_name, child_node_data|
expand = :none if (expand == :immediate_children)
element << node_value.payload(child_node_name.to_s.camelize(:lower), child_node_data[:value], expand, included, child_node_data[:precedence], maximum_precedence, child_node_data[:resource_collection])
end
end
end
elsif node_value.is_a?(Array)
if resource_collection
scoped_children_collection = sdata_collection_url(resource_collection[:parent], self.id, resource_collection[:url])
builder.__send__(xmlns_qualifier_for(node_name), {"xlmns:sdata:url" => scoped_children_collection}) do |element|
if (expand != :none) || included.include?(node_name.to_s.camelize(:lower))
expand = :none if (expand == :immediate_children)
node_value.each do |item|
element << self.payload(node_name.to_s.singularize.camelize(:lower), item, expand, included, element_precedence, maximum_precedence)
end
end
end
else
builder.__send__(xmlns_qualifier_for(node_name)) do |element|
expand = :none if (expand == :immediate_children)
node_value.each do |item|
element << self.payload(node_name.to_s.singularize, item, expand, included, element_precedence, maximum_precedence, (item.is_a?(Hash) ? item[:resource_collection] : nil))
end
end
end
elsif node_value.is_a?(Hash)
builder.__send__(xmlns_qualifier_for(node_name)) do |element|
expand = :none if (expand == :immediate_children)
node_value.each_pair do |child_node_name, child_node_data|
element << self.payload(child_node_name.to_s.camelize(:lower), child_node_data, expand, included, element_precedence, maximum_precedence, (child_node_data.is_a?(Hash) ? child_node_data[:resource_collection] : nil))
end
end
else
builder.__send__(xmlns_qualifier_for(node_name.to_s.camelize(:lower)), (node_value ? node_value.to_s : {'xlmns:xsi:nil' => "true"}))
end
end

end end
end end
end end
ActiveRecord::Base.extend SData::ActiveRecordMixin # Extension of ActiveRecord removed due to refactoring. Now 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.
10 changes: 5 additions & 5 deletions lib/controller_mixin.rb
Expand Up @@ -29,6 +29,7 @@ def sdata_collection
populate_open_search_for(collection) populate_open_search_for(collection)
build_feed_links_for(collection) build_feed_links_for(collection)
render :xml => collection, :content_type => "application/atom+xml; type=feed" render :xml => collection, :content_type => "application/atom+xml; type=feed"

rescue Exception => e rescue Exception => e
render :xml => ApplicationDiagnosis.new(:exception => e).to_xml(:root) render :xml => ApplicationDiagnosis.new(:exception => e).to_xml(:root)
end end
Expand Down Expand Up @@ -162,10 +163,8 @@ def sdata_scope
if sdata_options[:scoping] if sdata_options[:scoping]
options[:conditions] ||= [] options[:conditions] ||= []
sdata_options[:scoping].each do |scope| sdata_options[:scoping].each do |scope|
scopable = self.send(scope[:object]) options[:conditions][0] = [options[:conditions].to_a[0], scope].compact.join(' and ')
conditions = ["#{scope[:attribute]} = ?", scopable.send(scope[:key])] options[:conditions] << current_user.id.to_s
options[:conditions][0] = [options[:conditions].to_a[0], conditions[0]].compact.join(' and ')
options[:conditions] << conditions[1].to_s
end end
end end


Expand All @@ -181,6 +180,7 @@ def sdata_scope
#if user has hundreds of records but requests first 10, we shouldnt load them all into memory #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 #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 = sdata_options[:model].all(options)
@total_results = results.count @total_results = results.count
paginated_results = results[zero_based_start_index,records_to_return] paginated_results = results[zero_based_start_index,records_to_return]
Expand All @@ -200,4 +200,4 @@ def populate_open_search_for(feed)
end end
end end


ActionController::Base.extend SData::ControllerMixin ActionController::Base.extend SData::ControllerMixin
11 changes: 6 additions & 5 deletions lib/diagnosis.rb
Expand Up @@ -53,7 +53,7 @@ def diagnosis_payload
value = self.send(attribute) unless [:http_status_code].include?(attribute) value = self.send(attribute) unless [:http_status_code].include?(attribute)
if value if value
if value.is_a?(Exception) if value.is_a?(Exception)
node << (XML::Node.new("sdata:stackTrace") << value.backtrace) if !['production', 'staging'].include?(ENV['RAILS_ENV']) node << (XML::Node.new("sdata:stackTrace") << value.backtrace.join("\n") ) if !['production', 'staging'].include?(ENV['RAILS_ENV'])
else else
node << (XML::Node.new("sdata:#{attribute.to_s.camelize(:lower)}") << value) node << (XML::Node.new("sdata:#{attribute.to_s.camelize(:lower)}") << value)
end end
Expand Down Expand Up @@ -139,8 +139,8 @@ module SDataRescue
#TODO: rescue_action_in_public won't work for some reason here. When merging with real Billing Boss app, #TODO: rescue_action_in_public won't work for some reason here. When merging with real Billing Boss app,
#investigate. Need to preserve stack traces for dev env for non-simply requests. #investigate. Need to preserve stack traces for dev env for non-simply requests.
def sdata_global_rescue(exception, request_path) def sdata_global_rescue(exception, request_path)
error_payload = case exception.class.to_s error_payload = case exception.class.to_s.demodulize
when "NoMethodError" when 'NoMethodError'
if request_path.match /^\/sdata\/#{$SDATA_HIERARCHY[0]}\/#{$SDATA_HIERARCHY[1]}\/#{$SDATA_HIERARCHY[2]}\/[A-z]+\/.*/ if request_path.match /^\/sdata\/#{$SDATA_HIERARCHY[0]}\/#{$SDATA_HIERARCHY[1]}\/#{$SDATA_HIERARCHY[2]}\/[A-z]+\/.*/
SData::ApplicationDiagnosis.new(:exception => exception, :http_status_code => '500') SData::ApplicationDiagnosis.new(:exception => exception, :http_status_code => '500')
elsif request_path.match /^\/sdata\/#{$SDATA_HIERARCHY[0]}\/#{$SDATA_HIERARCHY[1]}\/#{$SDATA_HIERARCHY[2]}\/[A-z]+/ elsif request_path.match /^\/sdata\/#{$SDATA_HIERARCHY[0]}\/#{$SDATA_HIERARCHY[1]}\/#{$SDATA_HIERARCHY[2]}\/[A-z]+/
Expand All @@ -158,7 +158,8 @@ def sdata_global_rescue(exception, request_path)
else else
SData::ApplicationNotFound.new(:exception => exception, :http_status_code => '404') SData::ApplicationNotFound.new(:exception => exception, :http_status_code => '404')
end end
#TODO: customize further as needed when 'AccessDeniedException'
SData::ApplicationDiagnosis.new(:exception => exception, :http_status_code => '403')
else else
SData::ApplicationDiagnosis.new(:exception => exception, :http_status_code => '500') SData::ApplicationDiagnosis.new(:exception => exception, :http_status_code => '500')
end end
Expand All @@ -167,4 +168,4 @@ def sdata_global_rescue(exception, request_path)
end end
end end
end end
ActionController::Base.extend SData::ApplicationControllerMixin ActionController::Base.extend SData::ApplicationControllerMixin
111 changes: 111 additions & 0 deletions lib/payload.rb
@@ -0,0 +1,111 @@
class Payload

# FIXME: temporary
# is_sync? should be passed as param to generate, and should be true if feed is a synch feed.
# In this case, ?include will show only links and not embedded data for associations,
# while ?include=$children will be implied
def self.is_sync?
false
end

def self.generate(*params)
node_name, node_value, expand, included, selected, element_precedence, maximum_precedence, resource_collection = *params
expand = :all_children if is_sync?
return "" if element_precedence > maximum_precedence
return "" if self.excluded_in_select?(node_name, selected)
builder = Builder::XmlMarkup.new
if node_value.respond_to?('payload_map')
self.construct_from_sdata_model(*params)
elsif node_value.is_a?(Array)
self.construct_from_array(*params)
elsif node_value.is_a?(Hash)
self.construct_from_hash(*params)
else
self.construct_from_string(*params)
end
end

def self.construct_from_sdata_model(node_name, node_value, expand, included, selected, element_precedence, maximum_precedence, resource_collection=nil)
builder = Builder::XmlMarkup.new
builder.__send__(self.xmlns_qualifier_for(node_name), node_value.resource_header_attributes(node_value, included)) do |element|
if (expand != :none) || included.include?(node_name.to_s.camelize(:lower))
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
child_expand = :none
else
child_expand = expand
end
element << self.generate(child_node_name.to_s.camelize(:lower), child_node_data[:value], child_expand, included, selected, child_node_data[:precedence], maximum_precedence, child_node_data[:resource_collection])
end
end
end
end

def self.construct_from_array(*params)
builder = Builder::XmlMarkup.new
if params[7] #resource_collection
self.construct_from_sdata_array(*params)
else
self.construct_from_non_sdata_array(*params)
end
end

def self.construct_from_sdata_array(node_name, node_value, expand, included, selected, element_precedence, maximum_precedence, resource_collection)
builder = Builder::XmlMarkup.new
scoped_children_collection = self.sdata_collection_url(resource_collection[:parent], resource_collection[:url])
builder.__send__(self.xmlns_qualifier_for(node_name), {"xlmns:sdata:url" => scoped_children_collection}) do |element|
if (expand != :none || included.include?(node_name.to_s.camelize(:lower)))
expand = :immediate_children if (expand == :none && included.include?(node_name.to_s.camelize(:lower))) && !is_sync?
node_value.each do |item|
element << self.generate(item.sdata_node_name, item, expand, included, selected, element_precedence, maximum_precedence, nil)
end
end
end
end

def self.construct_from_non_sdata_array(node_name, node_value, expand, included, selected, element_precedence, maximum_precedence, resource_collection)
builder = Builder::XmlMarkup.new
builder.__send__(xmlns_qualifier_for(node_name)) do |element|
expand = :immediate_children if expand == :none
node_value.each do |item|
element << self.generate(node_name.to_s.singularize, item, expand, included, selected, element_precedence, maximum_precedence, (item.is_a?(Hash) ? item[:resource_collection] : nil))
end
end
end

def self.construct_from_hash(node_name, node_value, expand, included, selected, element_precedence, maximum_precedence, resource_collection)
builder = Builder::XmlMarkup.new
builder.__send__(xmlns_qualifier_for(node_name)) do |element|
expand = :none if (expand == :immediate_children)
node_value.each_pair do |child_node_name, child_node_data|
element << self.generate(child_node_name.to_s.camelize(:lower), child_node_data, expand, included, selected, element_precedence, maximum_precedence, (child_node_data.is_a?(Hash) ? child_node_data[:resource_collection] : nil))
end
end
end

def self.construct_from_string(node_name, node_value, expand, included, selected, element_precedence, maximum_precedence, resource_collection)
builder = Builder::XmlMarkup.new
builder.__send__(self.xmlns_qualifier_for(node_name.to_s.camelize(:lower)), (node_value ? node_value.to_s : {'xlmns:xsi:nil' => "true"}))
end

def self.xmlns_qualifier_for(element)
"xmlns:crmErp:#{element}"
end

def self.sdata_collection_url(parent, child)
$APPLICATION_URL + $SDATA_STORE_PATH + parent.sdata_node_name + "('#{parent.id}')/" + child
end

def self.excluded_in_select?(node_name, selected)
if selected.empty?
return false
elsif selected.include?('_root')
selected.delete '_root'
return false
else
return !selected.include?(node_name.to_s.camelize(:lower))
end
end

end
54 changes: 54 additions & 0 deletions lib/virtual_base.rb
@@ -0,0 +1,54 @@
# Tried using ActiveRecord::BaseWithoutTable, but had problems calling __include__ on it.
# Research if we need features provided by BWT, otherwise use this.

require 'forwardable'

class VirtualBase
extend Forwardable
attr_accessor :baze
def_delegators :baze, :id, :created_at, :updated_at, :save, :update_attributes

def initialize(the_baze, the_type=nil)
self.baze = the_baze

if self.respond_to?('sdata_type') && the_type
self.sdata_type = the_type
end

super()
end

def self.build_for(data, the_type=nil)
if data.is_a? Array
array = []
data.each do |item|
array << self.new(item, the_type) if item
end
array
else
data ? self.new(data, the_type) : nil
end
end

# FIXME: Below methods transfer ActiveRecord methods to the baze. Is there a cleaner way to do this?

def self.find(*params)
self.new(self.baze_class.find(*params))
end

def self.first(*params)
self.new(self.baze_class.first(*params))
end

def self.all(*params)
virtual_results = []
results = self.baze_class.all(*params)
results.each do |result|
virtual_results << self.new(result)
end
virtual_results
end
end


VirtualBase.extend SData::ActiveRecordMixin

0 comments on commit 83f815d

Please sign in to comment.