Merge branch 'master' of git://github.com/bsag/tracks into bsag

This commit is contained in:
Hans de Graaff 2010-03-27 11:22:52 +01:00
commit 8109d9ec7d
257 changed files with 24751 additions and 2042 deletions

3
.gitignore vendored
View file

@ -15,3 +15,6 @@ public/stylesheets/cache
tmp
vendor/plugins/query_trace/
rerun.txt
public/javascripts/jquery-all.js
public/javascripts/tracks.js
public/stylesheets/all.css

View file

@ -276,8 +276,6 @@ class ApplicationController < ActionController::Base
def set_time_zone
Time.zone = current_user.prefs.time_zone if logged_in?
locale = params[:locale] || 'en-US'
I18n.locale = locale
end
def set_zindex_counter

View file

@ -27,13 +27,29 @@ class NotesController < ApplicationController
def create
note = current_user.notes.build
note.attributes = params["new_note"]
if note.save
render :partial => 'notes_summary', :object => note
else
render :text => ''
end
note.attributes = params["note"]
saved = note.save
respond_to do |format|
format.js do
if note.save
render :partial => 'notes_summary', :object => note
else
render :text => ''
end
end
format.xml do
if saved
head :created, :location => note_url(note), :text => "new note with id #{note.id}"
else
render_failure note.errors.full_messages.join(', ')
end
end
format.html do
render :text => 'unexpected request for html rendering'
end
end
end
def destroy

View file

@ -7,7 +7,6 @@ class PreferencesController < ApplicationController
def edit
@page_title = "TRACKS::Edit Preferences"
render :object => prefs
end
def update
@ -20,4 +19,4 @@ class PreferencesController < ApplicationController
end
end
end
end

View file

@ -2,7 +2,7 @@ class RecurringTodosController < ApplicationController
helper :todos, :recurring_todos
append_before_filter :init, :only => [:index, :new, :edit]
append_before_filter :init, :only => [:index, :new, :edit, :create]
append_before_filter :get_recurring_todo_from_param, :only => [:destroy, :toggle_check, :toggle_star, :edit, :update]
def index
@ -243,7 +243,7 @@ class RecurringTodosController < ApplicationController
private
def init
def init
@days_of_week = [ ['Sunday',0], ['Monday',1], ['Tuesday', 2], ['Wednesday',3], ['Thursday',4], ['Friday',5], ['Saturday',6]]
@months_of_year = [
['January',1], ['Februari',2], ['March', 3], ['April',4], ['May',5], ['June',6],

View file

@ -6,9 +6,9 @@ class TodosController < ApplicationController
prepend_before_filter :login_or_feed_token_required, :only => [:index, :calendar]
append_before_filter :init, :except => [ :destroy, :completed,
:completed_archive, :check_deferred, :toggle_check, :toggle_star,
:edit, :update, :create, :calendar, :auto_complete_for_tag, :auto_complete_for_predecessor, :remove_predecessor, :add_predecessor]
:edit, :update, :create, :calendar, :auto_complete_for_predecessor, :remove_predecessor, :add_predecessor]
append_before_filter :get_todo_from_params, :only => [ :edit, :toggle_check, :toggle_star, :show, :update, :destroy, :remove_predecessor]
protect_from_forgery :except => [:auto_complete_for_tag, :auto_complete_for_predecessor]
protect_from_forgery :except => [:auto_complete_for_predecessor]
def index
current_user.deferred_todos.find_and_activate_ready
@ -77,7 +77,7 @@ class TodosController < ApplicationController
@todo.tags.reload
end
unless (@aved == false)
unless (@saved == false)
unless @todo.uncompleted_predecessors.empty? || @todo.state == 'project_hidden'
@todo.state = 'pending'
end
@ -140,8 +140,8 @@ class TodosController < ApplicationController
def add_predecessor
@source_view = params['_source_view'] || 'todo'
@predecessor = Todo.find(params['predecessor'])
@todo = Todo.find(params['successor'])
@predecessor = current_user.todos.find(params['predecessor'])
@todo = current_user.todos.find(params['successor'])
@original_state = @todo.state
# Add predecessor
@todo.add_predecessor(@predecessor)
@ -154,7 +154,7 @@ class TodosController < ApplicationController
def remove_predecessor
@source_view = params['_source_view'] || 'todo'
@predecessor = Todo.find(params['predecessor'])
@predecessor = current_user.todos.find(params['predecessor'])
@successor = @todo
@removed = @successor.remove_predecessor(@predecessor)
respond_to do |format|
@ -590,14 +590,6 @@ class TodosController < ApplicationController
}
end
end
def auto_complete_for_tag
@items = Tag.find(:all,
:conditions => [ "name LIKE ?", '%' + params['tag_list'] + '%' ],
:order => "name ASC",
:limit => 10)
render :inline => "<%= auto_complete_result(@items, :name) %>"
end
def auto_complete_for_predecessor
unless params['id'].nil?
@ -640,8 +632,8 @@ class TodosController < ApplicationController
end
def convert_to_project
@todo = Todo.find(params[:id])
@project = Project.new(:name => @todo.description, :description => @todo.notes,
@todo = current_user.todos.find(params[:id])
@project = current_user.projects.new(:name => @todo.description, :description => @todo.notes,
:default_context => @todo.context)
@todo.destroy
@project.save!

View file

@ -24,14 +24,12 @@ module TodosHelper
:class => "icon star_item", :title => "star the action '#{@todo.description}'")
end
def remote_edit_menu_item(parameters, todo)
return link_to_remote(
image_tag("edit_off.png", :mouseover => "edit_on.png", :alt => "Edit", :align => "absmiddle", :id => 'edit_icon_todo_'+todo.id.to_s)+" Edit",
:url => {:controller => 'todos', :action => 'edit', :id => todo.id},
:method => 'get',
:with => "'#{parameters}'",
:before => todo_start_waiting_js(todo),
:complete => todo_stop_waiting_js(todo))
def remote_edit_button
link_to(
image_tag("blank.png", :alt => "Edit", :align => "absmiddle", :id => 'edit_icon_todo_'+@todo.id.to_s, :class => 'edit_item'),
{:controller => 'todos', :action => 'edit', :id => @todo.id},
:class => "icon edit_item",
:title => "Edit the action '#{@todo.description}'")
end
def remote_delete_menu_item(parameters, todo)
@ -70,7 +68,7 @@ module TodosHelper
:_source_view => (@source_view.underscore.gsub(/\s+/,'_') rescue "")}
url[:_tag_name] = @tag_name if @source_view == 'tag'
return link_to("Promote to project", url)
return link_to(image_tag("to_project_off.png", :align => "absmiddle")+" Make project", url)
end
def todo_start_waiting_js(todo)
@ -121,8 +119,10 @@ module TodosHelper
def grip_span
unless @todo.completed?
image_tag('grip.png', :width => '7', :height => '16', :border => '0',
:title => 'Drag onto another action to make it depend on that action',
:class => 'grip')
:title => 'Drag onto another action to make it depend on that action',
:class => 'grip') +
image_tag('blank.png', :width => 16, :height => 16, :border => 0,
:title => "Drop an action to make it depend on this action", :class => 'successor_target')
end
end

View file

@ -26,7 +26,8 @@
:with => "'_source_view=#{@source_view}'",
:before => "$('#{dom_id(context)}').block({message:null});",
:complete => "$('#{dom_id(context)}').unblock();",
:confirm => "Are you sure that you want to delete the context '#{context.name}'?"
:confirm => "Are you sure that you want to delete the context '#{context.name}'?",
:html => { :id => dom_id(context, 'delete') }
) %>
<%= link_to_remote(
image_tag( "blank.png", :title => "Edit context", :class=>"edit_item"),

View file

@ -1,7 +1,7 @@
<%
context = text_context
todos_in_context = todos.select { |t| t.context_id == context.id }
todos_in_context = not_done_todos.select { |t| t.context_id == context.id }
if todos_in_context.length > 0
-%> <%= context.name.upcase %>:
<%= render :partial => "todos/text_todo", :collection => todos_in_context -%>
<% end -%>
<% end -%>

View file

@ -1,5 +1,3 @@
page['context_name_in_place_editor'].replace_html @context.name
page['default_context_name_id'].value = @context.name
page['todo_context_name'].value = @context.name

View file

@ -73,12 +73,6 @@
<%= options_from_collection_for_select(@active_contexts, "id", "name", @active_contexts.first.id) unless @active_projects.empty?-%>
<%= options_from_collection_for_select(@hidden_contexts, "id", "name") -%>
</select>
<%= observe_field "feed-contexts", :update => "feeds-for-context",
:with => 'context_id',
:url => { :controller => "feedlist", :action => "get_feeds_for_context" },
:before => "$('feeds-for-context').startWaiting()",
:complete => "$('feeds-for-context').stopWaiting()"
-%>
</li>
<li>Step 2 - Select the feed for this context
<div id="feedicons-context">
@ -101,12 +95,6 @@
<%= options_from_collection_for_select(@hidden_projects, "id", "name") -%>
<%= options_from_collection_for_select(@completed_projects, "id", "name") -%>
</select>
<%= observe_field "feed-projects", :update => "feeds-for-project",
:with => 'project_id',
:url => { :controller => "feedlist", :action => "get_feeds_for_project" },
:before => "$('feeds-for-project').startWaiting()",
:complete => "$('feeds-for-project').stopWaiting()"
-%>
</li>
<li>Step 2 - Select the feed for this project
<div id="feedicons-project">

View file

@ -27,12 +27,6 @@
<% if has_contexts -%>
<ol>
<li>Choose the context you want to add actions to: <select name="applescript1-contexts" id="applescript1-contexts"><%= options_from_collection_for_select(current_user.contexts, "id", "name", current_user.contexts.first.id) %></select>
<%= observe_field "applescript1-contexts", :update => "applescript1",
:with => 'context_id',
:url => { :controller => "integrations", :action => "get_applescript1" },
:before => "$('applescript1').startWaiting()",
:complete => "$('applescript1').stopWaiting()"
%>
</li>
<li>Copy the Applescript below to the clipboard.<br />
@ -52,12 +46,6 @@
<% if has_contexts -%>
<ol>
<li>Choose the context you want to add actions to: <select name="applescript2-contexts" id="applescript2-contexts"><%= options_from_collection_for_select(current_user.contexts, "id", "name", current_user.contexts.first.id) %></select>
<%= observe_field "applescript2-contexts", :update => "applescript2",
:with => 'context_id',
:url => { :controller => "integrations", :action => "get_applescript2" },
:before => "$('applescript2').startWaiting()",
:complete => "$('applescript2').stopWaiting()"
%>
</li>
<li>Copy the Applescript below to the clipboard.<br />
@ -79,12 +67,6 @@
<% if has_contexts -%>
<ol>
<li>Choose the context you want to add actions to: <select name="quicksilver-contexts" id="quicksilver-contexts"><%= options_from_collection_for_select(current_user.contexts, "id", "name", current_user.contexts.first.id) %></select>
<%= observe_field "quicksilver-contexts", :update => "quicksilver",
:with => 'context_id',
:url => { :controller => "integrations", :action => "get_quicksilver_applescript" },
:before => "$('quicksilver').startWaiting()",
:complete => "$('quicksilver').stopWaiting()"
%>
</li>
<li>Copy the Applescript below to the clipboard.<br />

View file

@ -2,37 +2,27 @@
<html xmlns="http://www.w3.org/1999/xhtml">
<head>
<meta http-equiv="Content-Type" content="text/html; charset=UTF-8" />
<% if @prefs.refresh != 0 -%>
<meta http-equiv="Refresh" content="<%= @prefs["refresh"].to_i*60 %>;url=<%= request.request_uri %>">
<% end -%>
<% bundle :name => "tracks_css" do %>
<%= stylesheet_link_tag *%w[ standard superfish niftyCorners jquery-ui jquery.autocomplete] %>
<% end %>
<%= stylesheet_link_tag 'standard','superfish','niftyCorners','jquery-ui',
'jquery.autocomplete', :cache => true %>
<%= stylesheet_link_tag "print", :media => "print" %>
<% bundle :name => "jquery" do %>
<%= javascript_include_tag 'jquery' %>
<%= javascript_include_tag 'jquery-ui' %>
<%= javascript_include_tag 'jquery.cookie' %>
<%= javascript_include_tag 'jquery.blockUI' %>
<%= javascript_include_tag 'jquery.jeditable' %>
<%= javascript_include_tag 'jquery.autocomplete' %>
<% end %>
<% bundle :name => "tracks_js" do %>
<%= javascript_include_tag *%w[
hoverIntent superfish application
accesskey-hints niftycube flashobject ] %>
<% end %>
<%= javascript_include_tag 'jquery','jquery-ui','jquery.cookie',
'jquery.blockUI','jquery.jeditable','jquery.autocomplete', :cache => 'jquery-all' %>
<%= javascript_include_tag 'hoverIntent','superfish','application',
'accesskey-hints','niftycube','flashobject', :cache => 'tracks' %>
<%= javascript_tag "var AUTH_TOKEN = #{form_authenticity_token.inspect};" if protect_against_forgery? %>
<%= javascript_tag "var SOURCE_VIEW = '#{@source_view}';" %>
<%= javascript_tag "var TAG_NAME = '#{@tag_name}';" if @tag_name %>
<script type="text/javascript">
<% if defined? context_names_for_autocomplete -%>
var contextNames = <%= context_names_for_autocomplete %>;
var projectNames = <%= project_names_for_autocomplete %>;
var defaultContexts = <%= default_contexts_for_autocomplete %>;
var defaultTags = <%= default_tags_for_autocomplete %>;
var tagNames = <%= tag_names_for_autocomplete %>;
var contextNames = <%= context_names_for_autocomplete rescue '[]' %>;
var projectNames = <%= project_names_for_autocomplete rescue '[]' %>;
var defaultContexts = <%= default_contexts_for_autocomplete rescue '{}' %>;
var defaultTags = <%= default_tags_for_autocomplete rescue '{}' %>;
var tagNames = <%= tag_names_for_autocomplete rescue '[]' %>;
var dateFormat = '<%= date_format_for_date_picker %>';
var weekStart = '<%= current_user.prefs.week_starts %>';
function relative_to_root(path) { return '<%= root_url %>'+path; };
<% if @prefs.refresh != 0 -%>
setup_auto_refresh(<%= @prefs["refresh"].to_i*60000 %>);
<% end -%>
</script>
<link rel="shortcut icon" href="<%= url_for(:controller => 'favicon.ico') %>" />

View file

@ -6,17 +6,12 @@
<div class="note_footer">
<%= link_to_remote(
image_tag("blank.png",
:title =>"Delete this note",
:class=>"delete_item",
:id => "delete_note_"+note.id.to_s),
{:update => dom_id(note),
:loading => visual_effect(:fade, dom_id(note, 'container')),
:complete => "$('#{dom_id(note, 'container')}').remove();",
:url => note_path(note),
image_tag("blank.png", :title =>"Delete this note", :class=>"delete_item", :id => "delete_note_"+note.id.to_s),
:url => note_path(note),
:html => {:class => 'delete_note', :title => "delete note"},
:method => :delete,
:confirm => "Are you sure that you want to delete the note \'#{note.id.to_s}\'?" },
{ :class => 'delete_note' }) -%>&nbsp;
:confirm => "Are you sure that you want to delete the note \'#{note.id.to_s}\'?",
:before => visual_effect(:fade, dom_id(note, 'container'))) -%>&nbsp;
<%= link_to_function(image_tag( "blank.png", :title => "Edit item", :class=>"edit_item"),
"$('##{dom_id(note)}').toggle(); $('##{dom_id(note, 'edit')}').show(); $('##{dom_id(note, 'edit_form')} textarea').focus();" ) + " | " %>
<%= link_to("In: " + note.project.name, project_path(note.project), :class=>"footer_link" ) %>&nbsp;|&nbsp;
@ -38,3 +33,4 @@
</div>
</div>
<% note = nil -%>

View file

@ -1,5 +1,9 @@
<div class="container">
<h2 id="project_name"><% if collapsible %><a href="#" class="container_toggle" id="toggle_p<%= project.id %>"><%= image_tag("collapse.png") %></a><% end %><%= project.name -%></h2>
<h2 id="project_name">
<% if collapsible -%>
<a href="#" class="container_toggle" id="toggle_p<%= project.id %>"><%= image_tag("collapse.png") %></a>
<% end -%>
<%= project.name -%></h2>
<div id="<%= dom_id(project, "container")%>">
<%= render :partial => "projects/project_settings", :locals => { :project => project, :collapsible => collapsible } %>
</div>

View file

@ -20,11 +20,12 @@
:method => 'get',
:with => "'_source_view=#{@source_view}'",
:before => "$('#{dom_id(project)}').block({message: null});",
:complete => "$('#{dom_id(project)}').unblock();enable_rich_interaction();"
:complete => "$('#{dom_id(project)}').unblock();enable_rich_interaction();",
:html => {:id => "link_edit_#{dom_id(project)}"}
) %>
</div>
<% unless project.description.blank? -%>
<div class="project_description"><%= sanitize(project.description) %></div>
<div class="project_description"><%= format_note(project.description) %></div>
<% end -%>
</div>
<div id="<%= dom_id(project, 'edit') %>" class="edit-form" style="display:none;">

View file

@ -28,8 +28,8 @@
:position => "bottom",
:complete => "$('#notes').effect('highlight', 1000);$('#empty-n').hide();$('#new-note form').clearForm();",
:html => {:id=>'form-new-note', :class => 'inline-form'} do %>
<%= hidden_field( "new_note", "project_id", "value" => "#{@project.id}" ) %>
<%= text_area( "new_note", "body", "cols" => 50, "rows" => 3, "tabindex" => 1 ) %>
<%= hidden_field( "note", "project_id", "value" => "#{@project.id}" ) %>
<%= text_area( "note", "body", "cols" => 50, "rows" => 3, "tabindex" => 1 ) %>
<br /><br />
<input type="submit" value="Add note" name="add-new-note" tabindex="2" />
<% end -%>

View file

@ -3,6 +3,7 @@ page['todo_project_name'].value = @project.name
# renew project auto complete array
page << "var projectNames = #{project_names_for_autocomplete};"
page << "enable_rich_interaction();"
status_message = "Name of project was changed"
page.notify :notice, status_message, 5.0

View file

@ -2,8 +2,7 @@
<% form_remote_tag(
:url => recurring_todos_path, :method => :post,
:html=> { :id=>'recurring-todo-form-new-action', :name=>'recurring_todo', :class => 'inline-form' },
:before => "$('#recurring_todo_new_action_submit').block({message: null})",
:complete => "$('#recurring_todo_new_action_submit').unblock();$('#recurring-todo-form-new-action').clearForm();") do
:before => "$('#recurring_todo_new_action_submit').block({message: null})") do
-%>
<div id="new_status"><%= error_messages_for("item", :object_name => 'action') %></div>
@ -18,7 +17,7 @@
<div class="page_name_auto_complete" id="project_list" style="display:none"></div>
<label for="recurring_todo_context_name">Context</label>
<input id="recurring_todo_context_name" name="context_name" autocomplete="off" tabindex="4" size="30" type="text" value="" />
<input id="recurring_todo_context_name" name="context_name" autocomplete="off" tabindex="4" size="30" type="text" value="<%= current_user.contexts.first.name unless current_user.contexts.first.nil?%>" />
<div class="page_name_auto_complete" id="context_list" style="display:none"></div>
<label for="tag_list">Tags (separate with commas)</label>
<%= text_field_tag "tag_list", nil, :size => 30, :tabindex => 5 -%>

View file

@ -4,8 +4,7 @@ page.replace_html 'new_status', "#{error_messages_for('recurring_todo')}"
page.notify :notice, @message, 5.0
if @recurring_saved
# reset form
page << "TracksForm.hide_all_recurring(); $('#recurring_daily').show();"
page << "$('#recurring_todo_new_action_submit').unblock();$('#recurring-todo-form-new-action').clearForm();"
page['new-recurring-todo'].replace_html :partial => 'recurring_todo_form'
# hide overlayed edit form
page << "TracksForm.toggle_overlay();"
# insert new recurring todo

View file

@ -22,7 +22,7 @@
<div id="status"><%= error_messages_for("item", :object_name => 'action') %></div>
<label for="todo_description">Description</label>
<%= text_field( "todo", "description", "size" => 30, "tabindex" => 1, "maxlength" => 100, "autocomplete" => "off") %>
<%= text_field( "todo", "description", "size" => 30, "tabindex" => 1, "maxlength" => 100, "autocomplete" => "off", :autofocus => 1) %>
<label for="todo_notes">Notes</label>
<%= text_area( "todo", "notes", "cols" => 29, "rows" => 6, "tabindex" => 2) %>

View file

@ -13,22 +13,15 @@
<div class="project_input">
<label for="<%= dom_id(@todo, 'project_name') %>">Project</label>
<input id="<%= dom_id(@todo, 'project_name') %>" name="project_name" autocomplete="off" tabindex="10" size="30" type="text" value="<%= @todo.project.nil? ? 'None' : @todo.project.name.gsub(/"/,"&quot;") %>" />
<div class="page_name_auto_complete" id="<%= dom_id(@todo, 'project_list') %>" style="display:none"></div>
</div>
<div class="context_input">
<label for="<%= dom_id(@todo, 'context_name') %>">Context</label>
<input id="<%= dom_id(@todo, 'context_name') %>" name="context_name" autocomplete="off" tabindex="11" size="30" type="text" value="<%= @todo.context.name %>" />
<div class="page_name_auto_complete" id="<%= dom_id(@todo, 'context_list') %>" style="display:none"></div>
</div>
<label class="tag_list_label" for="<%= dom_id(@todo, 'tag_list') %>">Tags (separate with commas)</label>
<%= text_field_tag 'tag_list', tag_list_text, :id => dom_id(@todo, 'tag_list'), :size => 30, :tabindex => 12 %>
<%= content_tag("div", "", :id => dom_id(@todo, 'tag_list')+"_auto_complete", :class => "auto_complete") %>
<%= auto_complete_field dom_id(@todo, 'tag_list'), {
:url => {:controller => 'todos', :action => 'auto_complete_for_tag'},
:tokens => [',']
} %>
<div class="due_input">
<label for="<%= dom_id(@todo, 'due_label') %>">Due</label>
@ -48,11 +41,6 @@
<label class="predecessor_list_label" for="<%= dom_id(@todo, 'predecessor_list') %>">Depends on (separate with commas)</label>
<%= text_field_tag 'predecessor_list', predecessor_list_text, :id => dom_id(@todo, 'predecessor_list'), :size => 30, :tabindex => 15 %>
<%= content_tag("div", "", :id => dom_id(@todo, 'predecessor_list')+"_auto_complete", :class => "auto_complete") %>
<%= auto_complete_field dom_id(@todo, 'predecessor_list'), {
:url => {:controller => 'todos', :action => 'auto_complete_for_predecessor', :id => @todo.id},
:tokens => [',']
} %>
<% if controller.controller_name == "project" || @todo.deferred? -%>
<input type="hidden" name="on_project_page" value="true" />

View file

@ -8,9 +8,9 @@ if parent_container_type == 'show_mobile' -%>
<p><label for="todo_done">Done?</label>&nbsp;<%= check_box_tag("done", 1, @todo && @todo.completed?, "tabindex" => 1) %></p>
<% end -%>
<h2><label for="todo_description">Description</label></h2>
<%= text_field( "todo", "description", "tabindex" => 2, "maxlength" => 100) %>
<%= text_field( "todo", "description", "tabindex" => 2, "maxlength" => 100, "size" => 50) %>
<h2><label for="todo_notes">Notes</label></h2>
<%= text_area( "todo", "notes", "cols" => 30, "rows" => 2, "tabindex" => 3) %>
<%= text_area( "todo", "notes", "cols" => 40, "rows" => 3, "tabindex" => 3) %>
<h2><label for="todo_context_id">Context</label></h2>
<%= unless @mobile_from_context
collection_select( "todo", "context_id", @contexts, "id", "name", {}, {"tabindex" => 4} )
@ -31,7 +31,7 @@ else
{"id" => :todo_project_id, :tabindex => 5} )
end %>
<h2><label for="tag_list">Tags (separate with commas)</label></h2>
<%= text_field_tag "tag_list", @tag_list_text, :size => 30, :tabindex => 6 %>
<%= text_field_tag "tag_list", @tag_list_text, :size => 50, :tabindex => 6 %>
<h2><label for="todo_due">Due</label></h2>
<%= date_select("todo", "due", {:order => [:day, :month, :year],
:start_year => this_year, :include_blank => '--'}, :tabindex => 7) %>

View file

@ -11,12 +11,12 @@ parameters += "&_tag_name=#{@tag_name}" if @source_view == 'tag'
<div id="<%= dom_id(todo, 'line') %>" class="item-show">
<%= remote_star_icon %>
<%= remote_toggle_checkbox unless source_view_is :deferred %>
<% unless suppress_edit_button %>
<%= remote_edit_button %>
<% end %>
<ul class="sf-menu sf-item-menu">
<li style="z-index:<%=@z_index_counter%>"><%= image_tag "downarrow.png", :alt=> "" %>
<ul id="ul<%= dom_id(todo) %>">
<% unless suppress_edit_button %>
<li><%= remote_edit_menu_item(parameters, todo) %></li>
<% end %>
<li><%= remote_delete_menu_item(parameters, todo) %></li>
<% unless todo.completed? || todo.deferred? %>
<li><%= remote_defer_menu_item(1, todo) %></li>

View file

@ -9,6 +9,14 @@ if @saved
# regenerate predecessor to add arrow
page[@predecessor].replace_html :partial => 'todos/todo', :locals => { :todo => @predecessor, :parent_container_type => parent_container_type }
# regenerate predecessor family
parents = @predecessor.predecessors
until parents.empty?
parent = parents.pop
parents += parent.predecessors
page[parent].replace_html :partial => 'todos/todo', :locals => { :todo => parent, :parent_container_type => parent_container_type }
end
# show in tickler box in project view
if source_view_is_one_of :project, :tag
page['tickler-empty-nd'].hide

View file

@ -52,13 +52,6 @@
</div><!-- End of display_box -->
<div class="input_box" id="input_box">
<% # hack for #860 Need to refactor this and use another dom element to bind projectDefauiltContextsMap to -%>
<input type="hidden" id="todo_context_name" value="hidden">
<script type="text/javascript">
var contextNames = <%= context_names_for_autocomplete %>;
var projectNames = <%= project_names_for_autocomplete %>;
$('todo_context_name').projectDefaultContextsMap = eval('(' + <%= @default_project_context_name_map %> + ')');
</script>
<p><%= link_to('<span class="feed">iCal</span>', {:format => 'ics', :token => current_user.token}, :title => "iCal feed" ) %>
- Get this calendar in iCal format</p>
</div>

View file

@ -2,3 +2,4 @@ page[dom_id(@todo, 'form')].find('.placeholder').show().replace_html :partial =>
page[dom_id(@todo, 'edit')].show
page[dom_id(@todo, 'line')].hide
page[dom_id(@todo, 'form')].find('input#todo_description').show().focus
page << "enable_rich_interaction();"

View file

@ -1 +1 @@
<%= render :partial => "contexts/text_context", :collection => @contexts, :locals => { :todos => @todos } %>
<%= render :partial => "contexts/text_context", :collection => @contexts, :locals => { :todos => @todos, :not_done_todos => @not_done_todos } %>

View file

@ -5,6 +5,14 @@ if @removed
# replace old predecessor with one without the successor
page.replace dom_id(@predecessor), :partial => 'todos/todo', :locals => {
:todo => @predecessor, :parent_container_type => parent_container_type }
# regenerate predecessor family
parents = @predecessor.predecessors
until parents.empty?
parent = parents.pop
parents += parent.predecessors
page[parent].replace_html :partial => 'todos/todo', :locals => { :todo => parent, :parent_container_type => parent_container_type }
end
# update display if pending->active
if @successor.active?

View file

@ -0,0 +1,179 @@
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
<!-- Created with Inkscape (http://www.inkscape.org/) -->
<svg
xmlns:dc="http://purl.org/dc/elements/1.1/"
xmlns:cc="http://creativecommons.org/ns#"
xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#"
xmlns:svg="http://www.w3.org/2000/svg"
xmlns="http://www.w3.org/2000/svg"
xmlns:xlink="http://www.w3.org/1999/xlink"
xmlns:sodipodi="http://sodipodi.sourceforge.net/DTD/sodipodi-0.dtd"
xmlns:inkscape="http://www.inkscape.org/namespaces/inkscape"
width="744.09448819"
height="1052.3622047"
id="svg2"
version="1.1"
inkscape:version="0.47pre4 r22446"
sodipodi:docname="predecessor.svg"
inkscape:export-filename="/Users/epall/code/tracks/public/images/add_successor_on.png"
inkscape:export-xdpi="9.6245861"
inkscape:export-ydpi="9.6245861">
<defs
id="defs4">
<linearGradient
id="linearGradient5258">
<stop
style="stop-color:#28343c;stop-opacity:1;"
offset="0"
id="stop5260" />
<stop
style="stop-color:#0096ff;stop-opacity:1;"
offset="1"
id="stop5262" />
</linearGradient>
<marker
inkscape:stockid="TriangleOutM"
orient="auto"
refY="0.0"
refX="0.0"
id="TriangleOutM"
style="overflow:visible">
<path
id="path3768"
d="M 5.77,0.0 L -2.88,5.0 L -2.88,-5.0 L 5.77,0.0 z "
style="fill-rule:evenodd;stroke:#000000;stroke-width:1.0pt;marker-start:none"
transform="scale(0.4)" />
</marker>
<marker
inkscape:stockid="TriangleOutS"
orient="auto"
refY="0.0"
refX="0.0"
id="TriangleOutS"
style="overflow:visible">
<path
id="path3771"
d="M 5.77,0.0 L -2.88,5.0 L -2.88,-5.0 L 5.77,0.0 z "
style="fill-rule:evenodd;stroke:#000000;stroke-width:1.0pt;marker-start:none"
transform="scale(0.2)" />
</marker>
<marker
inkscape:stockid="Arrow2Mend"
orient="auto"
refY="0.0"
refX="0.0"
id="Arrow2Mend"
style="overflow:visible;">
<path
id="path3649"
style="font-size:12.0;fill-rule:evenodd;stroke-width:0.62500000;stroke-linejoin:round;"
d="M 8.7185878,4.0337352 L -2.2072895,0.016013256 L 8.7185884,-4.0017078 C 6.9730900,-1.6296469 6.9831476,1.6157441 8.7185878,4.0337352 z "
transform="scale(0.6) rotate(180) translate(0,0)" />
</marker>
<marker
inkscape:stockid="Arrow1Mend"
orient="auto"
refY="0.0"
refX="0.0"
id="Arrow1Mend"
style="overflow:visible;">
<path
id="path3631"
d="M 0.0,0.0 L 5.0,-5.0 L -12.5,0.0 L 5.0,5.0 L 0.0,0.0 z "
style="fill-rule:evenodd;stroke:#000000;stroke-width:1.0pt;marker-start:none;"
transform="scale(0.4) rotate(180) translate(10,0)" />
</marker>
<marker
inkscape:stockid="TriangleOutL"
orient="auto"
refY="0.0"
refX="0.0"
id="TriangleOutL"
style="overflow:visible">
<path
id="path3765"
d="M 5.77,0.0 L -2.88,5.0 L -2.88,-5.0 L 5.77,0.0 z "
style="fill-rule:evenodd;stroke:#000000;stroke-width:1.0pt;marker-start:none"
transform="scale(0.8)" />
</marker>
<inkscape:path-effect
effect="spiro"
id="path-effect2826"
is_visible="true" />
<inkscape:path-effect
effect="spiro"
id="path-effect2822"
is_visible="true" />
<inkscape:perspective
sodipodi:type="inkscape:persp3d"
inkscape:vp_x="29.11825 : 616.44767 : 1"
inkscape:vp_y="0 : 1000 : 0"
inkscape:vp_z="744.09448 : 526.18109 : 1"
inkscape:persp3d-origin="372.04724 : 350.78739 : 1"
id="perspective10" />
<linearGradient
inkscape:collect="always"
xlink:href="#linearGradient5258"
id="linearGradient5264"
x1="142.18495"
y1="261.65085"
x2="142.54144"
y2="174.1613"
gradientUnits="userSpaceOnUse" />
<filter
inkscape:collect="always"
id="filter5282">
<feGaussianBlur
inkscape:collect="always"
stdDeviation="0.63631671"
id="feGaussianBlur5284" />
</filter>
</defs>
<sodipodi:namedview
id="base"
pagecolor="#ffffff"
bordercolor="#666666"
borderopacity="1.0"
inkscape:pageopacity="0.0"
inkscape:pageshadow="2"
inkscape:zoom="1.3579107"
inkscape:cx="109.12131"
inkscape:cy="846.66442"
inkscape:document-units="px"
inkscape:current-layer="g5238"
showgrid="false"
inkscape:window-width="675"
inkscape:window-height="547"
inkscape:window-x="0"
inkscape:window-y="0"
inkscape:window-maximized="0" />
<metadata
id="metadata7">
<rdf:RDF>
<cc:Work
rdf:about="">
<dc:format>image/svg+xml</dc:format>
<dc:type
rdf:resource="http://purl.org/dc/dcmitype/StillImage" />
<dc:title></dc:title>
</cc:Work>
</rdf:RDF>
</metadata>
<g
inkscape:label="Layer 1"
inkscape:groupmode="layer"
id="layer1">
<g
id="g5238"
transform="translate(-23.335007,-4.8042662)"
style="fill:none;stroke:#ff0000;stroke-width:0.7;stroke-miterlimit:4;stroke-dasharray:none">
<path
id="path5244"
style="fill:url(#linearGradient5264);fill-opacity:1;stroke:#3a3a3a;stroke-width:4;stroke-miterlimit:4;stroke-opacity:1;stroke-dasharray:none;marker-start:none;filter:url(#filter5282)"
d="m 124.05478,198.95569 25.59428,-0.003 -38.08971,39.75365 -36.022918,-39.75365 23.518346,0.003 0,-11.2623 c 0,-11.56985 3.814362,-23.72799 12.843752,-32.8125 9.02939,-9.08451 22.71756,-14.28125 39.34375,-14.28125 15.89659,0 28.81237,4.90636 36.875,13.9375 8.06263,9.03114 10.40625,20.50418 10.40625,30.9375 l 0,97.6875 -25,0 0,-97.6875 c 0,-6.68688 -1.49995,-11.41088 -4.0625,-14.28125 -2.56255,-2.87037 -7.00817,-5.59375 -18.21875,-5.59375 -11.69655,0 -17.86524,3.15497 -21.59375,6.90625 -3.72851,3.75128 -5.59375,8.92322 -5.59375,15.1875 l 0,11.2623 z"
sodipodi:nodetypes="ccccccscsccccssscc"
transform="matrix(-1,0,0,1,274.05996,0)" />
</g>
</g>
</svg>

After

Width:  |  Height:  |  Size: 6.1 KiB

175
artwork/predecessor.svg Normal file
View file

@ -0,0 +1,175 @@
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
<!-- Created with Inkscape (http://www.inkscape.org/) -->
<svg
xmlns:dc="http://purl.org/dc/elements/1.1/"
xmlns:cc="http://creativecommons.org/ns#"
xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#"
xmlns:svg="http://www.w3.org/2000/svg"
xmlns="http://www.w3.org/2000/svg"
xmlns:xlink="http://www.w3.org/1999/xlink"
xmlns:sodipodi="http://sodipodi.sourceforge.net/DTD/sodipodi-0.dtd"
xmlns:inkscape="http://www.inkscape.org/namespaces/inkscape"
width="744.09448819"
height="1052.3622047"
id="svg2"
version="1.1"
inkscape:version="0.47pre4 r22446"
sodipodi:docname="predecessor.svg">
<defs
id="defs4">
<linearGradient
id="linearGradient5258">
<stop
style="stop-color:#000000;stop-opacity:1;"
offset="0"
id="stop5260" />
<stop
style="stop-color:#ececec;stop-opacity:1;"
offset="1"
id="stop5262" />
</linearGradient>
<marker
inkscape:stockid="TriangleOutM"
orient="auto"
refY="0.0"
refX="0.0"
id="TriangleOutM"
style="overflow:visible">
<path
id="path3768"
d="M 5.77,0.0 L -2.88,5.0 L -2.88,-5.0 L 5.77,0.0 z "
style="fill-rule:evenodd;stroke:#000000;stroke-width:1.0pt;marker-start:none"
transform="scale(0.4)" />
</marker>
<marker
inkscape:stockid="TriangleOutS"
orient="auto"
refY="0.0"
refX="0.0"
id="TriangleOutS"
style="overflow:visible">
<path
id="path3771"
d="M 5.77,0.0 L -2.88,5.0 L -2.88,-5.0 L 5.77,0.0 z "
style="fill-rule:evenodd;stroke:#000000;stroke-width:1.0pt;marker-start:none"
transform="scale(0.2)" />
</marker>
<marker
inkscape:stockid="Arrow2Mend"
orient="auto"
refY="0.0"
refX="0.0"
id="Arrow2Mend"
style="overflow:visible;">
<path
id="path3649"
style="font-size:12.0;fill-rule:evenodd;stroke-width:0.62500000;stroke-linejoin:round;"
d="M 8.7185878,4.0337352 L -2.2072895,0.016013256 L 8.7185884,-4.0017078 C 6.9730900,-1.6296469 6.9831476,1.6157441 8.7185878,4.0337352 z "
transform="scale(0.6) rotate(180) translate(0,0)" />
</marker>
<marker
inkscape:stockid="Arrow1Mend"
orient="auto"
refY="0.0"
refX="0.0"
id="Arrow1Mend"
style="overflow:visible;">
<path
id="path3631"
d="M 0.0,0.0 L 5.0,-5.0 L -12.5,0.0 L 5.0,5.0 L 0.0,0.0 z "
style="fill-rule:evenodd;stroke:#000000;stroke-width:1.0pt;marker-start:none;"
transform="scale(0.4) rotate(180) translate(10,0)" />
</marker>
<marker
inkscape:stockid="TriangleOutL"
orient="auto"
refY="0.0"
refX="0.0"
id="TriangleOutL"
style="overflow:visible">
<path
id="path3765"
d="M 5.77,0.0 L -2.88,5.0 L -2.88,-5.0 L 5.77,0.0 z "
style="fill-rule:evenodd;stroke:#000000;stroke-width:1.0pt;marker-start:none"
transform="scale(0.8)" />
</marker>
<inkscape:path-effect
effect="spiro"
id="path-effect2826"
is_visible="true" />
<inkscape:path-effect
effect="spiro"
id="path-effect2822"
is_visible="true" />
<inkscape:perspective
sodipodi:type="inkscape:persp3d"
inkscape:vp_x="29.11825 : 616.44767 : 1"
inkscape:vp_y="0 : 1000 : 0"
inkscape:vp_z="744.09448 : 526.18109 : 1"
inkscape:persp3d-origin="372.04724 : 350.78739 : 1"
id="perspective10" />
<linearGradient
inkscape:collect="always"
xlink:href="#linearGradient5258"
id="linearGradient5264"
x1="142.54144"
y1="285.28931"
x2="142.54144"
y2="174.1613"
gradientUnits="userSpaceOnUse" />
<filter
inkscape:collect="always"
id="filter5282">
<feGaussianBlur
inkscape:collect="always"
stdDeviation="0.63631671"
id="feGaussianBlur5284" />
</filter>
</defs>
<sodipodi:namedview
id="base"
pagecolor="#ffffff"
bordercolor="#666666"
borderopacity="1.0"
inkscape:pageopacity="0.0"
inkscape:pageshadow="2"
inkscape:zoom="0.51319034"
inkscape:cx="156.62969"
inkscape:cy="949.8091"
inkscape:document-units="px"
inkscape:current-layer="g5238"
showgrid="false"
inkscape:window-width="675"
inkscape:window-height="547"
inkscape:window-x="0"
inkscape:window-y="0"
inkscape:window-maximized="0" />
<metadata
id="metadata7">
<rdf:RDF>
<cc:Work
rdf:about="">
<dc:format>image/svg+xml</dc:format>
<dc:type
rdf:resource="http://purl.org/dc/dcmitype/StillImage" />
<dc:title></dc:title>
</cc:Work>
</rdf:RDF>
</metadata>
<g
inkscape:label="Layer 1"
inkscape:groupmode="layer"
id="layer1">
<g
id="g5238"
transform="translate(-23.335007,-4.8042662)"
style="fill:none;stroke:#ff0000;stroke-width:0.7;stroke-miterlimit:4;stroke-dasharray:none">
<path
id="path5244"
style="fill:url(#linearGradient5264);stroke:#3a3a3a;stroke-width:4;stroke-miterlimit:4;stroke-dasharray:none;marker-start:none;stroke-opacity:1;fill-opacity:1;filter:url(#filter5282)"
d="m 124.05478,198.95569 25.59428,-0.003 -38.08971,39.75365 -36.022918,-39.75365 23.518346,0.003 0,-11.2623 c 0,-11.56985 3.814362,-23.72799 12.843752,-32.8125 9.02939,-9.08451 22.71756,-14.28125 39.34375,-14.28125 15.89659,0 28.81237,4.90636 36.875,13.9375 8.06263,9.03114 10.40625,20.50418 10.40625,30.9375 l 0,97.6875 -25,0 0,-97.6875 c 0,-6.68688 -1.49995,-11.41088 -4.0625,-14.28125 -2.56255,-2.87037 -7.00817,-5.59375 -18.21875,-5.59375 -11.69655,0 -17.86524,3.15497 -21.59375,6.90625 -3.72851,3.75128 -5.59375,8.92322 -5.59375,15.1875 l 0,11.2623 z"
sodipodi:nodetypes="ccccccscsccccssscc" />
</g>
</g>
</svg>

After

Width:  |  Height:  |  Size: 5.9 KiB

View file

@ -1,8 +1,9 @@
<%
rerun = File.file?('rerun.txt') ? IO.read('rerun.txt') : ""
rerun_opts = rerun.to_s.strip.empty? ? "--format progress features" : "--format #{ENV['CUCUMBER_FORMAT'] || 'pretty'} #{rerun}"
std_opts = "#{rerun_opts} --format rerun --out rerun.txt --strict --tags ~@wip"
rerun_opts = rerun.to_s.strip.empty? ? "--format progress " : "--format #{ENV['CUCUMBER_FORMAT'] || 'pretty'} #{rerun}"
requires = "-r features/support/env.rb -r features/step_definitions"
std_opts = "#{rerun_opts} #{requires} --format rerun --out rerun.txt --strict --tags ~@wip"
%>
default: <%= std_opts %> --tags ~@selenium
selenium: <%= std_opts %> --tags @selenium
selenium: <%= std_opts %> --tags @selenium -r features/support/selenium.rb
wip: --tags @wip:3 --wip features

View file

@ -5,7 +5,7 @@ development:
username: root
password:
test:
test: &TEST
adapter: sqlite3
database: ":memory:"
@ -15,3 +15,9 @@ production:
host: localhost
username: root
password:
cucumber:
<<: *TEST
selenium:
<<: *TEST

View file

@ -22,7 +22,6 @@ config.action_controller.allow_forgery_protection = false
config.action_mailer.delivery_method = :test
config.gem 'cucumber-rails', :lib => false, :version => '>=0.2.3' unless File.directory?(File.join(Rails.root, 'vendor/plugins/cucumber-rails'))
config.gem 'database_cleaner', :lib => false, :version => '>=0.2.3' unless File.directory?(File.join(Rails.root, 'vendor/plugins/database_cleaner'))
config.gem 'webrat', :lib => false, :version => '>=0.6.0' unless File.directory?(File.join(Rails.root, 'vendor/plugins/webrat'))
config.gem 'rspec', :lib => false, :version => '>=1.2.9' unless File.directory?(File.join(Rails.root, 'vendor/plugins/rspec'))
config.gem 'rspec-rails', :lib => false, :version => '>=1.2.9' unless File.directory?(File.join(Rails.root, 'vendor/plugins/rspec-rails'))

View file

@ -0,0 +1,30 @@
# Edit at your own peril - it's recommended to regenerate this file
# in the future when you upgrade to a newer version of Cucumber.
# IMPORTANT: Setting config.cache_classes to false is known to
# break Cucumber's use_transactional_fixtures method.
# For more information see https://rspec.lighthouseapp.com/projects/16211/tickets/165
config.cache_classes = true
# Log error messages when you accidentally call methods on nil.
config.whiny_nils = true
# Show full error reports and disable caching
config.action_controller.consider_all_requests_local = true
config.action_controller.perform_caching = false
# Disable request forgery protection in test environment
config.action_controller.allow_forgery_protection = false
# Tell Action Mailer not to deliver emails to the real world.
# The :test delivery method accumulates sent emails in the
# ActionMailer::Base.deliveries array.
config.action_mailer.delivery_method = :test
config.gem 'cucumber-rails', :lib => false, :version => '>=0.2.3' unless File.directory?(File.join(Rails.root, 'vendor/plugins/cucumber-rails'))
config.gem 'webrat', :lib => false, :version => '>=0.6.0' unless File.directory?(File.join(Rails.root, 'vendor/plugins/webrat'))
config.gem 'rspec', :lib => false, :version => '>=1.2.9' unless File.directory?(File.join(Rails.root, 'vendor/plugins/rspec'))
config.gem 'rspec-rails', :lib => false, :version => '>=1.2.9' unless File.directory?(File.join(Rails.root, 'vendor/plugins/rspec-rails'))
config.gem 'database_cleaner', :lib => false, :version => '>=0.2.3' unless File.directory?(File.join(Rails.root, 'vendor/plugins/database_cleaner'))

View file

@ -0,0 +1,50 @@
Feature: Manage contexts
In order to manage my contexts
As a Tracks user
I want to view, edit, add, or remove contexts
Background:
Given the following user record
| login | password | is_admin |
| testuser | secret | false |
And I have logged in as "testuser" with password "secret"
@selenium
Scenario: In place edit of context name
Given I have a context called "Errands"
When I visits the context page for "Errands"
And I edit the context name in place to be "OutAndAbout"
Then I should see the context name is "OutAndAbout"
When I go to the contexts page
Then he should see that a context named "Errands" is not present
And he should see that a context named "OutAndAbout" is present
@selenium
Scenario: Delete context from context page
Given I have a context called "@computer"
When I go to the contexts page
Then the badge should show 1
When I delete the context "@computer"
Then he should see that a context named "@computer" is not present
And the badge should show 0
@selenium
Scenario: Delete context from context page right after an edit
Given I have a context called "@computer"
When I go to the contexts page
And I edit the context to rename it to "@laptop"
When I delete the context "@laptop"
Then he should see that a context named "@laptop" is not present
And the badge should show 0
@selenium
Scenario: Edit context from context twice
Given I have a context called "@computer"
When I go to the contexts page
And I edit the context to rename it to "@laptop"
And I edit the context to rename it to "@ipad"
Then he should see that a context named "@computer" is not present
And he should see that a context named "@laptop" is not present
And he should see that a context named "@ipad" is present
And the badge should show 1

View file

@ -0,0 +1,25 @@
Feature: dependencies
As a Tracks user
In order to keep track of complex todos
I want to assign and manage todo dependencies
Background:
Given the following user record
| login | password | is_admin |
| testuser | secret | false |
And I have logged in as "testuser" with password "secret"
@selenium
Scenario: Adding dependency to dependency
Given I have a project "dependencies" with 3 todos
And "Todo 2" depends on "Todo 1"
When I visit the "dependencies" project
And I drag "Todo 3" to "Todo 2"
Then the dependencies of "Todo 2" should include "Todo 1"
And the dependencies of "Todo 3" should include "Todo 2"
When I expand the dependencies of "Todo 1"
Then I should see "Todo 2" within the dependencies of "Todo 1"
And I should see "Todo 3" within the dependencies of "Todo 1"
When I expand the dependencies of "Todo 2"
Then I should see "Todo 3" within the dependencies of "Todo 2"

View file

@ -0,0 +1,19 @@
Feature: Manage a project
In order to reach a goal by doing several related todos
As a Tracks user
I want to manage these todos in a project
Background:
Given the following user record
| login | password | is_admin |
| testuser | secret | false |
And I have logged in as "testuser" with password "secret"
And there exists a project "manage me" for user "testuser"
@selenium
Scenario: I can describe the project using markup
When I visit the "manage me" project
And I edit the project description to "_successfull outcome_: project is *done*"
Then I should see the italic text "successfull outcome" in the project description
And I should see the bold text "done" in the project description

View file

@ -8,17 +8,16 @@ Feature: View, add, remove notes
Given the following user record
| login | password | is_admin |
| testuser | secret | false |
And I have logged in as "testuser" with password "secret"
Scenario: View notes
Given I have logged in as "testuser" with password "secret"
And I have two projects with one note each
Given I have two projects with one note each
When I go to the notes page
Then 2 notes should be visible
And the badge should show 2
Scenario: Add a new note
Given I have logged in as "testuser" with password "secret"
And I have one project "Pass Final Exam" with no notes
Given I have one project "Pass Final Exam" with no notes
When I add note "My Note A" from the "Pass Final Exam" project page
Then I should see note "My Note A" on the "Pass Final Exam" project page
And I should see note "My Note A" on the notes page
@ -26,25 +25,15 @@ Feature: View, add, remove notes
@selenium
Scenario: Delete note from notes page
Given I have logged in as "testuser" with password "secret"
And I have a project "Pass Final Exam" with 2 notes
Given I have a project "Pass Final Exam" with 2 notes
When I go to the notes page
And I delete the first note
Then the first note should disappear
Then the badge should show 1
And the badge should show 1
@selenium
Scenario: Link to note
Given I have logged in as "testuser" with password "secret"
And I have a project "Pass Final Exam" with 1 note
Given I have a project "Pass Final Exam" with 1 note
When I visit the "Pass Final Exam" project
And I click the icon next to the note
Then I should see the note text
@selenium
Scenario: Toggle notes
Given I have logged in as "testuser" with password "secret"
And I have two projects with one note each
When I go to the notes page
And I click Toggle Notes
Then I should see the body of the notes

View file

@ -0,0 +1,47 @@
Given /^I have a context called "([^\"]*)"$/ do |context_name|
@context = @current_user.contexts.create!(:name => context_name)
end
When /^I visits the context page for "([^\"]*)"$/ do |context_name|
context = @current_user.contexts.find_by_name(context_name)
context.should_not be_nil
visit "/contexts/#{context.id}"
end
When /^I edit the context name in place to be "([^\"]*)"$/ do |new_context_name|
selenium.click "context_name"
fill_in "value", :with => "OutAndAbout"
click_button "OK"
end
Then /^I should see the context name is "([^\"]*)"$/ do |context_name|
Then "I should see \"#{context_name}\""
end
Then /^he should see that a context named "([^\"]*)" is present$/ do |context_name|
Then "I should see \"#{context_name}\""
end
Then /^he should see that a context named "([^\"]*)" is not present$/ do |context_name|
Then "I should not see \"#{context_name}\""
end
Given /^I have a context "([^\"]*)" with (.*) actions$/ do |context_name, number_of_actions|
context = @current_user.contexts.create!(:name => context_name)
1.upto number_of_actions.to_i do |i|
@current_user.todos.create!(:context_id => context.id, :description => "todo #{i}")
end
end
When /^I delete the context "([^\"]*)"$/ do |context_name|
context = @current_user.contexts.find_by_name(context_name)
context.should_not be_nil
click_link "delete_context_#{context.id}"
selenium.get_confirmation.should == "Are you sure that you want to delete the context '#{context_name}'?"
end
When /^I edit the context to rename it to "([^\"]*)"$/ do |new_name|
click_link "edit_context_#{@context.id}"
fill_in "context_name", :with => new_name
click_button "submit_context_#{@context.id}"
end

View file

@ -1,7 +1,16 @@
Then /the badge should show (.*)/ do |number|
# puts response.body.inspect
badge = -1
response.should have_xpath("//span[@id='badge_count']") do |node|
badge = node.first.content.to_i
xpath= "//span[@id='badge_count']"
if Rails.env == 'selenium'
response.should have_xpath(xpath)
badge = response.selenium.get_text("xpath=#{xpath}").to_i
else
response.should have_xpath(xpath) do |node|
badge = node.first.content.to_i
end
end
badge.should == number.to_i
end

View file

@ -1,9 +1,9 @@
Given /^I have logged in as "(.*)" with password "(.*)"$/ do |username, password|
visit login_path
fill_in "login", :with => username
fill_in "password", :with => password
click_button "Sign in"
response.body.should =~ /Login successful/m
fill_in "Login", :with => username
fill_in "Password", :with => password
click_button
response.should contain(/Login successful/)
@current_user = User.find_by_login(username)
end
@ -11,4 +11,4 @@ When /^I submit the login form as user "([^\"]*)" with password "([^\"]*)"$/ do
fill_in 'Login', :with => username
fill_in 'Password', :with => password
click_button
end
end

View file

@ -1,3 +1,7 @@
Given /^I have one project "([^\"]*)" with no notes$/ do |project_name|
@current_user.projects.create!(:name => project_name)
end
Given /^I have two projects with one note each$/ do
project_a = @current_user.projects.create!(:name => 'project A')
project_a.notes.create!(:user_id => @current_user.id, :body => 'note for project A')
@ -5,26 +9,38 @@ Given /^I have two projects with one note each$/ do
project_b.notes.create!(:user_id => @current_user.id, :body => 'note for project B')
end
Then /^(.*) notes should be visible$/ do |number|
# count number of project_notes
count = 0
response.should have_xpath("//div[@class='project_notes']") { |nodes| nodes.each { |n| count += 1 }}
count.should == number.to_i
Given /^I have a project "([^\"]*)" with (.*) notes?$/ do |project_name, num|
project = @current_user.projects.create!(:name => project_name)
1.upto num.to_i do |i|
project.notes.create!(:user_id => @current_user.id, :body => "A note #{i}. This is the very long body of note #{i} where you should not see the last part of the note after 50 characters")
end
end
When /^I click Toggle Notes$/ do
click_link 'Toggle notes'
end
Given /^I have one project "([^\"]*)" with no notes$/ do |project_name|
@current_user.projects.create!(:name => project_name)
end
When /^I add note "([^\"]*)" from the "([^\"]*)" project page$/ do |note, project|
project = Project.find_by_name(project)
project.notes.create!(:user_id => @current_user.id, :body => note)
end
When /^I delete the first note$/ do
click_link "delete note"
selenium.get_confirmation.should == "Are you sure that you want to delete the note '1'?"
end
When /^I click the icon next to the note$/ do
click_link "Show note"
end
Then /^(.*) notes should be visible$/ do |number|
# count number of project_notes
count = 0
response.should have_xpath("//div[@class='project_notes']") { |nodes| nodes.each { |n| count += 1 }}
count.should == number.to_i
end
Then /^I should see note "([^\"]*)" on the "([^\"]*)" project page$/ do |note, project|
project = Project.find_by_name(project)
visit project_path(project)
@ -36,75 +52,12 @@ Then /^I should see note "([^\"]*)" on the notes page$/ do |note|
Then "I should see \"#{note}\""
end
Given /^I have a project "([^\"]*)" with (.*) notes?$/ do |project_name, num|
project = @current_user.projects.create!(:name => project_name)
num.to_i.downto 0 do |i|
project.notes.create!(:user_id => @current_user.id, :body => "A note #{i}")
end
Then /^the first note should disappear$/ do
# the first note contains "A note 1", generated by the Given def above
Then "I should not see \"A note 1\""
end
When /^I delete the first note$/ do
# need selenium for this to check on the js
pending
end
Given /^I have one project "([^\"]*)" with 1 note$/ do |arg1|
pending
end
When /^I click the icon next to the note$/ do
# need selenium for this to check on the js
pending
end
Then /^I should see the note text$/ do
# need selenium for this to check on the js
pending
end
Then /^I should see the body of the notes$/ do
pending
end
#------ left over from old stories. can be removed if pending stuff is done
When "Luis adds a note from the Pass Final Exam project page" do
When "Luis visits the Pass Final Exam project page"
clicks_link 'Add a note', :wait => :ajax
fills_in 'new_note_body', :with => 'new exam note'
clicks_button 'Add note', :wait => :ajax
end
When "Luis deletes the first note" do
selenium.click "css=a.delete_note"
selenium.get_confirmation.should =~ /delete/
end
When "clicks the icon next to the note" do
selenium.click "css=a.link_to_notes"
wait_for_page_to_load
end
When "Luis clicks Toggle Notes" do
clicks_link 'Toggle notes', :wait => :effects
end
Then "Luis should see the note on the Pass Final Exam project page" do
should_see "new exam note"
end
Then "Luis should see the note on the notes page" do
visits '/notes'
should_see "new exam note"
end
Then "the first note should disappear" do
wait_for_ajax_and_effects
should_not_see 'exam note 1'
end
Then "he should see the note text" do
should_see 'exam note 1'
Then "I should see \"after 50 characters\""
end

View file

@ -1,4 +1,48 @@
When /^I visit the "([^\"]*)" project$/ do |project_name|
project = Project.find_by_name(project_name)
visit project_path(project)
Given /^I have a project "([^\"]*)" with (.*) todos$/ do |project_name, num_todos|
context = @current_user.contexts.create!(:name => "Context A")
project = @current_user.projects.create!(:name => project_name)
1.upto num_todos.to_i do |i|
@current_user.todos.create!(
:project_id => project.id,
:context_id => context.id,
:description => "Todo #{i}")
end
end
Given /^there exists a project "([^\"]*)" for user "([^\"]*)"$/ do |project_name, user_name|
user = User.find_by_login(user_name)
user.should_not be_nil
user.projects.create!(:name => project_name)
end
When /^I visit the "([^\"]*)" project$/ do |project_name|
@project = Project.find_by_name(project_name)
@project.should_not be_nil
visit project_path(@project)
end
When /^I edit the project description to "([^\"]*)"$/ do |new_description|
click_link "link_edit_project_#{@project.id}"
fill_in "project[description]", :with => new_description
click_button "submit_project_#{@project.id}"
end
Then /^I should see the bold text "([^\"]*)" in the project description$/ do |bold|
xpath="//div[@class='project_description']/p/strong"
response.should have_xpath(xpath)
bold_text = response.selenium.get_text("xpath=#{xpath}")
puts "bt=#{bold_text}"
bold_text.should =~ /#{bold}/
end
Then /^I should see the italic text "([^\"]*)" in the project description$/ do |italic|
xpath="//div[@class='project_description']/p/em"
response.should have_xpath(xpath)
italic_text = response.selenium.get_text("xpath=#{xpath}")
puts "it=#{italic_text}"
italic_text.should =~ /#{italic}/
end

View file

@ -24,8 +24,67 @@ Given /^I have ([0-9]+) completed todos$/ do |count|
end
end
Given /^"(.*)" depends on "(.*)"$/ do |successor_name, predecessor_name|
successor = Todo.find_by_description(successor_name)
predecessor = Todo.find_by_description(predecessor_name)
successor.add_predecessor(predecessor)
successor.state = "pending"
successor.save!
end
When /^I drag "(.*)" to "(.*)"$/ do |dragged, target|
drag_id = Todo.find_by_description(dragged).id
drop_id = Todo.find_by_description(target).id
drag_name = "xpath=//div[@id='line_todo_#{drag_id}']//img[@class='grip']"
# xpath does not seem to work here... reverting to css
# xpath=//div[@id='line_todo_#{drop_id}']//img[@class='successor_target']
drop_name = "css=div#line_todo_#{drop_id} img.successor_target"
# HACK: the target img is hidden until drag starts. We need to show the img or the
# xpath will not find it
js="$('div#line_todo_#{drop_id} img.successor_target').show();"
selenium.get_eval "(function() {with(this) {#{js}}}).call(selenium.browserbot.getCurrentWindow());"
selenium.drag_and_drop_to_object(drag_name, drop_name)
arrow = "xpath=//div[@id='line_todo_#{drop_id}']/div/a[@class='show_successors']/img"
selenium.wait_for_element(arrow)
end
When /^I expand the dependencies of "([^\"]*)"$/ do |todo_name|
todo = Todo.find_by_description(todo_name)
todo.should_not be_nil
expand_img_locator = "xpath=//div[@id='line_todo_#{todo.id}']/div/a[@class='show_successors']/img"
selenium.click(expand_img_locator)
end
Then /^I should see ([0-9]+) todos$/ do |count|
count.to_i.downto 1 do |i|
match_xpath "div["
end
end
end
Then /^the dependencies of "(.*)" should include "(.*)"$/ do |child_name, parent_name|
parent = @current_user.todos.find_by_description(parent_name)
parent.should_not be_nil
child = parent.pending_successors.find_by_description(child_name)
child.should_not be_nil
end
Then /^I should see "([^\"]*)" within the dependencies of "([^\"]*)"$/ do |successor_description, todo_description|
todo = @current_user.todos.find_by_description(todo_description)
todo.should_not be_nil
successor = @current_user.todos.find_by_description(successor_description)
successor.should_not be_nil
# argh, webrat on selenium does not support within, so this won't work
# xpath = "//div[@id='line_todo_#{todo.id}'"
# Then "I should see \"#{successor_description}\" within \"xpath=#{xpath}\""
# let selenium look for the presence of the successor
xpath = "xpath=//div[@id='line_todo_#{todo.id}']//div[@id='successor_line_todo_#{successor.id}']//span"
selenium.wait_for_element(xpath, :timeout_in_seconds => 5)
end

View file

@ -14,6 +14,8 @@ module NavigationHelpers
login_path
when /the notes page/
notes_path
when /the contexts page/
contexts_path
# Add more page name => path mappings here

View file

@ -0,0 +1,22 @@
Webrat.configure do |config|
config.mode = :selenium
config.application_environment = :selenium
config.selenium_browser_startup_timeout = 30
#config.selenium_server_address = "localhost"
end
Cucumber::Rails::World.use_transactional_fixtures = false
require 'database_cleaner'
# clean the database once when starting
DatabaseCleaner.clean_with :truncation
DatabaseCleaner.strategy = :truncation
Before do
DatabaseCleaner.start
end
After do
DatabaseCleaner.clean
end

View file

@ -7,41 +7,48 @@
unless ARGV.any? {|a| a =~ /^gems/} # Don't load anything when running the gems:* tasks
vendored_cucumber_bin = Dir["#{RAILS_ROOT}/vendor/{gems,plugins}/cucumber*/bin/cucumber"].first
$LOAD_PATH.unshift(File.dirname(vendored_cucumber_bin) + '/../lib') unless vendored_cucumber_bin.nil?
vendored_cucumber_bin = Dir["#{RAILS_ROOT}/vendor/{gems,plugins}/cucumber*/bin/cucumber"].first
$LOAD_PATH.unshift(File.dirname(vendored_cucumber_bin) + '/../lib') unless vendored_cucumber_bin.nil?
begin
require 'cucumber/rake/task'
begin
require 'cucumber/rake/task'
namespace :cucumber do
Cucumber::Rake::Task.new({:ok => 'db:test:prepare'}, 'Run features that should pass') do |t|
t.binary = vendored_cucumber_bin # If nil, the gem's binary is used.
t.fork = true # You may get faster startup if you set this to false
t.profile = 'default'
namespace :cucumber do
Cucumber::Rake::Task.new({:ok => 'db:test:prepare'}, 'Run features that should pass') do |t|
t.binary = vendored_cucumber_bin # If nil, the gem's binary is used.
t.fork = true # You may get faster startup if you set this to false
t.profile = 'default'
end
Cucumber::Rake::Task.new({:wip => 'db:test:prepare'}, 'Run features that are being worked on') do |t|
t.binary = vendored_cucumber_bin
t.fork = true # You may get faster startup if you set this to false
t.profile = 'wip'
end
Cucumber::Rake::Task.new({:selenium => 'db:test:prepare'}, 'Run features that require selenium') do |t|
t.binary = vendored_cucumber_bin
t.fork = true # You may get faster startup if you set this to false
t.profile = 'selenium'
ENV['RAILS_ENV'] = "selenium" # switch to selenium environment
end
desc 'Run all features'
task :all => [:ok, :wip]
end
desc 'Alias for cucumber:ok'
task :cucumber => 'cucumber:ok'
Cucumber::Rake::Task.new({:wip => 'db:test:prepare'}, 'Run features that are being worked on') do |t|
t.binary = vendored_cucumber_bin
t.fork = true # You may get faster startup if you set this to false
t.profile = 'wip'
task :default => :cucumber
task :features => :cucumber do
STDERR.puts "*** The 'features' task is deprecated. See rake -T cucumber ***"
end
rescue LoadError
desc 'cucumber rake task not available (cucumber not installed)'
task :cucumber do
abort 'Cucumber rake task is not available. Be sure to install cucumber as a gem or plugin'
end
desc 'Run all features'
task :all => [:ok, :wip]
end
desc 'Alias for cucumber:ok'
task :cucumber => 'cucumber:ok'
task :default => :cucumber
task :features => :cucumber do
STDERR.puts "*** The 'features' task is deprecated. See rake -T cucumber ***"
end
rescue LoadError
desc 'cucumber rake task not available (cucumber not installed)'
task :cucumber do
abort 'Cucumber rake task is not available. Be sure to install cucumber as a gem or plugin'
end
end
end

Binary file not shown.

After

Width:  |  Height:  |  Size: 801 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 794 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 5.6 KiB

View file

@ -49,7 +49,7 @@ $.fn.clearForm = function() {
* Unobtrusive jQuery written by Eric Allen
****************************************/
/* Set up authenticity token proplery */
/* Set up authenticity token properly */
$(document).ajaxSend(function(event, request, settings) {
if ( settings.type == 'POST' ) {
if(typeof(AUTH_TOKEN) != 'undefined'){
@ -154,9 +154,9 @@ function update_order(event, ui){
url = '';
if(row.hasClass('context'))
url = '/contexts/order';
url = relative_to_root('contexts/order');
else if(row.hasClass('project'))
url = '/projects/order';
url = relative_to_root('projects/order');
else {
console.log("Bad sortable list");
return;
@ -185,11 +185,11 @@ function project_defaults(){
}
function enable_rich_interaction(){
$('input.Date').datepicker({'dateFormat': dateFormat});
$('input.Date').datepicker({'dateFormat': dateFormat, 'firstDay': weekStart});
/* Autocomplete */
$('input[name=context_name]:not(.ac_input)').autocomplete(contextNames, {matchContains: true});
$('input[name=project[default_context_name]]:not(.ac_input)').autocomplete(contextNames, {matchContains: true});
$('input[name=project_name]:not(.ac_input)').autocomplete(projectNames, {matchContains: true});
$('input[name=context_name]').autocomplete(contextNames, {matchContains: true});
$('input[name=project[default_context_name]]').autocomplete(contextNames, {matchContains: true});
$('input[name=project_name]').autocomplete(projectNames, {matchContains: true});
$('input[name=tag_list]:not(.ac_input)').autocomplete(tagNames, {multiple: true,multipleSeparator:',',matchContains:true});
$('input[name=predecessor_list]:not(.ac_input)').autocomplete('/todos/auto_complete_for_predecessor',
{multiple: true,multipleSeparator:','});
@ -209,18 +209,41 @@ function enable_rich_interaction(){
/* Drag & Drop for successor/predecessor */
function drop_todo(evt, ui) {
dragged_todo = ui.draggable[0].id.split('_')[2];
dropped_todo = this.id.split('_')[2];
dropped_todo = $(this).parents('.item-show').get(0).id.split('_')[2];
ui.draggable.hide();
$(this).block({message: null});
$.post('/todos/add_predecessor',
$.post(relative_to_root('todos/add_predecessor'),
{successor: dragged_todo, predecessor: dropped_todo},
null, 'script');
}
$('.item-show').draggable({handle: '.grip', revert: 'invalid'});
$('.item-show').droppable({
drop: drop_todo,
hoverClass: 'hover'
$('.item-show').draggable({handle: '.grip',
revert: 'invalid',
start: function() {$('.successor_target').show();},
stop: function() {$('.successor_target').hide();}});
$('.successor_target').droppable({drop: drop_todo,
tolerance: 'pointer',
hoverClass: 'hover'});
/* Reset auto updater */
field_touched = false;
}
/* Auto-refresh */
function setup_auto_refresh(interval){
field_touched = false;
function refresh_page() {
if(!field_touched){
window.location.reload();
}
}
setTimeout(refresh_page, interval);
$(function(){
$("input").live('keydown', function(){
field_touched = true;
});
});
}
@ -287,6 +310,17 @@ $(document).ready(function() {
$.post(this.value, params, null, 'script');
});
/* set behavior for edit icon */
$(".item-container a.edit_item").live('click', function (ev){
itemContainer = $(this).parents(".item-container");
$.ajax({
url: this.href,
beforeSend: function() { itemContainer.block({message: null});},
complete: function() { itemContainer.unblock();},
dataType: 'script'});
return false;
});
setup_container_toggles();
$('#toggle_action_new').click(function(){
@ -339,7 +373,6 @@ $(document).ready(function() {
});
$("#recurring_todo_new_action_cancel").click(function(){
$('#recurring-todo-form-new-action').clearForm();
$('#recurring-todo-form-new-action input:text:first').focus();
TracksForm.hide_all_recurring();
$('#recurring_daily').show();
@ -347,7 +380,6 @@ $(document).ready(function() {
});
$("#recurring_todo_edit_action_cancel").live('click', function(){
$('#recurring-todo-form-edit-action').clearForm();
$('#recurring-todo-form-edit-action input:text:first').focus();
TracksForm.hide_all_recurring();
$('#recurring_daily').show();
@ -372,9 +404,9 @@ $(document).ready(function() {
highlight = function(){
$('div.context span#context_name').effect('highlight', {}, 500);
};
$.post('/contexts/update/'+context_id, {'context[name]': value}, highlight);
$.post(relative_to_root('contexts/update/'+context_id), {'context[name]': value}, highlight);
return(value);
}, {style: 'padding:0px', submit: "OK"});
}, {style: 'padding:0px', submit: "OK", cancel: "CANCEL"});
/* Projects behavior */
@ -383,7 +415,7 @@ $(document).ready(function() {
highlight = function(){
$('h2#project_name').effect('highlight', {}, 500);
};
$.post('/projects/update/'+project_id, {'project[name]': value, 'update_project_name': 'true'}, highlight, 'script');
$.post(relative_to_root('projects/update/'+project_id), {'project[name]': value, 'update_project_name': 'true'}, highlight, 'script');
return(value);
};
@ -448,6 +480,51 @@ $(document).ready(function() {
$("#list-contexts-active").sortable({handle: '.handle', update: update_order});
$("#list-contexts-hidden").sortable({handle: '.handle', update: update_order});
/* Feeds page */
$("#feed-contexts").change(function(){
$("#feeds-for-context").load('/feedlist/get_feeds_for_context?context_id='+this.value);
});
$("#feed-projects").change(function(){
$("#feeds-for-project").load('/feedlist/get_feeds_for_project?project_id='+this.value);
});
/* Integrations page */
/*
<%= observe_field "applescript1-contexts", :update => "applescript1",
:with => 'context_id',
:url => { :controller => "integrations", :action => "get_applescript1" },
:before => "$('applescript1').startWaiting()",
:complete => "$('applescript1').stopWaiting()"
%>
*/
$('#applescript1-contexts').live('change', function(){
$("#applescript1").load(relative_to_root('integrations/get_applescript1?context_id='+this.value));
});
/*
<%= observe_field "applescript2-contexts", :update => "applescript2",
:with => 'context_id',
:url => { :controller => "integrations", :action => "get_applescript2" },
:before => "$('applescript2').startWaiting()",
:complete => "$('applescript2').stopWaiting()"
%>
*/
$('#applescript2-contexts').live('change', function(){
$("#applescript2").load(relative_to_root('integrations/get_applescript2?context_id='+this.value));
});
/*
<%= observe_field "quicksilver-contexts", :update => "quicksilver",
:with => 'context_id',
:url => { :controller => "integrations", :action => "get_quicksilver_applescript" },
:before => "$('quicksilver').startWaiting()",
:complete => "$('quicksilver').stopWaiting()"
%>
*/
$('#quicksilver-contexts').live('change', function(){
$("#quicksilver").load(relative_to_root('integrations/get_quicksilver_applescript?context_id='+this.value));
});
/* Gets called from some AJAX callbacks, too */
enable_rich_interaction();

View file

@ -99,8 +99,8 @@ a.to_bottom:hover {background: transparent url(../images/bottom_on.png) no-repea
a.show_notes, a.link_to_notes {background-image: url(../images/notes_off.png); background-repeat: no-repeat; padding: 1px; background-color: transparent;}
a.show_notes:hover, a.link_to_notes:hover {background-image: url(../images/notes_on.png); background-repeat: no-repeat; padding: 1px; background-color: transparent;}
a.show_successors, a.link_to_successors {background-image: url(/images/successor_off.png); background-repeat: no-repeat; padding: 1px; background-color: transparent;}
a.show_successors:hover, a.link_to_successors:hover {background-image: url(/images/successor_on.png); background-repeat: no-repeat; padding: 1px; background-color: transparent;}
a.show_successors, a.link_to_successors {background-image: url(../images/successor_off.png); background-repeat: no-repeat; padding: 1px; background-color: transparent;}
a.show_successors:hover, a.link_to_successors:hover {background-image: url(../images/successor_on.png); background-repeat: no-repeat; padding: 1px; background-color: transparent;}
/* Structural divs */
@ -939,9 +939,17 @@ div.message {
cursor: move;
}
.hover {
background: #EAEAEA;
font-weight: bold;
.successor_target {
background-image:url("../images/add_successor_off.png");
background-repeat: no-repeat;
background-position: center right;
display: none;
}
.successor_target.hover {
background-image:url("../images/add_successor_on.png");
background-repeat: no-repeat;
background-position: center right;
}
/* Error message styles */
@ -1284,3 +1292,6 @@ div.auto_complete ul strong.highlight {
margin:0;
padding:0;
}
.ui-datepicker {
z-index: 1000;
}

View file

@ -1,4 +0,0 @@
dir = File.dirname(__FILE__)
Dir[File.expand_path("#{dir}/**/*.rb")].uniq.each do |file|
require file
end

View file

@ -1,15 +0,0 @@
Story: Change context name
As a Tracks user
I want to change the name of a context
So that it can best reflect my daily life
Scenario: In place edit of context name
Given a logged in user Luis
And Luis has a context Errands
When Luis visits the Errands context page
And he edits the Errands context name in place to be OutAndAbout
Then he should see the context name is OutAndAbout
When Luis visits the context listing page
Then he should see that a context named Errands is not present
And he should see that a context named OutAndAbout is present

View file

@ -1,3 +0,0 @@
ENV["RAILS_ENV"] = "test"
require File.expand_path(File.dirname(__FILE__) + "/../config/environment")
require 'spec/rails/story_adapter'

View file

@ -1,41 +0,0 @@
Story: View, add, remove notes
As a Tracks user
I want to view, add, and remove notes
So that I can keep important information easily accessible
Scenario: View and toggle notes
Given a logged in user Luis
And Luis has two projects with one note each
When Luis visits the notes page
Then two notes should be visible
And the badge should show 2
When Luis clicks Toggle Notes
Then the body of the notes should be shown
Scenario: Add a new note
Given a logged in user Luis
And Luis has one project Pass Final Exam with no notes
When Luis adds a note from the Pass Final Exam project page
Then Luis should see the note on the Pass Final Exam project page
And Luis should see the note on the notes page
And the badge on the notes page should show 1
Scenario: Delete note from notes page
Given a logged in user Luis
And Luis has one project Pass Final Exam with 2 notes
When Luis visits the notes page
And Luis deletes the first note
Then the first note should disappear
Then the badge should show 1
Scenario: Link to note
Given a logged in user Luis
And Luis has one project Pass Final Exam with 1 note
When Luis visits the Pass Final Exam project page
And clicks the icon next to the note
Then he should see the note text

View file

@ -1,34 +0,0 @@
steps_for :context_detail do
include_steps_for :users
Given "Luis has a context Errands" do
@errands = @luis.contexts.create!(:name => 'Errands')
end
When "Luis visits the Errands context page" do
visits "/contexts/#{@errands.to_param}"
end
When "he edits the Errands context name in place to be OutAndAbout" do
selenium.click 'context_name_in_place_editor'
wait_for_ajax_and_effects
selenium.type "css=#context_name_in_place_editor-inplaceeditor input.editor_field", "OutAndAbout"
clicks_button "ok", :wait => :ajax
end
When "Luis visits the context listing page" do
visits "/contexts"
end
Then "he should see the context name is OutAndAbout" do
should_see 'OutAndAbout'
end
Then "he should see that a context named Errands is not present" do
should_not_see 'Errands'
end
Then "he should see that a context named OutAndAbout is present" do
should_see 'OutAndAbout'
end
end

View file

@ -1,94 +0,0 @@
steps_for :notes do
include_steps_for :users
Given "Luis has two projects with one note each" do
project_a = @luis.projects.create!(:name => 'project A')
project_a.notes.create!(:user_id => @luis.id, :body => 'note for project A')
project_b = @luis.projects.create!(:name => 'project B')
project_b.notes.create!(:user_id => @luis.id, :body => 'note for project B')
end
Given "Luis has one project Pass Final Exam with no notes" do
@exam_project = @luis.projects.create!(:name => 'Pass Final Exam')
end
Given "Luis has one project Pass Final Exam with 1 note" do
Given "Luis has one project Pass Final Exam with no notes"
@exam_project.notes.create!(:user_id => @luis.id, :body => 'exam note 1')
end
Given "Luis has one project Pass Final Exam with 2 notes" do
Given "Luis has one project Pass Final Exam with 1 note"
@exam_project.notes.create!(:user_id => @luis.id, :body => 'exam note 2')
end
When "Luis visits the notes page" do
visits '/notes'
end
When "Luis adds a note from the Pass Final Exam project page" do
When "Luis visits the Pass Final Exam project page"
clicks_link 'Add a note', :wait => :ajax
fills_in 'new_note_body', :with => 'new exam note'
clicks_button 'Add note', :wait => :ajax
end
When "Luis visits the Pass Final Exam project page" do
visits "/projects/#{@exam_project.to_param}"
end
When "Luis deletes the first note" do
selenium.click "css=a.delete_note"
selenium.get_confirmation.should =~ /delete/
end
When "clicks the icon next to the note" do
selenium.click "css=a.link_to_notes"
wait_for_page_to_load
end
When "Luis clicks Toggle Notes" do
clicks_link 'Toggle notes', :wait => :effects
end
Then "the body of the notes should be shown" do
wait_for_effects
selenium.is_visible("css=body.notes").should be_true
end
Then "Luis should see the note on the Pass Final Exam project page" do
should_see "new exam note"
end
Then "Luis should see the note on the notes page" do
visits '/notes'
should_see "new exam note"
end
Then "the badge on the notes page should show 1" do
badge_count_should_show(1)
end
Then "the first note should disappear" do
wait_for_ajax_and_effects
should_not_see 'exam note 1'
end
Then "the badge should show 1" do
wait_for_ajax_and_effects
badge_count_should_show(1)
end
Then "the badge should show 2" do
badge_count_should_show(2)
end
Then "two notes should be visible" do
should_see 'note for project A'
should_see 'note for project B'
end
Then "he should see the note text" do
should_see 'exam note 1'
end
end

View file

@ -1,10 +0,0 @@
setup :fixtures => :all
login :as => 'admin'
open "/contexts/1"
click "context_name"
wait_for_element_present "css=#context_name form input"
type "css=#context_name form input", "Test Foo"
click "css=#context_name form button"
wait_for_text "context_name", "Test Foo"
open "/contexts/1"
wait_for_text "context_name", "Test Foo"

View file

@ -1,6 +0,0 @@
setup :fixtures => :all
login :as => 'admin'
open "/contexts"
click "css=#context_3 .buttons img.delete_item"
assert_confirmation "Are you sure that you want to delete the context 'email'?"
wait_for_element_not_present "context_3"

566
vendor/gems/soap4r-1.5.8/.specification vendored Normal file
View file

@ -0,0 +1,566 @@
--- !ruby/object:Gem::Specification
name: soap4r
version: !ruby/object:Gem::Version
version: 1.5.8
platform: ruby
authors:
- NAKAMURA, Hiroshi
autorequire:
bindir: bin
cert_chain:
date: 2007-09-23 00:00:00 -07:00
default_executable:
dependencies:
- !ruby/object:Gem::Dependency
name: httpclient
type: :runtime
version_requirement:
version_requirements: !ruby/object:Gem::Requirement
requirements:
- - ">="
- !ruby/object:Gem::Version
version: 2.1.1
version:
description:
email: nahi@ruby-lang.org
executables:
- wsdl2ruby.rb
- xsd2ruby.rb
extensions: []
extra_rdoc_files: []
files:
- bin/wsdl2ruby.rb
- bin/xsd2ruby.rb
- lib/tags
- lib/xsd
- lib/xsd/datatypes.rb
- lib/xsd/namedelements.rb
- lib/xsd/iconvcharset.rb
- lib/xsd/charset.rb
- lib/xsd/qname.rb
- lib/xsd/mapping.rb
- lib/xsd/codegen
- lib/xsd/codegen/methoddef.rb
- lib/xsd/codegen/moduledef.rb
- lib/xsd/codegen/gensupport.rb
- lib/xsd/codegen/commentdef.rb
- lib/xsd/codegen/classdef.rb
- lib/xsd/xmlparser
- lib/xsd/xmlparser/xmlscanner.rb
- lib/xsd/xmlparser/rexmlparser.rb
- lib/xsd/xmlparser/parser.rb
- lib/xsd/xmlparser/xmlparser.rb
- lib/xsd/codegen.rb
- lib/xsd/xmlparser.rb
- lib/xsd/ns.rb
- lib/xsd/datatypes1999.rb
- lib/wsdl
- lib/wsdl/xmlSchema
- lib/wsdl/xmlSchema/fractiondigits.rb
- lib/wsdl/xmlSchema/union.rb
- lib/wsdl/xmlSchema/importHandler.rb
- lib/wsdl/xmlSchema/minexclusive.rb
- lib/wsdl/xmlSchema/complexRestriction.rb
- lib/wsdl/xmlSchema/totaldigits.rb
- lib/wsdl/xmlSchema/group.rb
- lib/wsdl/xmlSchema/include.rb
- lib/wsdl/xmlSchema/annotation.rb
- lib/wsdl/xmlSchema/content.rb
- lib/wsdl/xmlSchema/maxexclusive.rb
- lib/wsdl/xmlSchema/element.rb
- lib/wsdl/xmlSchema/choice.rb
- lib/wsdl/xmlSchema/unique.rb
- lib/wsdl/xmlSchema/sequence.rb
- lib/wsdl/xmlSchema/list.rb
- lib/wsdl/xmlSchema/any.rb
- lib/wsdl/xmlSchema/maxinclusive.rb
- lib/wsdl/xmlSchema/attribute.rb
- lib/wsdl/xmlSchema/complexType.rb
- lib/wsdl/xmlSchema/enumeration.rb
- lib/wsdl/xmlSchema/length.rb
- lib/wsdl/xmlSchema/maxlength.rb
- lib/wsdl/xmlSchema/complexExtension.rb
- lib/wsdl/xmlSchema/simpleType.rb
- lib/wsdl/xmlSchema/parser.rb
- lib/wsdl/xmlSchema/pattern.rb
- lib/wsdl/xmlSchema/whitespace.rb
- lib/wsdl/xmlSchema/all.rb
- lib/wsdl/xmlSchema/schema.rb
- lib/wsdl/xmlSchema/xsd2ruby.rb
- lib/wsdl/xmlSchema/simpleContent.rb
- lib/wsdl/xmlSchema/mininclusive.rb
- lib/wsdl/xmlSchema/complexContent.rb
- lib/wsdl/xmlSchema/anyAttribute.rb
- lib/wsdl/xmlSchema/minlength.rb
- lib/wsdl/xmlSchema/simpleExtension.rb
- lib/wsdl/xmlSchema/simpleRestriction.rb
- lib/wsdl/xmlSchema/attributeGroup.rb
- lib/wsdl/xmlSchema/data.rb
- lib/wsdl/xmlSchema/import.rb
- lib/wsdl/xmlSchema/importer.rb
- lib/wsdl/documentation.rb
- lib/wsdl/param.rb
- lib/wsdl/portType.rb
- lib/wsdl/definitions.rb
- lib/wsdl/wsdl.rb
- lib/wsdl/operation.rb
- lib/wsdl/operationBinding.rb
- lib/wsdl/parser.rb
- lib/wsdl/message.rb
- lib/wsdl/port.rb
- lib/wsdl/types.rb
- lib/wsdl/part.rb
- lib/wsdl/soap
- lib/wsdl/soap/servletStubCreator.rb
- lib/wsdl/soap/body.rb
- lib/wsdl/soap/methodDefCreator.rb
- lib/wsdl/soap/encodedMappingRegistryCreator.rb
- lib/wsdl/soap/driverCreator.rb
- lib/wsdl/soap/literalMappingRegistryCreator.rb
- lib/wsdl/soap/element.rb
- lib/wsdl/soap/definitions.rb
- lib/wsdl/soap/servantSkeltonCreator.rb
- lib/wsdl/soap/standaloneServerStubCreator.rb
- lib/wsdl/soap/mappingRegistryCreatorSupport.rb
- lib/wsdl/soap/fault.rb
- lib/wsdl/soap/complexType.rb
- lib/wsdl/soap/cgiStubCreator.rb
- lib/wsdl/soap/operation.rb
- lib/wsdl/soap/address.rb
- lib/wsdl/soap/wsdl2ruby.rb
- lib/wsdl/soap/clientSkeltonCreator.rb
- lib/wsdl/soap/classDefCreatorSupport.rb
- lib/wsdl/soap/headerfault.rb
- lib/wsdl/soap/classDefCreator.rb
- lib/wsdl/soap/mappingRegistryCreator.rb
- lib/wsdl/soap/classNameCreator.rb
- lib/wsdl/soap/binding.rb
- lib/wsdl/soap/data.rb
- lib/wsdl/soap/header.rb
- lib/wsdl/binding.rb
- lib/wsdl/service.rb
- lib/wsdl/data.rb
- lib/wsdl/import.rb
- lib/wsdl/importer.rb
- lib/wsdl/info.rb
- lib/soap
- lib/soap/mappingRegistry.rb
- lib/soap/filter
- lib/soap/filter/filterchain.rb
- lib/soap/filter/streamhandler.rb
- lib/soap/filter/handler.rb
- lib/soap/encodingstyle
- lib/soap/encodingstyle/literalHandler.rb
- lib/soap/encodingstyle/soapHandler.rb
- lib/soap/encodingstyle/aspDotNetHandler.rb
- lib/soap/encodingstyle/handler.rb
- lib/soap/wsdlDriver.rb
- lib/soap/rpc
- lib/soap/rpc/driver.rb
- lib/soap/rpc/element.rb
- lib/soap/rpc/soaplet.rb
- lib/soap/rpc/cgistub.rb
- lib/soap/rpc/proxy.rb
- lib/soap/rpc/rpc.rb
- lib/soap/rpc/standaloneServer.rb
- lib/soap/rpc/router.rb
- lib/soap/rpc/httpserver.rb
- lib/soap/XMLSchemaDatatypes.rb
- lib/soap/marshal.rb
- lib/soap/driver.rb
- lib/soap/mapping
- lib/soap/mapping/registry.rb
- lib/soap/mapping/typeMap.rb
- lib/soap/mapping/mapping.rb
- lib/soap/mapping/rubytypeFactory.rb
- lib/soap/mapping/encodedregistry.rb
- lib/soap/mapping/wsdlencodedregistry.rb
- lib/soap/mapping/factory.rb
- lib/soap/mapping/wsdlliteralregistry.rb
- lib/soap/mapping/schemadefinition.rb
- lib/soap/mapping/literalregistry.rb
- lib/soap/nestedexception.rb
- lib/soap/element.rb
- lib/soap/charset.rb
- lib/soap/qname.rb
- lib/soap/httpconfigloader.rb
- lib/soap/netHttpClient.rb
- lib/soap/mapping.rb
- lib/soap/cgistub.rb
- lib/soap/XMLSchemaDatatypes1999.rb
- lib/soap/proxy.rb
- lib/soap/filter.rb
- lib/soap/baseData.rb
- lib/soap/streamHandler.rb
- lib/soap/generator.rb
- lib/soap/rpcUtils.rb
- lib/soap/mimemessage.rb
- lib/soap/header
- lib/soap/header/simplehandler.rb
- lib/soap/header/handlerset.rb
- lib/soap/header/mappinghandler.rb
- lib/soap/header/handler.rb
- lib/soap/property.rb
- lib/soap/attachment.rb
- lib/soap/parser.rb
- lib/soap/processor.rb
- lib/soap/standaloneServer.rb
- lib/soap/server.rb
- lib/soap/rpcRouter.rb
- lib/soap/ns.rb
- lib/soap/compat.rb
- lib/soap/soap.rb
- lib/soap/namespace.rb
- test/interopR4
- test/interopR4/client.rb
- test/testutil.rb
- test/sm11
- test/sm11/driver.rb
- test/sm11/servant.rb
- test/sm11/classDef.rb
- test/sm11/server.rb
- test/sm11/client.rb
- test/xsd
- test/xsd/xmlschema.xml
- test/xsd/test_xsd.rb
- test/xsd/xsd2ruby
- test/xsd/xsd2ruby/expected_mysample.rb
- test/xsd/xsd2ruby/section.xsd
- test/xsd/xsd2ruby/expected_mysample_mapper.rb
- test/xsd/xsd2ruby/test_xsd2ruby.rb
- test/xsd/xsd2ruby/expected_mysample_mapping_registry.rb
- test/xsd/test_noencoding.rb
- test/xsd/codegen
- test/xsd/codegen/test_classdef.rb
- test/xsd/noencoding.xml
- test/xsd/test_xmlschemaparser.rb
- test/xsd/test_ns.rb
- test/xsd/xmllang.xml
- test/wsdl
- test/wsdl/simpletype
- test/wsdl/simpletype/rpc
- test/wsdl/simpletype/rpc/expectedEchoVersion.rb
- test/wsdl/simpletype/rpc/test_rpc.rb
- test/wsdl/simpletype/rpc/expectedClient.rb
- test/wsdl/simpletype/rpc/expectedDriver.rb
- test/wsdl/simpletype/rpc/expectedMappingRegistry.rb
- test/wsdl/simpletype/rpc/rpc.wsdl
- test/wsdl/simpletype/rpc/expectedServant.rb
- test/wsdl/simpletype/rpc/expectedService.rb
- test/wsdl/simpletype/simpletype.wsdl
- test/wsdl/simpletype/test_simpletype.rb
- test/wsdl/abstract
- test/wsdl/abstract/test_abstract.rb
- test/wsdl/abstract/abstract.wsdl
- test/wsdl/rpc
- test/wsdl/rpc/test_rpc.rb
- test/wsdl/rpc/test_rpc_lit.rb
- test/wsdl/rpc/test-rpc-lit.wsdl
- test/wsdl/rpc/rpc.wsdl
- test/wsdl/test_multiplefault.rb
- test/wsdl/marshal
- test/wsdl/marshal/person.wsdl
- test/wsdl/marshal/test_wsdlmarshal.rb
- test/wsdl/marshal/person_org.rb
- test/wsdl/choice
- test/wsdl/choice/choice.wsdl
- test/wsdl/choice/test_choice.rb
- test/wsdl/simplecontent
- test/wsdl/simplecontent/test_simplecontent.rb
- test/wsdl/simplecontent/simplecontent.wsdl
- test/wsdl/overload
- test/wsdl/overload/overload.wsdl
- test/wsdl/overload/test_overload.rb
- test/wsdl/complexcontent
- test/wsdl/complexcontent/test_echo.rb
- test/wsdl/complexcontent/complexContent.wsdl
- test/wsdl/multiplefault.wsdl
- test/wsdl/raa
- test/wsdl/raa/README.txt
- test/wsdl/raa/RAAService.rb
- test/wsdl/raa/raa.wsdl
- test/wsdl/raa/test_raa.rb
- test/wsdl/raa/expectedDriver.rb
- test/wsdl/raa/expectedMappingRegistry.rb
- test/wsdl/raa/expectedClassDef.rb
- test/wsdl/document
- test/wsdl/document/test_rpc.rb
- test/wsdl/document/test_number.rb
- test/wsdl/document/ping_nosoapaction.wsdl
- test/wsdl/document/document.wsdl
- test/wsdl/document/number.wsdl
- test/wsdl/document/array
- test/wsdl/document/array/test_array.rb
- test/wsdl/document/array/double.wsdl
- test/wsdl/document/test_nosoapaction.rb
- test/wsdl/any
- test/wsdl/any/expectedDriver.rb
- test/wsdl/any/expectedMappingRegistry.rb
- test/wsdl/any/expectedEcho.rb
- test/wsdl/any/any.wsdl
- test/wsdl/any/test_any.rb
- test/wsdl/any/expectedService.rb
- test/wsdl/datetime
- test/wsdl/datetime/datetimeServant.rb
- test/wsdl/datetime/test_datetime.rb
- test/wsdl/datetime/datetime.wsdl
- test/wsdl/datetime/DatetimeService.rb
- test/wsdl/datetime/datetime.rb
- test/wsdl/list
- test/wsdl/list/test_list.rb
- test/wsdl/list/list.wsdl
- test/wsdl/ref
- test/wsdl/ref/product.wsdl
- test/wsdl/ref/test_ref.rb
- test/wsdl/ref/expectedDriver.rb
- test/wsdl/ref/expectedProduct.rb
- test/wsdl/test_fault.rb
- test/wsdl/emptycomplextype.wsdl
- test/wsdl/test_emptycomplextype.rb
- test/wsdl/map
- test/wsdl/map/map.xml
- test/wsdl/map/map.wsdl
- test/wsdl/map/test_map.rb
- test/wsdl/axisArray
- test/wsdl/axisArray/test_axisarray.rb
- test/wsdl/axisArray/axisArray.wsdl
- test/wsdl/qualified
- test/wsdl/qualified/np.wsdl
- test/wsdl/qualified/lp.xsd
- test/wsdl/qualified/test_qualified.rb
- test/wsdl/qualified/test_unqualified.rb
- test/wsdl/qualified/lp.wsdl
- test/wsdl/soap
- test/wsdl/soap/test_soapbodyparts.rb
- test/wsdl/soap/soapbodyparts.wsdl
- test/wsdl/soap/wsdl2ruby
- test/wsdl/soap/wsdl2ruby/expectedClassdef.rb
- test/wsdl/soap/wsdl2ruby/expectedClient.rb
- test/wsdl/soap/wsdl2ruby/section
- test/wsdl/soap/wsdl2ruby/section/test_section.rb
- test/wsdl/soap/wsdl2ruby/section/expectedClassdef.rb
- test/wsdl/soap/wsdl2ruby/section/section.xsd
- test/wsdl/soap/wsdl2ruby/expectedDriver.rb
- test/wsdl/soap/wsdl2ruby/expectedService.cgi
- test/wsdl/soap/wsdl2ruby/expectedMappingRegistry.rb
- test/wsdl/soap/wsdl2ruby/soapenc
- test/wsdl/soap/wsdl2ruby/soapenc/soapenc.wsdl
- test/wsdl/soap/wsdl2ruby/soapenc/test_soapenc.rb
- test/wsdl/soap/wsdl2ruby/rpc.wsdl
- test/wsdl/soap/wsdl2ruby/expectedServant.rb
- test/wsdl/soap/wsdl2ruby/expectedService.rb
- test/wsdl/soap/wsdl2ruby/test_wsdl2ruby.rb
- test/wsdl/anonymous
- test/wsdl/anonymous/test_anonymous.rb
- test/wsdl/anonymous/expectedDriver.rb
- test/wsdl/anonymous/expectedMappingRegistry.rb
- test/wsdl/anonymous/expectedClassDef.rb
- test/wsdl/anonymous/lp.wsdl
- test/wsdl/oneway
- test/wsdl/oneway/test_oneway.rb
- test/wsdl/oneway/oneway.wsdl
- test/wsdl/group
- test/wsdl/group/expectedClassdef.rb
- test/wsdl/group/test_rpc.rb
- test/wsdl/group/group.wsdl
- test/wsdl/group/expectedDriver.rb
- test/wsdl/group/expectedMappingRegistry.rb
- test/wsdl/soaptype
- test/wsdl/soaptype/soaptype.wsdl
- test/wsdl/soaptype/test_soaptype.rb
- test/wsdl/fault
- test/wsdl/fault/test_multifault.rb
- test/wsdl/fault/test_fault.rb
- test/wsdl/fault/multifault.wsdl
- test/wsdl/fault/fault.wsdl
- test/runner.rb
- test/interopR2
- test/interopR2/clientBEAWebLogic.rb
- test/interopR2/client4S4C2.rb
- test/interopR2/client.log
- test/interopR2/rwikiInteropService.rb
- test/interopR2/InteropTest.wsdl
- test/interopR2/clientJSOAP.rb
- test/interopR2/clientMSSOAPToolkit2.0.rb
- test/interopR2/clientOpenLink.rb
- test/interopR2/clientSilverStream.rb
- test/interopR2/clientSIMACE.rb
- test/interopR2/clientMSSOAPToolkit3.0.rb
- test/interopR2/InteropTestDriver.rb
- test/interopR2/clientApacheAxis.rb
- test/interopR2/SOAPBuildersInterop_R2GrB.wsdl
- test/interopR2/clientWASP.rb
- test/interopR2/InteropTestC.wsdl
- test/interopR2/clientEasySoap.rb
- test/interopR2/README.txt
- test/interopR2/interopService.rb
- test/interopR2/clientGLUE.rb
- test/interopR2/SOAPBuildersInterop_R2.wsdl
- test/interopR2/clientBase.rb
- test/interopR2/clientWASPC.rb
- test/interopR2/clientSpray2001.rb
- test/interopR2/clientASP.NET.rb
- test/interopR2/clientNuSOAP.rb
- test/interopR2/clientCapeConnect.rb
- test/interopR2/clientZSI.rb
- test/interopR2/clientSQLData.rb
- test/interopR2/groupc.wsdl
- test/interopR2/clientJAX-RPC.rb
- test/interopR2/iSimonReg.rb
- test/interopR2/clientWhiteMesa.rb
- test/interopR2/clienteSOAP.rb
- test/interopR2/SOAP4R_SOAPBuildersInteropTest_R2GroupB.wsdl
- test/interopR2/clientOracle.rb
- test/interopR2/clientSun.rb
- test/interopR2/clientHP.rb
- test/interopR2/clientgSOAP.rb
- test/interopR2/simonReg.rb
- test/interopR2/clientDelphi.rb
- test/interopR2/clientXMLRPC-EPI.rb
- test/interopR2/client4S4C.rb
- test/interopR2/clientPhalanx.rb
- test/interopR2/server.cgi
- test/interopR2/clientPEAR.rb
- test/interopR2/clientkSOAP.rb
- test/interopR2/SOAP4R_SOAPBuildersInteropTest_R2.wsdl
- test/interopR2/clientVWOpentalkSoap.rb
- test/interopR2/clientSOAP4R.rb
- test/interopR2/clientNuWave.rb
- test/interopR2/clientKafkaXSLT.rb
- test/interopR2/InteropTest.rb
- test/interopR2/server.rb
- test/interopR2/interopResultBase.rb
- test/interopR2/clientXSOAP.rb
- test/interopR2/clientFrontier.rb
- test/interopR2/client.rb
- test/interopR2/test.sh
- test/interopR2/SOAP4R_SOAPBuildersInteropTest_R2GroupCClient.rb
- test/interopR2/clientWebMethods.rb
- test/interopR2/clientWingfoot.rb
- test/interopR2/SOAPBuildersInterop_R2GrC.wsdl
- test/interopR2/base.rb
- test/interopR2/clientXMLBus.rb
- test/interopR2/client.NetRemoting.rb
- test/interopR2/clientSOAP__Lite.rb
- test/interopR2/clientApacheSOAP.rb
- test/soap
- test/soap/ssl
- test/soap/ssl/client.cert
- test/soap/ssl/server.cert
- test/soap/ssl/README
- test/soap/ssl/test_ssl.rb
- test/soap/ssl/sslsvr.rb
- test/soap/ssl/server.key
- test/soap/ssl/ca.cert
- test/soap/ssl/client.key
- test/soap/ssl/subca.cert
- test/soap/filter
- test/soap/filter/test_filter.rb
- test/soap/marshal
- test/soap/marshal/test_digraph.rb
- test/soap/marshal/test_struct.rb
- test/soap/marshal/test_marshal.rb
- test/soap/marshal/marshaltestlib.rb
- test/soap/test_generator.rb
- test/soap/test_nestedexception.rb
- test/soap/helloworld
- test/soap/helloworld/test_helloworld.rb
- test/soap/helloworld/hw_s.rb
- test/soap/test_basetype.rb
- test/soap/test_custommap.rb
- test/soap/calc
- test/soap/calc/test_calc2.rb
- test/soap/calc/server2.rb
- test/soap/calc/test_calc_cgi.rb
- test/soap/calc/calc.rb
- test/soap/calc/calc2.rb
- test/soap/calc/server.cgi
- test/soap/calc/test_calc.rb
- test/soap/calc/server.rb
- test/soap/test_empty.rb
- test/soap/test_extraattr.rb
- test/soap/test_response_as_xml.rb
- test/soap/wsdlDriver
- test/soap/wsdlDriver/test_document.rb
- test/soap/wsdlDriver/README.txt
- test/soap/wsdlDriver/simpletype.wsdl
- test/soap/wsdlDriver/calc.wsdl
- test/soap/wsdlDriver/echo_version.rb
- test/soap/wsdlDriver/document.wsdl
- test/soap/wsdlDriver/test_calc.rb
- test/soap/wsdlDriver/test_simpletype.rb
- test/soap/test_soapelement.rb
- test/soap/test_httpconfigloader.rb
- test/soap/test_envelopenamespace.rb
- test/soap/struct
- test/soap/struct/test_struct.rb
- test/soap/test_nil.rb
- test/soap/case
- test/soap/case/test_mapping.rb
- test/soap/test_cookie.rb
- test/soap/test_property.rb
- test/soap/test_streamhandler.rb
- test/soap/header
- test/soap/header/test_simplehandler.rb
- test/soap/header/test_authheader.rb
- test/soap/header/server.cgi
- test/soap/header/test_authheader_cgi.rb
- test/soap/asp.net
- test/soap/asp.net/hello.wsdl
- test/soap/asp.net/test_aspdotnet.rb
- test/soap/styleuse
- test/soap/styleuse/server.rb
- test/soap/styleuse/client.rb
- test/soap/test_styleuse.rb
- test/soap/test_no_indent.rb
- test/soap/test_custom_ns.rb
- test/soap/htpasswd
- test/soap/literalArrayMapping
- test/soap/literalArrayMapping/amazonEcDriver.rb
- test/soap/literalArrayMapping/amazonresponse.xml
- test/soap/literalArrayMapping/amazonEc.rb
- test/soap/literalArrayMapping/test_definedarray.rb
- test/soap/auth
- test/soap/auth/htdigest
- test/soap/auth/test_basic.rb
- test/soap/auth/htpasswd
- test/soap/auth/test_digest.rb
- test/soap/swa
- test/soap/swa/test_file.rb
- test/soap/fault
- test/soap/fault/test_soaparray.rb
- test/soap/fault/test_customfault.rb
- test/soap/test_mapping.rb
- test/16runner.rb
has_rdoc: true
homepage: http://dev.ctor.org/soap4r
licenses: []
post_install_message:
rdoc_options: []
require_paths:
- lib
required_ruby_version: !ruby/object:Gem::Requirement
requirements:
- - ">"
- !ruby/object:Gem::Version
version: 0.0.0
version:
required_rubygems_version: !ruby/object:Gem::Requirement
requirements:
- - ">="
- !ruby/object:Gem::Version
version: "0"
version:
requirements: []
rubyforge_project:
rubygems_version: 1.3.5
signing_key:
specification_version: 1
summary: An implementation of SOAP 1.1 for Ruby.
test_files:
- test/runner.rb

View file

@ -0,0 +1,108 @@
# soap/attachment.rb: SOAP4R - SwA implementation.
# Copyright (C) 2000-2007 NAKAMURA, Hiroshi <nahi@ruby-lang.org>.
# This program is copyrighted free software by NAKAMURA, Hiroshi. You can
# redistribute it and/or modify it under the same terms of Ruby's license;
# either the dual license version in 2003, or any later version.
require 'soap/baseData'
require 'soap/mapping'
module SOAP
class SOAPAttachment < SOAPExternalReference
attr_reader :data
def initialize(value)
super()
@data = value
end
private
def external_contentid
@data.contentid
end
end
class Attachment
attr_reader :io
attr_accessor :contenttype
def initialize(string_or_readable = nil)
@string_or_readable = string_or_readable
@contenttype = "application/octet-stream"
@contentid = nil
@content = nil
end
def contentid
@contentid ||= Attachment.contentid(self)
end
def contentid=(contentid)
@contentid = contentid
end
def mime_contentid
'<' + contentid + '>'
end
def content
if @content == nil and @string_or_readable != nil
@content = @string_or_readable.respond_to?(:read) ?
@string_or_readable.read : @string_or_readable
end
@content
end
def to_s
content
end
def write(out)
out.write(content)
end
def save(filename)
File.open(filename, "wb") do |f|
write(f)
end
end
def self.contentid(obj)
# this needs to be fixed
[obj.__id__.to_s, Process.pid.to_s].join('.')
end
def self.mime_contentid(obj)
'<' + contentid(obj) + '>'
end
end
module Mapping
class AttachmentFactory < SOAP::Mapping::Factory
def obj2soap(soap_class, obj, info, map)
soap_obj = soap_class.new(obj)
mark_marshalled_obj(obj, soap_obj)
soap_obj
end
def soap2obj(obj_class, node, info, map)
obj = node.data
mark_unmarshalled_obj(node, obj)
return true, obj
end
end
DefaultRegistry.add(::SOAP::Attachment, ::SOAP::SOAPAttachment,
AttachmentFactory.new, nil)
end
end

View file

@ -0,0 +1,34 @@
# SOAP4R - attribute proxy interface.
# Copyright (C) 2000-2007 NAKAMURA, Hiroshi <nahi@ruby-lang.org>.
# This program is copyrighted free software by NAKAMURA, Hiroshi. You can
# redistribute it and/or modify it under the same terms of Ruby's license;
# either the dual license version in 2003, or any later version.
module SOAP
module AttrProxy
def self.included(klass)
klass.extend(AttrProxyClassSupport)
end
module AttrProxyClassSupport
def attr_proxy(symbol, assignable = false)
name = symbol.to_s
define_method(name) {
attrproxy.__send__(name)
}
if assignable
aname = name + '='
define_method(aname) { |rhs|
attrproxy.__send__(aname, rhs)
}
end
end
end
end
end

File diff suppressed because it is too large Load diff

View file

@ -0,0 +1,277 @@
# SOAP4R - SOAP elements library
# Copyright (C) 2000-2007 NAKAMURA, Hiroshi <nahi@ruby-lang.org>.
# This program is copyrighted free software by NAKAMURA, Hiroshi. You can
# redistribute it and/or modify it under the same terms of Ruby's license;
# either the dual license version in 2003, or any later version.
require 'xsd/qname'
require 'soap/baseData'
module SOAP
###
## SOAP elements
#
module SOAPEnvelopeElement; end
class SOAPFault < SOAPStruct
include SOAPEnvelopeElement
include SOAPCompoundtype
public
def faultcode
self['faultcode']
end
def faultstring
self['faultstring']
end
def faultactor
self['faultactor']
end
def detail
self['detail']
end
def faultcode=(rhs)
self['faultcode'] = rhs
end
def faultstring=(rhs)
self['faultstring'] = rhs
end
def faultactor=(rhs)
self['faultactor'] = rhs
end
def detail=(rhs)
self['detail'] = rhs
end
def initialize(faultcode = nil, faultstring = nil, faultactor = nil, detail = nil)
super(EleFaultName)
@elename = EleFaultName
@encodingstyle = EncodingNamespace
if faultcode
self.faultcode = faultcode
self.faultstring = faultstring
self.faultactor = faultactor
self.detail = detail
self.faultcode.elename = EleFaultCodeName if self.faultcode
self.faultstring.elename = EleFaultStringName if self.faultstring
self.faultactor.elename = EleFaultActorName if self.faultactor
self.detail.elename = EleFaultDetailName if self.detail
end
faultcode.parent = self if faultcode
faultstring.parent = self if faultstring
faultactor.parent = self if faultactor
detail.parent = self if detail
end
def encode(generator, ns, attrs = {})
Generator.assign_ns(attrs, ns, EnvelopeNamespace)
Generator.assign_ns(attrs, ns, EncodingNamespace)
attrs[ns.name(AttrEncodingStyleName)] = EncodingNamespace
name = ns.name(@elename)
generator.encode_tag(name, attrs)
yield(self.faultcode)
yield(self.faultstring)
yield(self.faultactor)
yield(self.detail) if self.detail
generator.encode_tag_end(name, true)
end
end
class SOAPBody < SOAPStruct
include SOAPEnvelopeElement
attr_reader :is_fault
def initialize(data = nil, is_fault = false)
super(nil)
@elename = EleBodyName
@encodingstyle = nil
if data
if data.respond_to?(:to_xmlpart)
data = SOAP::SOAPRawData.new(data)
elsif defined?(::REXML) and data.is_a?(::REXML::Element)
data = SOAP::SOAPRawData.new(SOAP::SOAPREXMLElementWrap.new(data))
end
if data.respond_to?(:elename)
add(data.elename.name, data)
else
data.to_a.each do |datum|
add(datum.elename.name, datum)
end
end
end
@is_fault = is_fault
end
def encode(generator, ns, attrs = {})
name = ns.name(@elename)
generator.encode_tag(name, attrs)
@data.each do |data|
yield(data)
end
generator.encode_tag_end(name, @data.size > 0)
end
def root_node
@data.each do |node|
if node.root == 1
return node
end
end
# No specified root...
@data.each do |node|
if node.root != 0
return node
end
end
raise Parser::FormatDecodeError.new('no root element')
end
end
class SOAPHeaderItem < XSD::NSDBase
include SOAPEnvelopeElement
include SOAPCompoundtype
public
attr_accessor :element
attr_accessor :mustunderstand
attr_accessor :encodingstyle
attr_accessor :actor
def initialize(element, mustunderstand = true, encodingstyle = nil, actor = nil)
super()
@type = nil
@element = element
@mustunderstand = mustunderstand
@encodingstyle = encodingstyle
@actor = actor
element.parent = self if element
element.qualified = true
end
def encode(generator, ns, attrs = {})
attrs.each do |key, value|
@element.extraattr[key] = value
end
# to remove mustUnderstand attribute, set it to nil
unless @mustunderstand.nil?
@element.extraattr[AttrMustUnderstandName] = (@mustunderstand ? '1' : '0')
end
if @encodingstyle
@element.extraattr[AttrEncodingStyleName] = @encodingstyle
end
unless @element.encodingstyle
@element.encodingstyle = @encodingstyle
end
if @actor
@element.extraattr[AttrActorName] = @actor
end
yield(@element)
end
end
class SOAPHeader < SOAPStruct
include SOAPEnvelopeElement
attr_writer :force_encode
def initialize
super(nil)
@elename = EleHeaderName
@encodingstyle = nil
@force_encode = false
end
def encode(generator, ns, attrs = {})
name = ns.name(@elename)
generator.encode_tag(name, attrs)
@data.each do |data|
yield(data)
end
generator.encode_tag_end(name, @data.size > 0)
end
def add(name, value)
actor = value.extraattr[AttrActorName]
mu = value.extraattr[AttrMustUnderstandName]
encstyle = value.extraattr[AttrEncodingStyleName]
mu_value = mu.nil? ? nil : (mu == '1')
# to remove mustUnderstand attribute, set it to nil
item = SOAPHeaderItem.new(value, mu_value, encstyle, actor)
super(name, item)
end
def length
@data.length
end
alias size length
def encode?
@force_encode or length > 0
end
end
class SOAPEnvelope < XSD::NSDBase
include SOAPEnvelopeElement
include SOAPCompoundtype
attr_reader :header
attr_reader :body
attr_reader :external_content
def initialize(header = nil, body = nil)
super()
@type = nil
@elename = EleEnvelopeName
@encodingstyle = nil
@header = header
@body = body
@external_content = {}
header.parent = self if header
body.parent = self if body
end
def header=(header)
header.parent = self
@header = header
end
def body=(body)
body.parent = self
@body = body
end
def encode(generator, ns, attrs = {})
Generator.assign_ns(attrs, ns, elename.namespace)
name = ns.name(@elename)
generator.encode_tag(name, attrs)
yield(@header) if @header and @header.encode?
yield(@body)
generator.encode_tag_end(name, true)
end
def to_ary
[header, body]
end
end
end

View file

@ -0,0 +1,207 @@
# SOAP4R - ASP.NET EncodingStyle handler library
# Copyright (C) 2000-2007 NAKAMURA, Hiroshi <nahi@ruby-lang.org>.
# This program is copyrighted free software by NAKAMURA, Hiroshi. You can
# redistribute it and/or modify it under the same terms of Ruby's license;
# either the dual license version in 2003, or any later version.
require 'soap/encodingstyle/handler'
module SOAP
module EncodingStyle
class ASPDotNetHandler < Handler
Namespace = 'http://tempuri.org/ASP.NET'
add_handler
def initialize(charset = nil)
super(charset)
@textbuf = ''
@decode_typemap = nil
end
###
## encode interface.
#
def encode_data(generator, ns, data, parent)
attrs = {}
# ASPDotNetHandler is intended to be used for accessing an ASP.NET doc/lit
# service as an rpc/encoded service. in the situation, local elements
# should be qualified. propagate parent's namespace to children.
if data.elename.namespace.nil?
data.elename =
XSD::QName.new(parent.elename.namespace, data.elename.name)
end
name = generator.encode_name(ns, data, attrs)
case data
when SOAPRawString
generator.encode_tag(name, attrs)
generator.encode_rawstring(data.to_s)
when XSD::XSDString
generator.encode_tag(name, attrs)
generator.encode_string(@charset ?
XSD::Charset.encoding_to_xml(data.to_s, @charset) : data.to_s)
when XSD::XSDAnySimpleType
generator.encode_tag(name, attrs)
generator.encode_string(data.to_s)
when SOAPStruct
generator.encode_tag(name, attrs)
data.each do |key, value|
generator.encode_child(ns, value, data)
end
when SOAPArray
generator.encode_tag(name, attrs)
data.traverse do |child, *rank|
data.position = nil
generator.encode_child(ns, child, data)
end
else
raise EncodingStyleError.new(
"unknown object:#{data} in this encodingStyle")
end
end
def encode_data_end(generator, ns, data, parent)
name = generator.encode_name_end(ns, data)
cr = (data.is_a?(SOAPCompoundtype) and data.have_member)
generator.encode_tag_end(name, cr)
end
###
## decode interface.
#
class SOAPTemporalObject
attr_accessor :parent
def initialize
@parent = nil
end
end
class SOAPUnknown < SOAPTemporalObject
def initialize(handler, elename)
super()
@handler = handler
@elename = elename
end
def as_struct
o = SOAPStruct.decode(@elename, XSD::AnyTypeName)
o.parent = @parent
o.type.name = @name
@handler.decode_parent(@parent, o)
o
end
def as_string
o = SOAPString.decode(@elename)
o.parent = @parent
@handler.decode_parent(@parent, o)
o
end
end
def decode_tag(ns, elename, attrs, parent)
@textbuf = ''
o = SOAPUnknown.new(self, elename)
o.parent = parent
o
end
def decode_tag_end(ns, node)
o = node.node
if o.is_a?(SOAPUnknown)
newnode = o.as_string
# if /\A\s*\z/ =~ @textbuf
# o.as_struct
# else
# o.as_string
# end
node.replace_node(newnode)
o = node.node
end
decode_textbuf(o)
@textbuf = ''
end
def decode_text(ns, text)
# @textbuf is set at decode_tag_end.
@textbuf << text
end
def decode_prologue
end
def decode_epilogue
end
def decode_parent(parent, node)
case parent.node
when SOAPUnknown
newparent = parent.node.as_struct
node.parent = newparent
parent.replace_node(newparent)
decode_parent(parent, node)
when SOAPStruct
data = parent.node[node.elename.name]
case data
when nil
parent.node.add(node.elename.name, node)
when SOAPArray
name, type_ns = node.elename.name, node.type.namespace
data.add(node)
node.elename, node.type.namespace = name, type_ns
else
parent.node[node.elename.name] = SOAPArray.new
name, type_ns = data.elename.name, data.type.namespace
parent.node[node.elename.name].add(data)
data.elename.name, data.type.namespace = name, type_ns
name, type_ns = node.elename.name, node.type.namespace
parent.node[node.elename.name].add(node)
node.elename.name, node.type.namespace = name, type_ns
end
when SOAPArray
if node.position
parent.node[*(decode_arypos(node.position))] = node
parent.node.sparse = true
else
parent.node.add(node)
end
when SOAPBasetype
raise EncodingStyleError.new("SOAP base type must not have a child")
else
# SOAPUnknown does not have parent.
# raise EncodingStyleError.new("illegal parent: #{parent}")
end
end
private
def decode_textbuf(node)
if node.is_a?(XSD::XSDString)
if @charset
node.set(XSD::Charset.encoding_from_xml(@textbuf, @charset))
else
node.set(@textbuf)
end
else
# Nothing to do...
end
end
end
ASPDotNetHandler.new
end
end

View file

@ -0,0 +1,120 @@
# SOAP4R - EncodingStyle handler library
# Copyright (C) 2000-2007 NAKAMURA, Hiroshi <nahi@ruby-lang.org>.
# This program is copyrighted free software by NAKAMURA, Hiroshi. You can
# redistribute it and/or modify it under the same terms of Ruby's license;
# either the dual license version in 2003, or any later version.
require 'soap/soap'
require 'soap/baseData'
require 'soap/element'
module SOAP
module EncodingStyle
class Handler
@@handlers = {}
class EncodingStyleError < Error; end
class << self
def uri
self::Namespace
end
def handler(uri)
@@handlers[uri]
end
def each
@@handlers.each do |key, value|
yield(value)
end
end
private
def add_handler
@@handlers[self.uri] = self
end
end
attr_reader :charset
attr_accessor :generate_explicit_type
def decode_typemap=(definedtypes)
@decode_typemap = definedtypes
end
def initialize(charset)
@charset = charset
@generate_explicit_type = true
@decode_typemap = nil
end
###
## encode interface.
#
# Returns a XML instance as a string.
def encode_data(generator, ns, data, parent)
raise NotImplementError
end
def encode_data_end(generator, ns, data, parent)
raise NotImplementError
end
def encode_prologue
end
def encode_epilogue
end
###
## decode interface.
#
# Returns SOAP/OM data.
def decode_tag(ns, name, attrs, parent)
raise NotImplementError
end
def decode_tag_end(ns, name)
raise NotImplementError
end
def decode_text(ns, text)
raise NotImplementError
end
def decode_prologue
end
def decode_epilogue
end
def encode_attr_key(attrs, ns, qname)
if qname.namespace.nil?
qname.name
else
unless ns.assigned_as_tagged?(qname.namespace)
Generator.assign_ns!(attrs, ns, qname.namespace)
end
ns.name_attr(qname)
end
end
def encode_qname(attrs, ns, qname)
if qname.namespace.nil?
qname.name
else
Generator.assign_ns(attrs, ns, qname.namespace)
ns.name(qname)
end
end
end
end
end

View file

@ -0,0 +1,195 @@
# SOAP4R - XML Literal EncodingStyle handler library
# Copyright (C) 2000-2007 NAKAMURA, Hiroshi <nahi@ruby-lang.org>.
# This program is copyrighted free software by NAKAMURA, Hiroshi. You can
# redistribute it and/or modify it under the same terms of Ruby's license;
# either the dual license version in 2003, or any later version.
require 'soap/encodingstyle/handler'
module SOAP
module EncodingStyle
class LiteralHandler < Handler
Namespace = SOAP::LiteralNamespace
add_handler
def initialize(charset = nil)
super(charset)
@textbuf = []
end
###
## encode interface.
#
def encode_data(generator, ns, data, parent)
attrs = {}
name = generator.encode_name(ns, data, attrs)
if data.type and data.type.name and
(@generate_explicit_type or data.force_typed)
data.extraattr[XSD::AttrTypeName] = data.type
end
data.extraattr.each do |key, value|
keytag = key
if key.is_a?(XSD::QName)
keytag = encode_attr_key(attrs, ns, key)
end
if value.is_a?(XSD::QName)
value = encode_qname(attrs, ns, value)
end
attrs[keytag] = value
end
case data
when SOAPExternalReference
# do not encode SOAPExternalReference in
# literalHandler (which is used for literal service)
data.referred
when SOAPRawString
generator.encode_tag(name, attrs)
generator.encode_rawstring(data.to_s)
when XSD::XSDString
generator.encode_tag(name, attrs)
str = decode_str(data.to_s)
generator.encode_string(str)
when XSD::XSDAnySimpleType
generator.encode_tag(name, attrs)
generator.encode_string(data.to_s)
when SOAPStruct
generator.encode_tag(name, attrs)
data.each do |key, value|
generator.encode_child(ns, value, data)
end
when SOAPArray
generator.encode_tag(name, attrs)
data.traverse do |child, *rank|
data.position = nil
generator.encode_child(ns, child, data)
end
when SOAPElement
unless generator.use_default_namespace
# passes 2 times for simplifying namespace definition
data.each do |key, value|
if value.elename.namespace
Generator.assign_ns(attrs, ns, value.elename.namespace)
end
end
end
if data.text and data.text.is_a?(XSD::QName)
Generator.assign_ns(attrs, ns, data.text.namespace)
end
generator.encode_tag(name, attrs)
if data.text
if data.text.is_a?(XSD::QName)
text = ns.name(data.text)
else
text = data.text
end
generator.encode_string(text)
end
data.each do |key, value|
generator.encode_child(ns, value, data)
end
else
raise EncodingStyleError.new(
"unknown object:#{data} in this encodingStyle")
end
end
def encode_data_end(generator, ns, data, parent)
# do not encode SOAPExternalReference in
# literalHandler (which is used for literal service)
return nil if data.is_a?(SOAPExternalReference)
name = generator.encode_name_end(ns, data)
cr = (data.is_a?(SOAPCompoundtype) and data.have_member)
generator.encode_tag_end(name, cr)
end
###
## decode interface.
#
def decode_tag(ns, elename, attrs, parent)
@textbuf.clear
if attrs[XSD::AttrNilName] == 'true'
o = SOAPNil.decode(elename)
else
o = SOAPElement.decode(elename)
end
if definedtype = attrs[XSD::AttrTypeName]
o.type = ns.parse(definedtype)
end
o.parent = parent
o.extraattr.update(attrs)
decode_parent(parent, o)
o
end
def decode_tag_end(ns, node)
textbufstr = @textbuf.join
@textbuf.clear
o = node.node
decode_textbuf(o, textbufstr)
end
def decode_text(ns, text)
# @textbuf is set at decode_tag_end.
@textbuf << text
end
def decode_prologue
end
def decode_epilogue
end
def decode_parent(parent, node)
return unless parent.node
case parent.node
when SOAPElement
parent.node.add(node)
node.parent = parent.node
when SOAPStruct
parent.node.add(node.elename.name, node)
node.parent = parent.node
when SOAPArray
if node.position
parent.node[*(decode_arypos(node.position))] = node
parent.node.sparse = true
else
parent.node.add(node)
end
node.parent = parent.node
else
raise EncodingStyleError.new("illegal parent: #{parent.node}")
end
end
private
def decode_textbuf(node, textbufstr)
case node
when XSD::XSDString, SOAPElement
if @charset
node.set(decode_str(textbufstr))
else
node.set(textbufstr)
end
else
# Nothing to do...
end
end
def decode_str(str)
@charset ? XSD::Charset.encoding_from_xml(str, @charset) : str
end
end
LiteralHandler.new
end
end

View file

@ -0,0 +1,559 @@
# SOAP4R - SOAP EncodingStyle handler library
# Copyright (C) 2000-2007 NAKAMURA, Hiroshi <nahi@ruby-lang.org>.
# This program is copyrighted free software by NAKAMURA, Hiroshi. You can
# redistribute it and/or modify it under the same terms of Ruby's license;
# either the dual license version in 2003, or any later version.
require 'soap/encodingstyle/handler'
require 'soap/mapping/registry'
module SOAP
module EncodingStyle
class SOAPHandler < Handler
Namespace = SOAP::EncodingNamespace
add_handler
def initialize(charset = nil)
super(charset)
@refpool = []
@idpool = []
@textbuf = []
@is_first_top_ele = true
end
###
## encode interface.
#
def encode_data(generator, ns, data, parent)
attrs = encode_attrs(generator, ns, data, parent)
if parent && parent.is_a?(SOAPArray) && parent.position
attrs[ns.name(AttrPositionName)] = "[#{parent.position.join(',')}]"
end
name = generator.encode_name(ns, data, attrs)
case data
when SOAPReference
attrs['href'] = data.refidstr
generator.encode_tag(name, attrs)
when SOAPExternalReference
data.referred
attrs['href'] = data.refidstr
generator.encode_tag(name, attrs)
when SOAPRawString
generator.encode_tag(name, attrs)
generator.encode_rawstring(data.to_s)
when XSD::XSDString
generator.encode_tag(name, attrs)
generator.encode_string(@charset ?
XSD::Charset.encoding_to_xml(data.to_s, @charset) : data.to_s)
when XSD::XSDAnySimpleType
generator.encode_tag(name, attrs)
generator.encode_string(data.to_s)
when SOAPStruct
generator.encode_tag(name, attrs)
data.each do |key, value|
generator.encode_child(ns, value, data)
end
when SOAPArray
generator.encode_tag(name, attrs)
data.traverse do |child, *rank|
data.position = data.sparse ? rank : nil
generator.encode_child(ns, child, data)
end
else
raise EncodingStyleError.new(
"unknown object:#{data} in this encodingStyle")
end
end
def encode_data_end(generator, ns, data, parent)
name = generator.encode_name_end(ns, data)
cr = (data.is_a?(SOAPCompoundtype) and data.have_member)
generator.encode_tag_end(name, cr)
end
###
## decode interface.
#
class SOAPTemporalObject
attr_accessor :parent
attr_accessor :position
attr_accessor :id
attr_accessor :root
def initialize
@parent = nil
@position = nil
@id = nil
@root = nil
end
end
class SOAPUnknown < SOAPTemporalObject
attr_reader :type
attr_accessor :definedtype
attr_reader :extraattr
def initialize(handler, elename, type, extraattr)
super()
@handler = handler
@elename = elename
@type = type
@extraattr = extraattr
@definedtype = nil
end
def as_struct
if @extraattr[XSD::AttrNilName] == 'true'
return as_nil
end
o = SOAPStruct.decode(@elename, @type)
o.id = @id
o.root = @root
o.parent = @parent
o.position = @position
o.extraattr.update(@extraattr)
@handler.decode_parent(@parent, o)
o
end
def as_string
if @extraattr[XSD::AttrNilName] == 'true'
return as_nil
end
o = SOAPString.decode(@elename)
o.id = @id
o.root = @root
o.parent = @parent
o.position = @position
o.extraattr.update(@extraattr)
@handler.decode_parent(@parent, o)
o
end
def as_nil
o = SOAPNil.decode(@elename)
o.id = @id
o.root = @root
o.parent = @parent
o.position = @position
o.extraattr.update(@extraattr)
@handler.decode_parent(@parent, o)
o
end
end
def decode_tag(ns, elename, attrs, parent)
@textbuf.clear
is_nil, type, arytype, root, offset, position, href, id =
extract_attrs(ns, attrs)
o = nil
if is_nil
o = SOAPNil.decode(elename)
elsif href
o = SOAPReference.decode(elename, href)
@refpool << o
elsif @decode_typemap
o = decode_tag_by_wsdl(ns, elename, type, parent.node, arytype, attrs)
else
o = decode_tag_by_type(ns, elename, type, parent.node, arytype, attrs)
end
if o.is_a?(SOAPArray)
if offset
o.offset = decode_arypos(offset)
o.sparse = true
else
o.sparse = false
end
end
o.parent = parent
o.id = id
o.root = root
o.position = position
unless o.is_a?(SOAPTemporalObject)
@idpool << o if o.id
decode_parent(parent, o)
end
o
end
def decode_tag_end(ns, node)
textbufstr = @textbuf.join
@textbuf.clear
o = node.node
if o.is_a?(SOAPUnknown)
newnode = if /\A\s*\z/ =~ textbufstr
o.as_struct
else
o.as_string
end
if newnode.id
@idpool << newnode
end
node.replace_node(newnode)
o = node.node
end
decode_textbuf(o, textbufstr)
# unlink definedtype
o.definedtype = nil
end
def decode_text(ns, text)
@textbuf << text
end
def decode_prologue
@refpool.clear
@idpool.clear
@is_first_top_ele = true
end
def decode_epilogue
decode_resolve_id
end
def decode_parent(parent, node)
return unless parent.node
case parent.node
when SOAPUnknown
newparent = parent.node.as_struct
node.parent = newparent
if newparent.id
@idpool << newparent
end
parent.replace_node(newparent)
decode_parent(parent, node)
when SOAPStruct
parent.node.add(node.elename.name, node)
node.parent = parent.node
when SOAPArray
if node.position
parent.node[*(decode_arypos(node.position))] = node
parent.node.sparse = true
else
parent.node.add(node)
end
node.parent = parent.node
else
raise EncodingStyleError.new("illegal parent: #{parent.node}")
end
end
private
def content_ranksize(typename)
typename.scan(/\[[\d,]*\]$/)[0]
end
def content_typename(typename)
typename.sub(/\[,*\]$/, '')
end
def create_arytype(ns, data)
XSD::QName.new(data.arytype.namespace,
content_typename(data.arytype.name) + "[#{data.size.join(',')}]")
end
def encode_attrs(generator, ns, data, parent)
attrs = {}
return attrs if data.is_a?(SOAPReference)
if !parent || parent.encodingstyle != EncodingNamespace
if @generate_explicit_type
Generator.assign_ns(attrs, ns, EnvelopeNamespace)
attrs[ns.name(AttrEncodingStyleName)] = EncodingNamespace
end
data.encodingstyle = EncodingNamespace
end
if data.is_a?(SOAPNil)
attrs[ns.name(XSD::AttrNilName)] = XSD::NilValue
elsif @generate_explicit_type
if data.type.namespace
Generator.assign_ns(attrs, ns, data.type.namespace)
end
if data.is_a?(SOAPArray)
if data.arytype.namespace
Generator.assign_ns(attrs, ns, data.arytype.namespace)
end
Generator.assign_ns(attrs, ns, EncodingNamespace)
attrs[ns.name(AttrArrayTypeName)] = ns.name(create_arytype(ns, data))
if data.type.name
attrs[ns.name(XSD::AttrTypeName)] = ns.name(data.type)
end
elsif parent && parent.is_a?(SOAPArray) && (parent.arytype == data.type)
# No need to add.
elsif !data.type.namespace
# No need to add.
else
attrs[ns.name(XSD::AttrTypeName)] = ns.name(data.type)
end
end
data.extraattr.each do |key, value|
keytag = key
if key.is_a?(XSD::QName)
keytag = encode_attr_key(attrs, ns, key)
end
if value.is_a?(XSD::QName)
value = encode_qname(attrs, ns, value)
else
value = encode_attr_value(generator, ns, key, value)
end
attrs[keytag] = value
end
if data.id
attrs['id'] = data.id
end
attrs
end
def encode_attr_value(generator, ns, qname, value)
case value
when SOAPType
ref = SOAPReference.new(value)
generator.add_reftarget(qname.name, value)
ref.refidstr
else
value.to_s
end
end
def decode_tag_by_wsdl(ns, elename, typestr, parent, arytypestr, attrs)
o = nil
if parent.class == SOAPBody
# root element: should branch by root attribute?
if @is_first_top_ele
# Unqualified name is allowed here.
@is_first_top_ele = false
type = @decode_typemap[elename] ||
@decode_typemap.find_name(elename.name)
if type
o = SOAPStruct.new(elename)
o.elename = elename
o.definedtype = type
return o
end
end
# multi-ref element.
if typestr
typename = ns.parse(typestr)
typedef = @decode_typemap[typename]
if typedef
return decode_definedtype(elename, typename, typedef, arytypestr)
end
end
return decode_tag_by_type(ns, elename, typestr, parent, arytypestr, attrs)
end
if parent.type == XSD::AnyTypeName
return decode_tag_by_type(ns, elename, typestr, parent, arytypestr, attrs)
end
# parent.definedtype == nil means the parent is SOAPUnknown. SOAPUnknown
# is generated by decode_tag_by_type when its type is anyType.
parenttype = parent.definedtype || @decode_typemap[parent.type]
unless parenttype
return decode_tag_by_type(ns, elename, typestr, parent, arytypestr, attrs)
end
definedtype_name = parenttype.child_type(elename)
if definedtype_name and (klass = TypeMap[definedtype_name])
return decode_basetype(klass, elename)
elsif definedtype_name == XSD::AnyTypeName
return decode_tag_by_type(ns, elename, typestr, parent, arytypestr, attrs)
end
if definedtype_name
typedef = @decode_typemap[definedtype_name]
else
typedef = parenttype.child_defined_complextype(elename)
end
decode_definedtype(elename, definedtype_name, typedef, arytypestr)
end
def decode_definedtype(elename, typename, typedef, arytypestr)
unless typedef
raise EncodingStyleError.new("unknown type '#{typename}'")
end
if typedef.is_a?(::WSDL::XMLSchema::SimpleType)
decode_defined_simpletype(elename, typename, typedef, arytypestr)
else
decode_defined_complextype(elename, typename, typedef, arytypestr)
end
end
def decode_basetype(klass, elename)
klass.decode(elename)
end
def decode_defined_simpletype(elename, typename, typedef, arytypestr)
if typedef.base
o = decode_basetype(TypeMap[typedef.base], elename)
o.definedtype = typedef
o
else
raise RuntimeError.new("unsupported simpleType: #{typedef}")
end
end
def decode_defined_complextype(elename, typename, typedef, arytypestr)
case typedef.compoundtype
when :TYPE_STRUCT, :TYPE_MAP
o = SOAPStruct.decode(elename, typename)
o.definedtype = typedef
return o
when :TYPE_ARRAY
expected_arytype = typedef.find_arytype
if arytypestr
actual_arytype = XSD::QName.new(expected_arytype.namespace,
content_typename(expected_arytype.name) <<
content_ranksize(arytypestr))
o = SOAPArray.decode(elename, typename, actual_arytype)
else
o = SOAPArray.new(typename, 1, expected_arytype)
o.elename = elename
end
o.definedtype = typedef
return o
when :TYPE_EMPTY
o = SOAPNil.decode(elename)
o.definedtype = typedef
return o
else
raise RuntimeError.new(
"Unknown kind of complexType: #{typedef.compoundtype}")
end
nil
end
def decode_tag_by_type(ns, elename, typestr, parent, arytypestr, attrs)
if arytypestr
type = typestr ? ns.parse(typestr) : ValueArrayName
node = SOAPArray.decode(elename, type, ns.parse(arytypestr))
node.extraattr.update(attrs)
return node
end
type = nil
if typestr
type = ns.parse(typestr)
elsif parent.is_a?(SOAPArray)
type = parent.arytype
else
# Since it's in dynamic(without any type) encoding process,
# assumes entity as its type itself.
# <SOAP-ENC:Array ...> => type Array in SOAP-ENC.
# <Country xmlns="foo"> => type Country in foo.
type = elename
end
if klass = TypeMap[type]
node = decode_basetype(klass, elename)
node.extraattr.update(attrs)
return node
end
# Unknown type... Struct or String
SOAPUnknown.new(self, elename, type, attrs)
end
def decode_textbuf(node, textbufstr)
case node
when XSD::XSDHexBinary, XSD::XSDBase64Binary
node.set_encoded(textbufstr)
when XSD::XSDString
if @charset
textbufstr = XSD::Charset.encoding_from_xml(textbufstr, @charset)
end
if node.definedtype
node.definedtype.check_lexical_format(textbufstr)
end
node.set(textbufstr)
when SOAPNil
# Nothing to do.
when SOAPBasetype
node.set(textbufstr)
else
# Nothing to do...
end
end
NilLiteralMap = {
'true' => true,
'1' => true,
'false' => false,
'0' => false
}
RootLiteralMap = {
'1' => 1,
'0' => 0
}
def extract_attrs(ns, attrs)
is_nil = NilLiteralMap[attrs[XSD::AttrNilName]]
type = attrs[XSD::AttrTypeName]
arytype = attrs[AttrArrayTypeName]
root = attrs[AttrRootName]
offset = attrs[AttrOffsetName]
position = attrs[AttrPositionName]
href = attrs[AttrHrefName]
id = attrs[AttrIdName]
if attrs.key?(Mapping::RubyIVarName)
attrs[Mapping::RubyIVarName] =
decode_ref_value(ns, attrs[Mapping::RubyIVarName])
end
return is_nil, type, arytype, root, offset, position, href, id
end
def decode_ref_value(ns, value)
if /\A#/ =~ value
o = SOAPReference.decode(nil, value)
@refpool << o
o
else
value
end
end
def decode_arypos(position)
/^\[(.+)\]$/ =~ position
$1.split(',').collect { |s| s.to_i }
end
def decode_resolve_id
count = @refpool.length # To avoid infinite loop
while !@refpool.empty? && count > 0
@refpool = @refpool.find_all { |ref|
o = @idpool.find { |item|
item.id == ref.refid
}
if o.is_a?(SOAPReference)
true # link of link.
elsif o
ref.__setobj__(o)
false
elsif o = ref.rootnode.external_content[ref.refid]
ref.__setobj__(o)
false
else
raise EncodingStyleError.new("unresolved reference: #{ref.refid}")
end
}
count -= 1
end
end
end
SOAPHandler.new
end
end

View file

@ -0,0 +1,13 @@
# SOAP4R - SOAP filter.
# Copyright (C) 2000-2007 NAKAMURA, Hiroshi <nahi@ruby-lang.org>.
# This program is copyrighted free software by NAKAMURA, Hiroshi. You can
# redistribute it and/or modify it under the same terms of Ruby's license;
# either the dual license version in 2003, or any later version.
require 'soap/filter/filterchain'
# envelope filter
require 'soap/filter/handler'
# steram filter
require 'soap/filter/streamhandler'

View file

@ -0,0 +1,51 @@
# SOAP4R - SOAP filter chain.
# Copyright (C) 2000-2007 NAKAMURA, Hiroshi <nahi@ruby-lang.org>.
# This program is copyrighted free software by NAKAMURA, Hiroshi. You can
# redistribute it and/or modify it under the same terms of Ruby's license;
# either the dual license version in 2003, or any later version.
require 'soap/filter/handler'
module SOAP
module Filter
class FilterChain
def each
@array.each do |filter|
yield filter
end
end
def reverse_each
@array.reverse_each do |filter|
yield filter
end
end
def initialize
@array = []
end
def add(filter)
@array << filter
end
alias << add
def delete(filter)
@array.delete(filter)
end
def include?(filter)
@array.include?(filter)
end
end
end
end

View file

@ -0,0 +1,31 @@
# SOAP4R - SOAP envelope filter base class.
# Copyright (C) 2000-2007 NAKAMURA, Hiroshi <nahi@ruby-lang.org>.
# This program is copyrighted free software by NAKAMURA, Hiroshi. You can
# redistribute it and/or modify it under the same terms of Ruby's license;
# either the dual license version in 2003, or any later version.
module SOAP
module Filter
class Handler
# should return envelope. opt can be updated for other filters.
def on_outbound(envelope, opt)
# do something.
envelope
end
# should return xml. opt can be updated for other filters.
def on_inbound(xml, opt)
# do something.
xml
end
end
end
end

View file

@ -0,0 +1,29 @@
# SOAP4R - SOAP stream filter base class.
# Copyright (C) 2000-2007 NAKAMURA, Hiroshi <nahi@ruby-lang.org>.
# This program is copyrighted free software by NAKAMURA, Hiroshi. You can
# redistribute it and/or modify it under the same terms of Ruby's license;
# either the dual license version in 2003, or any later version.
module SOAP
module Filter
class StreamHandler
# no returning value expected.
def on_http_outbound(req)
# do something.
end
# no returning value expected.
def on_http_inbound(req, res)
# do something.
end
end
end
end

View file

@ -0,0 +1,299 @@
# SOAP4R - SOAP XML Instance Generator library.
# Copyright (C) 2000-2007 NAKAMURA, Hiroshi <nahi@ruby-lang.org>.
# This program is copyrighted free software by NAKAMURA, Hiroshi. You can
# redistribute it and/or modify it under the same terms of Ruby's license;
# either the dual license version in 2003, or any later version.
require 'soap/soap'
require 'soap/ns'
require 'soap/baseData'
require 'soap/encodingstyle/handler'
require 'xsd/codegen/gensupport'
module SOAP
###
## CAUTION: MT-unsafe
#
class Generator
include SOAP
include XSD::CodeGen::GenSupport
class FormatEncodeError < Error; end
public
attr_accessor :charset
attr_accessor :default_encodingstyle
attr_accessor :generate_explicit_type
attr_accessor :use_numeric_character_reference
attr_accessor :use_default_namespace
def initialize(opt = {})
@reftarget = nil
@handlers = {}
@charset = opt[:charset] || XSD::Charset.xml_encoding_label
@default_encodingstyle = opt[:default_encodingstyle] || EncodingNamespace
@generate_explicit_type =
opt.key?(:generate_explicit_type) ? opt[:generate_explicit_type] : true
@use_default_namespace = opt[:use_default_namespace]
@attributeformdefault = opt[:attributeformdefault]
@use_numeric_character_reference = opt[:use_numeric_character_reference]
@indentstr = opt[:no_indent] ? '' : ' '
@buf = @indent = @curr = nil
@default_ns = opt[:default_ns]
@default_ns_tag = opt[:default_ns_tag]
end
def generate(obj, io = nil)
@buf = io || ''
@indent = ''
@encode_char_regexp = get_encode_char_regexp()
prologue
@handlers.each do |uri, handler|
handler.encode_prologue
end
ns = SOAP::NS.new
if @default_ns
@default_ns.each_ns do |default_ns, default_tag|
Generator.assign_ns(obj.extraattr, ns, default_ns, default_tag)
end
end
if @default_ns_tag
@default_ns_tag.each_ns do |default_ns, default_tag|
ns.known_tag[default_ns] = default_tag
end
end
@buf << xmldecl
encode_data(ns, obj, nil)
@handlers.each do |uri, handler|
handler.encode_epilogue
end
epilogue
@buf
end
def encode_data(ns, obj, parent)
if obj.respond_to?(:to_xmlpart)
formatted = trim_eol(obj.to_xmlpart)
formatted = trim_indent(formatted)
formatted = formatted.gsub(/^/, @indent).sub(/\n+\z/, '')
@buf << "\n#{formatted}"
return
elsif obj.is_a?(SOAPEnvelopeElement)
encode_element(ns, obj, parent)
return
end
if @reftarget && !obj.precedents.empty?
add_reftarget(obj.elename.name, obj)
ref = SOAPReference.new(obj)
ref.elename = ref.elename.dup_name(obj.elename.name)
obj.precedents.clear # Avoid cyclic delay.
obj.encodingstyle = parent.encodingstyle
# SOAPReference is encoded here.
obj = ref
end
encodingstyle = obj.encodingstyle
# Children's encodingstyle is derived from its parent.
encodingstyle ||= parent.encodingstyle if parent
obj.encodingstyle = encodingstyle
handler = find_handler(encodingstyle || @default_encodingstyle)
unless handler
raise FormatEncodeError.new("Unknown encodingStyle: #{ encodingstyle }.")
end
if !obj.elename.name
raise FormatEncodeError.new("Element name not defined: #{ obj }.")
end
handler.encode_data(self, ns, obj, parent)
handler.encode_data_end(self, ns, obj, parent)
end
def add_reftarget(name, node)
unless @reftarget
raise FormatEncodeError.new("Reftarget is not defined.")
end
@reftarget.add(name, node)
end
def encode_child(ns, child, parent)
indent_backup, @indent = @indent, @indent + @indentstr
encode_data(ns.clone_ns, child, parent)
@indent = indent_backup
end
def encode_element(ns, obj, parent)
attrs = obj.extraattr
if obj.is_a?(SOAPBody)
@reftarget = obj
obj.encode(self, ns, attrs) do |child|
indent_backup, @indent = @indent, @indent + @indentstr
encode_data(ns.clone_ns, child, obj)
@indent = indent_backup
end
@reftarget = nil
else
if obj.is_a?(SOAPEnvelope)
Generator.assign_ns(attrs, ns, XSD::InstanceNamespace)
Generator.assign_ns(attrs, ns, XSD::Namespace)
end
obj.encode(self, ns, attrs) do |child|
indent_backup, @indent = @indent, @indent + @indentstr
encode_data(ns.clone_ns, child, obj)
@indent = indent_backup
end
end
end
def encode_name(ns, data, attrs)
if element_local?(data)
data.elename.name
else
if @use_default_namespace
Generator.assign_ns(attrs, ns, data.elename.namespace, '')
else
Generator.assign_ns(attrs, ns, data.elename.namespace)
end
ns.name(data.elename)
end
end
def encode_name_end(ns, data)
if element_local?(data)
data.elename.name
else
ns.name(data.elename)
end
end
def encode_tag(elename, attrs = nil)
if attrs.nil? or attrs.empty?
@buf << "\n#{ @indent }<#{ elename }>"
return
end
ary = []
attrs.each do |key, value|
ary << %Q[#{ key }="#{ get_encoded(value.to_s) }"]
end
case ary.size
when 0
@buf << "\n#{ @indent }<#{ elename }>"
when 1
@buf << %Q[\n#{ @indent }<#{ elename } #{ ary[0] }>]
else
@buf << "\n#{ @indent }<#{ elename } " <<
ary.join("\n#{ @indent }#{ @indentstr * 2 }") <<
'>'
end
end
def encode_tag_end(elename, cr = nil)
if cr
@buf << "\n#{ @indent }</#{ elename }>"
else
@buf << "</#{ elename }>"
end
end
def encode_rawstring(str)
@buf << str
end
def encode_string(str)
@buf << get_encoded(str)
end
def element_local?(element)
element.elename.namespace.nil?
end
def self.assign_ns(attrs, ns, namespace, tag = nil)
if namespace.nil?
raise FormatEncodeError.new("empty namespace")
end
override_default_ns = (tag == '' and namespace != ns.default_namespace)
if override_default_ns or !ns.assigned?(namespace)
assign_ns!(attrs, ns, namespace, tag)
end
end
def self.assign_ns!(attrs, ns, namespace, tag = nil)
tag = ns.assign(namespace, tag)
if tag == ''
attr = 'xmlns'
else
attr = "xmlns:#{tag}"
end
attrs[attr] = namespace
end
private
def prologue
end
def epilogue
end
ENCODE_CHAR_REGEXP = {}
EncodeMap = {
'&' => '&amp;',
'<' => '&lt;',
'>' => '&gt;',
'"' => '&quot;',
'\'' => '&apos;',
"\r" => '&#xd;'
}
def get_encoded(str)
if @use_numeric_character_reference and !XSD::Charset.is_us_ascii(str)
str.gsub!(@encode_char_regexp) { |c| EncodeMap[c] }
str.unpack("U*").collect { |c|
if c == 0x9 or c == 0xa or c == 0xd or (c >= 0x20 and c <= 0x7f)
c.chr
else
sprintf("&#x%x;", c)
end
}.join
else
str.gsub(@encode_char_regexp) { |c| EncodeMap[c] }
end
end
def get_encode_char_regexp
ENCODE_CHAR_REGEXP[XSD::Charset.encoding] ||=
Regexp.new("[#{EncodeMap.keys.join}]")
end
def find_handler(encodingstyle)
unless @handlers.key?(encodingstyle)
factory = SOAP::EncodingStyle::Handler.handler(encodingstyle)
if factory
handler = factory.new(@charset)
handler.generate_explicit_type = @generate_explicit_type
handler.encode_prologue
@handlers[encodingstyle] = handler
end
end
@handlers[encodingstyle]
end
def xmldecl
if @charset
%Q[<?xml version="1.0" encoding="#{ @charset }" ?>]
else
%Q[<?xml version="1.0" ?>]
end
end
end
end

View file

@ -0,0 +1,61 @@
# SOAP4R - SOAP Header handler item
# Copyright (C) 2000-2007 NAKAMURA, Hiroshi <nahi@ruby-lang.org>.
# This program is copyrighted free software by NAKAMURA, Hiroshi. You can
# redistribute it and/or modify it under the same terms of Ruby's license;
# either the dual license version in 2003, or any later version.
require 'soap/element'
module SOAP
module Header
class Handler
attr_reader :elename
attr_accessor :mustunderstand
attr_reader :encodingstyle
attr_reader :target_actor
def initialize(elename)
@elename = elename
@mustunderstand = false
@encodingstyle = nil
@target_actor = nil
end
# Should return a SOAP/OM, a SOAPHeaderItem or nil.
def on_outbound
nil
end
# Given header is a SOAPHeaderItem or nil.
def on_inbound(header, mustunderstand = false)
# do something.
end
def on_outbound_headeritem(header)
arity = self.method(:on_outbound).arity
item = (arity == 0) ? on_outbound : on_outbound(header)
if item.nil?
nil
elsif item.is_a?(::SOAP::SOAPHeaderItem)
item.elename = @elename
item
else
item.elename = @elename
::SOAP::SOAPHeaderItem.new(item, @mustunderstand, @encodingstyle,
@target_actor)
end
end
def on_inbound_headeritem(header, item)
on_inbound(item.element, item.mustunderstand)
end
end
end
end

View file

@ -0,0 +1,70 @@
# SOAP4R - SOAP Header handler set
# Copyright (C) 2000-2007 NAKAMURA, Hiroshi <nahi@ruby-lang.org>.
# This program is copyrighted free software by NAKAMURA, Hiroshi. You can
# redistribute it and/or modify it under the same terms of Ruby's license;
# either the dual license version in 2003, or any later version.
require 'xsd/namedelements'
module SOAP
module Header
class HandlerSet
def initialize
@store = XSD::NamedElements.new
end
def dup
obj = HandlerSet.new
obj.store = @store.dup
obj
end
def add(handler)
@store << handler
end
alias << add
def delete(handler)
@store.delete(handler)
end
def include?(handler)
@store.include?(handler)
end
# returns: Array of SOAPHeaderItem
def on_outbound(header)
@store.collect { |handler|
handler.on_outbound_headeritem(header)
}.compact
end
# header: SOAPHeaderItem enumerable object
def on_inbound(header)
header.each do |name, item|
handler = @store.find { |handler|
handler.elename == item.element.elename
}
if handler
handler.on_inbound_headeritem(header, item)
elsif item.mustunderstand
raise UnhandledMustUnderstandHeaderError.new(item.element.elename.to_s)
end
end
end
protected
def store=(store)
@store = store
end
end
end
end

View file

@ -0,0 +1,47 @@
# SOAP4R - SOAP Mapping header item handler
# Copyright (C) 2000-2007 NAKAMURA, Hiroshi <nahi@ruby-lang.org>.
# This program is copyrighted free software by NAKAMURA, Hiroshi. You can
# redistribute it and/or modify it under the same terms of Ruby's license;
# either the dual license version in 2003, or any later version.
require 'soap/header/handler'
require 'soap/mapping/mapping'
module SOAP
module Header
class MappingHandler < SOAP::Header::Handler
attr_accessor :registry
def initialize(elename, registry = nil)
super(elename)
@registry = registry
end
# Should return an Object for mapping
def on_mapping_outbound
nil
end
# Given header is a mapped Object
def on_mapping_inbound(obj, mustunderstand)
end
def on_outbound
obj = on_mapping_outbound
obj ? SOAP::Mapping.obj2soap(obj, @registry, @elename) : nil
end
def on_inbound(header, mustunderstand)
obj = SOAP::Mapping.soap2obj(header, @registry)
on_mapping_inbound(obj, mustunderstand)
end
end
end
end

View file

@ -0,0 +1,44 @@
# SOAP4R - SOAP Simple header item handler
# Copyright (C) 2000-2007 NAKAMURA, Hiroshi <nahi@ruby-lang.org>.
# This program is copyrighted free software by NAKAMURA, Hiroshi. You can
# redistribute it and/or modify it under the same terms of Ruby's license;
# either the dual license version in 2003, or any later version.
require 'soap/header/handler'
require 'soap/baseData'
module SOAP
module Header
class SimpleHandler < SOAP::Header::Handler
def initialize(elename)
super(elename)
end
# Should return a Hash, String or nil.
def on_simple_outbound
nil
end
# Given header is a Hash, String or nil.
def on_simple_inbound(header, mustunderstand)
end
def on_outbound
h = on_simple_outbound
h ? SOAPElement.from_obj(h, elename.namespace) : nil
end
def on_inbound(header, mustunderstand)
h = header.respond_to?(:to_obj) ? header.to_obj : header.data
on_simple_inbound(h, mustunderstand)
end
end
end
end

View file

@ -0,0 +1,139 @@
# SOAP4R - HTTP config loader.
# Copyright (C) 2000-2007 NAKAMURA, Hiroshi <nahi@ruby-lang.org>.
# This program is copyrighted free software by NAKAMURA, Hiroshi. You can
# redistribute it and/or modify it under the same terms of Ruby's license;
# either the dual license version in 2003, or any later version.
require 'soap/property'
module SOAP
module HTTPConfigLoader
module_function
def set_options(client, options)
client.proxy = options["proxy"]
options.add_hook("proxy") do |key, value|
client.proxy = value
end
client.no_proxy = options["no_proxy"]
options.add_hook("no_proxy") do |key, value|
client.no_proxy = value
end
if client.respond_to?(:protocol_version=)
client.protocol_version = options["protocol_version"]
options.add_hook("protocol_version") do |key, value|
client.protocol_version = value
end
end
ssl_config = options["ssl_config"] ||= ::SOAP::Property.new
set_ssl_config(client, ssl_config)
ssl_config.add_hook(true) do |key, value|
set_ssl_config(client, ssl_config)
end
basic_auth = options["basic_auth"] ||= ::SOAP::Property.new
set_basic_auth(client, basic_auth)
basic_auth.add_hook do |key, value|
set_basic_auth(client, basic_auth)
end
auth = options["auth"] ||= ::SOAP::Property.new
set_auth(client, auth)
auth.add_hook do |key, value|
set_auth(client, auth)
end
options.add_hook("connect_timeout") do |key, value|
client.connect_timeout = value
end
options.add_hook("send_timeout") do |key, value|
client.send_timeout = value
end
options.add_hook("receive_timeout") do |key, value|
client.receive_timeout = value
end
end
def set_basic_auth(client, basic_auth)
basic_auth.values.each do |ele|
client.set_basic_auth(*authele_to_triplets(ele))
end
end
def set_auth(client, auth)
auth.values.each do |ele|
client.set_auth(*authele_to_triplets(ele))
end
end
def authele_to_triplets(ele)
if ele.is_a?(::Array)
url, userid, passwd = ele
else
url, userid, passwd = ele[:url], ele[:userid], ele[:password]
end
return url, userid, passwd
end
def set_ssl_config(client, ssl_config)
ssl_config.each do |key, value|
cfg = client.ssl_config
if cfg.nil?
raise NotImplementedError.new("SSL not supported")
end
case key
when 'client_cert'
cfg.client_cert = cert_from_file(value)
when 'client_key'
cfg.client_key = key_from_file(value)
when 'client_ca'
cfg.client_ca = value
when 'ca_path'
cfg.set_trust_ca(value)
when 'ca_file'
cfg.set_trust_ca(value)
when 'crl'
cfg.set_crl(value)
when 'verify_mode'
cfg.verify_mode = ssl_config_int(value)
when 'verify_depth'
cfg.verify_depth = ssl_config_int(value)
when 'options'
cfg.options = value
when 'ciphers'
cfg.ciphers = value
when 'verify_callback'
cfg.verify_callback = value
when 'cert_store'
cfg.cert_store = value
else
raise ArgumentError.new("unknown ssl_config property #{key}")
end
end
end
def ssl_config_int(value)
if value.nil? or value.to_s.empty?
nil
else
begin
Integer(value)
rescue ArgumentError
::SOAP::Property::Util.const_from_name(value.to_s)
end
end
end
def cert_from_file(filename)
OpenSSL::X509::Certificate.new(File.open(filename) { |f| f.read })
end
def key_from_file(filename)
OpenSSL::PKey::RSA.new(File.open(filename) { |f| f.read })
end
end
end

View file

@ -0,0 +1,12 @@
# SOAP4R - Ruby type mapping utility.
# Copyright (C) 2000-2007 NAKAMURA, Hiroshi <nahi@ruby-lang.org>.
# This program is copyrighted free software by NAKAMURA, Hiroshi. You can
# redistribute it and/or modify it under the same terms of Ruby's license;
# either the dual license version in 2003, or any later version.
require 'soap/mapping/mapping'
require 'soap/mapping/registry'
require 'soap/mapping/encodedregistry'
require 'soap/mapping/literalregistry'

View file

@ -0,0 +1,537 @@
# SOAP4R - encoded mapping registry.
# Copyright (C) 2000-2007 NAKAMURA, Hiroshi <nahi@ruby-lang.org>.
# This program is copyrighted free software by NAKAMURA, Hiroshi. You can
# redistribute it and/or modify it under the same terms of Ruby's license;
# either the dual license version in 2003, or any later version.
require 'soap/baseData'
require 'soap/mapping/mapping'
require 'soap/mapping/typeMap'
require 'soap/mapping/factory'
require 'soap/mapping/rubytypeFactory'
module SOAP
module Mapping
# Inner class to pass an exception.
class SOAPException
attr_reader :excn_type_name, :cause
def initialize(e)
@excn_type_name = Mapping.name2elename(e.class.to_s)
@cause = e
end
def to_e
if @cause.is_a?(::Exception)
@cause.extend(::SOAP::Mapping::MappedException)
return @cause
elsif @cause.respond_to?(:message) and @cause.respond_to?(:backtrace)
e = RuntimeError.new(@cause.message)
e.set_backtrace(@cause.backtrace)
return e
end
klass = Mapping.class_from_name(Mapping.elename2name(@excn_type_name.to_s))
if klass.nil? or not klass <= ::Exception
return RuntimeError.new(@cause.inspect)
end
obj = klass.new(@cause.message)
obj.extend(::SOAP::Mapping::MappedException)
obj
end
end
class EncodedRegistry
include TraverseSupport
include RegistrySupport
class Map
def initialize(registry)
@obj2soap = {}
@soap2obj = {}
@registry = registry
end
def obj2soap(obj)
klass = obj.class
if map = @obj2soap[klass]
map.each do |soap_class, factory, info|
ret = factory.obj2soap(soap_class, obj, info, @registry)
return ret if ret
end
end
klass.ancestors.each do |baseclass|
next if baseclass == klass
if map = @obj2soap[baseclass]
map.each do |soap_class, factory, info|
if info[:derived_class]
ret = factory.obj2soap(soap_class, obj, info, @registry)
return ret if ret
end
end
end
end
nil
end
def soap2obj(node, klass = nil)
if map = @soap2obj[node.class]
map.each do |obj_class, factory, info|
next if klass and obj_class != klass
conv, obj = factory.soap2obj(obj_class, node, info, @registry)
return true, obj if conv
end
end
return false, nil
end
# Give priority to former entry.
def init(init_map = [])
clear
init_map.reverse_each do |obj_class, soap_class, factory, info|
add(obj_class, soap_class, factory, info)
end
end
# Give priority to latter entry.
def add(obj_class, soap_class, factory, info)
info ||= {}
(@obj2soap[obj_class] ||= []).unshift([soap_class, factory, info])
(@soap2obj[soap_class] ||= []).unshift([obj_class, factory, info])
end
def clear
@obj2soap.clear
@soap2obj.clear
end
def find_mapped_soap_class(target_obj_class)
map = @obj2soap[target_obj_class]
map.empty? ? nil : map[0][1]
end
def find_mapped_obj_class(target_soap_class)
map = @soap2obj[target_soap_class]
map.empty? ? nil : map[0][0]
end
end
StringFactory = StringFactory_.new
BasetypeFactory = BasetypeFactory_.new
FixnumFactory = FixnumFactory_.new
DateTimeFactory = DateTimeFactory_.new
ArrayFactory = ArrayFactory_.new
Base64Factory = Base64Factory_.new
URIFactory = URIFactory_.new
TypedArrayFactory = TypedArrayFactory_.new
TypedStructFactory = TypedStructFactory_.new
HashFactory = HashFactory_.new
SOAPBaseMap = [
[::NilClass, ::SOAP::SOAPNil, BasetypeFactory],
[::TrueClass, ::SOAP::SOAPBoolean, BasetypeFactory],
[::FalseClass, ::SOAP::SOAPBoolean, BasetypeFactory],
[::String, ::SOAP::SOAPString, StringFactory,
{:derived_class => true}],
[::DateTime, ::SOAP::SOAPDateTime, DateTimeFactory],
[::Date, ::SOAP::SOAPDate, DateTimeFactory],
[::Time, ::SOAP::SOAPDateTime, DateTimeFactory],
[::Time, ::SOAP::SOAPTime, DateTimeFactory],
[::Float, ::SOAP::SOAPDouble, BasetypeFactory,
{:derived_class => true}],
[::Float, ::SOAP::SOAPFloat, BasetypeFactory,
{:derived_class => true}],
[::Fixnum, ::SOAP::SOAPInt, FixnumFactory],
[::Integer, ::SOAP::SOAPInt, BasetypeFactory,
{:derived_class => true}],
[::Integer, ::SOAP::SOAPLong, BasetypeFactory,
{:derived_class => true}],
[::Integer, ::SOAP::SOAPInteger, BasetypeFactory,
{:derived_class => true}],
[::Integer, ::SOAP::SOAPShort, BasetypeFactory,
{:derived_class => true}],
[::Integer, ::SOAP::SOAPByte, BasetypeFactory,
{:derived_class => true}],
[::Integer, ::SOAP::SOAPNonPositiveInteger, BasetypeFactory,
{:derived_class => true}],
[::Integer, ::SOAP::SOAPNegativeInteger, BasetypeFactory,
{:derived_class => true}],
[::Integer, ::SOAP::SOAPNonNegativeInteger, BasetypeFactory,
{:derived_class => true}],
[::Integer, ::SOAP::SOAPPositiveInteger, BasetypeFactory,
{:derived_class => true}],
[::Integer, ::SOAP::SOAPUnsignedLong, BasetypeFactory,
{:derived_class => true}],
[::Integer, ::SOAP::SOAPUnsignedInt, BasetypeFactory,
{:derived_class => true}],
[::Integer, ::SOAP::SOAPUnsignedShort, BasetypeFactory,
{:derived_class => true}],
[::Integer, ::SOAP::SOAPUnsignedByte, BasetypeFactory,
{:derived_class => true}],
[::URI::Generic, ::SOAP::SOAPAnyURI, URIFactory,
{:derived_class => true}],
[::String, ::SOAP::SOAPBase64, Base64Factory],
[::String, ::SOAP::SOAPHexBinary, Base64Factory],
[::String, ::SOAP::SOAPDecimal, BasetypeFactory],
[::String, ::SOAP::SOAPDuration, BasetypeFactory],
[::String, ::SOAP::SOAPGYearMonth, BasetypeFactory],
[::String, ::SOAP::SOAPGYear, BasetypeFactory],
[::String, ::SOAP::SOAPGMonthDay, BasetypeFactory],
[::String, ::SOAP::SOAPGDay, BasetypeFactory],
[::String, ::SOAP::SOAPGMonth, BasetypeFactory],
[::String, ::SOAP::SOAPQName, BasetypeFactory],
[::Hash, ::SOAP::SOAPArray, HashFactory,
{:derived_class => true}],
[::Hash, ::SOAP::SOAPStruct, HashFactory,
{:derived_class => true}],
[::Array, ::SOAP::SOAPArray, ArrayFactory,
{:derived_class => true}],
[::SOAP::Mapping::SOAPException,
::SOAP::SOAPStruct, TypedStructFactory,
{:type => XSD::QName.new(RubyCustomTypeNamespace, "SOAPException")}],
]
RubyOriginalMap = [
[::NilClass, ::SOAP::SOAPNil, BasetypeFactory],
[::TrueClass, ::SOAP::SOAPBoolean, BasetypeFactory],
[::FalseClass, ::SOAP::SOAPBoolean, BasetypeFactory],
[::String, ::SOAP::SOAPString, StringFactory],
[::DateTime, ::SOAP::SOAPDateTime, DateTimeFactory],
[::Date, ::SOAP::SOAPDate, DateTimeFactory],
[::Time, ::SOAP::SOAPDateTime, DateTimeFactory],
[::Time, ::SOAP::SOAPTime, DateTimeFactory],
[::Float, ::SOAP::SOAPDouble, BasetypeFactory,
{:derived_class => true}],
[::Float, ::SOAP::SOAPFloat, BasetypeFactory,
{:derived_class => true}],
[::Fixnum, ::SOAP::SOAPInt, FixnumFactory],
[::Integer, ::SOAP::SOAPInt, BasetypeFactory,
{:derived_class => true}],
[::Integer, ::SOAP::SOAPLong, BasetypeFactory,
{:derived_class => true}],
[::Integer, ::SOAP::SOAPInteger, BasetypeFactory,
{:derived_class => true}],
[::Integer, ::SOAP::SOAPShort, BasetypeFactory,
{:derived_class => true}],
[::Integer, ::SOAP::SOAPByte, BasetypeFactory,
{:derived_class => true}],
[::Integer, ::SOAP::SOAPNonPositiveInteger, BasetypeFactory,
{:derived_class => true}],
[::Integer, ::SOAP::SOAPNegativeInteger, BasetypeFactory,
{:derived_class => true}],
[::Integer, ::SOAP::SOAPNonNegativeInteger, BasetypeFactory,
{:derived_class => true}],
[::Integer, ::SOAP::SOAPPositiveInteger, BasetypeFactory,
{:derived_class => true}],
[::Integer, ::SOAP::SOAPUnsignedLong, BasetypeFactory,
{:derived_class => true}],
[::Integer, ::SOAP::SOAPUnsignedInt, BasetypeFactory,
{:derived_class => true}],
[::Integer, ::SOAP::SOAPUnsignedShort, BasetypeFactory,
{:derived_class => true}],
[::Integer, ::SOAP::SOAPUnsignedByte, BasetypeFactory,
{:derived_class => true}],
[::URI::Generic, ::SOAP::SOAPAnyURI, URIFactory,
{:derived_class => true}],
[::String, ::SOAP::SOAPBase64, Base64Factory],
[::String, ::SOAP::SOAPHexBinary, Base64Factory],
[::String, ::SOAP::SOAPDecimal, BasetypeFactory],
[::String, ::SOAP::SOAPDuration, BasetypeFactory],
[::String, ::SOAP::SOAPGYearMonth, BasetypeFactory],
[::String, ::SOAP::SOAPGYear, BasetypeFactory],
[::String, ::SOAP::SOAPGMonthDay, BasetypeFactory],
[::String, ::SOAP::SOAPGDay, BasetypeFactory],
[::String, ::SOAP::SOAPGMonth, BasetypeFactory],
[::String, ::SOAP::SOAPQName, BasetypeFactory],
[::Hash, ::SOAP::SOAPArray, HashFactory],
[::Hash, ::SOAP::SOAPStruct, HashFactory],
# Does not allow Array's subclass here.
[::Array, ::SOAP::SOAPArray, ArrayFactory],
[::SOAP::Mapping::SOAPException,
::SOAP::SOAPStruct, TypedStructFactory,
{:type => XSD::QName.new(RubyCustomTypeNamespace, "SOAPException")}],
]
attr_accessor :default_factory
attr_accessor :excn_handler_obj2soap
attr_accessor :excn_handler_soap2obj
def initialize(config = {})
super()
@config = config
@map = Map.new(self)
if @config[:allow_original_mapping]
@allow_original_mapping = true
@map.init(RubyOriginalMap)
else
@allow_original_mapping = false
@map.init(SOAPBaseMap)
end
@allow_untyped_struct = @config.key?(:allow_untyped_struct) ?
@config[:allow_untyped_struct] : true
@rubytype_factory = RubytypeFactory.new(
:allow_untyped_struct => @allow_untyped_struct,
:allow_original_mapping => @allow_original_mapping
)
@default_factory = @rubytype_factory
@excn_handler_obj2soap = nil
@excn_handler_soap2obj = nil
end
# initial mapping interface
# new interface Registry#register is defined in RegisterSupport
def add(obj_class, soap_class, factory, info = nil)
@map.add(obj_class, soap_class, factory, info)
end
alias set add
def obj2soap(obj, type_qname = nil)
soap = _obj2soap(obj, type_qname)
if @allow_original_mapping
addextend2soap(soap, obj)
end
soap
end
def soap2obj(node, klass = nil)
obj = _soap2obj(node, klass)
if @allow_original_mapping
addextend2obj(obj, node.extraattr[RubyExtendName])
addiv2obj(obj, node.extraattr[RubyIVarName])
end
obj
end
def find_mapped_soap_class(obj_class)
@map.find_mapped_soap_class(obj_class)
end
def find_mapped_obj_class(soap_class)
@map.find_mapped_obj_class(soap_class)
end
private
def _obj2soap(obj, type_qname = nil)
ret = nil
if obj.is_a?(SOAPCompoundtype)
obj.replace do |ele|
Mapping._obj2soap(ele, self)
end
return obj
elsif obj.is_a?(SOAPBasetype)
return obj
elsif type_qname && type = TypeMap[type_qname]
return base2soap(obj, type)
end
cause = nil
begin
if definition = schema_definition_from_class(obj.class)
return stubobj2soap(obj, definition)
end
ret = @map.obj2soap(obj) ||
@default_factory.obj2soap(nil, obj, nil, self)
return ret if ret
rescue MappingError
cause = $!
end
if @excn_handler_obj2soap
ret = @excn_handler_obj2soap.call(obj) { |yield_obj|
Mapping._obj2soap(yield_obj, self)
}
return ret if ret
end
raise MappingError.new("Cannot map #{ obj.class.name } to SOAP/OM.", cause)
end
# Might return nil as a mapping result.
def _soap2obj(node, klass = nil)
definition = find_node_definition(node)
if klass
klass_definition = schema_definition_from_class(klass)
if definition and (definition.class_for < klass)
klass = definition.class_for
else
definition = klass_definition
end
else
klass = definition.class_for if definition
end
if definition and node.is_a?(::SOAP::SOAPNameAccessible)
return elesoap2stubobj(node, klass, definition)
end
if node.extraattr.key?(RubyTypeName)
conv, obj = @rubytype_factory.soap2obj(nil, node, nil, self)
return obj if conv
end
conv, obj = @map.soap2obj(node)
return obj if conv
conv, obj = @default_factory.soap2obj(nil, node, nil, self)
return obj if conv
cause = nil
if @excn_handler_soap2obj
begin
return @excn_handler_soap2obj.call(node) { |yield_node|
Mapping._soap2obj(yield_node, self)
}
rescue Exception
cause = $!
end
end
raise MappingError.new("Cannot map #{ node.type } to Ruby object.", cause)
end
def addiv2obj(obj, attr)
return unless attr
vars = {}
attr.__getobj__.each do |name, value|
vars[name] = Mapping._soap2obj(value, self)
end
Mapping.set_attributes(obj, vars)
end
def addextend2obj(obj, attr)
return unless attr
attr.split(/ /).reverse_each do |mstr|
obj.extend(Mapping.module_from_name(mstr))
end
end
def addextend2soap(node, obj)
return if obj.is_a?(Symbol) or obj.is_a?(Fixnum)
list = (class << obj; self; end).ancestors - obj.class.ancestors
unless list.empty?
node.extraattr[RubyExtendName] = list.collect { |c|
name = c.name
if name.nil? or name.empty?
raise TypeError.new("singleton can't be dumped #{ obj }")
end
name
}.join(" ")
end
end
def stubobj2soap(obj, definition)
case obj
when ::Array
array2soap(obj, definition)
else
unknownstubobj2soap(obj, definition)
end
end
def array2soap(obj, definition)
return SOAPNil.new if obj.nil? # ToDo: check nillable.
eledef = definition.elements[0]
soap_obj = SOAPArray.new(ValueArrayName, 1, eledef.elename)
mark_marshalled_obj(obj, soap_obj)
obj.each do |item|
soap_obj.add(typedobj2soap(item, eledef.mapped_class))
end
soap_obj
end
def unknownstubobj2soap(obj, definition)
return SOAPNil.new if obj.nil?
if definition.elements.size == 0
ele = Mapping.obj2soap(obj)
ele.elename = definition.elename if definition.elename
ele.extraattr[XSD::AttrTypeName] = definition.type if definition.type
return ele
else
ele = SOAPStruct.new(definition.type)
mark_marshalled_obj(obj, ele)
end
definition.elements.each do |eledef|
name = eledef.elename.name
if obj.respond_to?(:each) and eledef.as_array?
obj.each do |item|
ele.add(name, typedobj2soap(item, eledef.mapped_class))
end
else
child = Mapping.get_attribute(obj, eledef.varname)
if child.respond_to?(:each) and eledef.as_array?
child.each do |item|
ele.add(name, typedobj2soap(item, eledef.mapped_class))
end
else
ele.add(name, typedobj2soap(child, eledef.mapped_class))
end
end
end
ele
end
def typedobj2soap(value, klass)
if klass and klass.include?(::SOAP::SOAPBasetype)
base2soap(value, klass)
else
Mapping._obj2soap(value, self)
end
end
def elesoap2stubobj(node, obj_class, definition)
obj = Mapping.create_empty_object(obj_class)
add_elesoap2stubobj(node, obj, definition)
obj
end
# XXX consider to merge with the method in LiteralRegistry
def add_elesoap2stubobj(node, obj, definition)
vars = {}
node.each do |name, value|
item = definition.elements.find_element(value.elename)
if item
child = soap2typedobj(value, item.mapped_class)
else
# unknown element is treated as anyType.
child = Mapping._soap2obj(value, self)
end
if item and item.as_array?
(vars[name] ||= []) << child
elsif vars.key?(name)
vars[name] = [vars[name], child].flatten
else
vars[name] = child
end
end
if obj.is_a?(::Array) and is_stubobj_elements_for_array(vars)
Array.instance_method(:replace).bind(obj).call(vars.values[0])
else
Mapping.set_attributes(obj, vars)
end
end
def soap2typedobj(value, klass)
unless klass
raise MappingError.new("unknown class: #{klass}")
end
if klass.include?(::SOAP::SOAPBasetype)
obj = base2obj(value, klass)
else
obj = Mapping._soap2obj(value, self, klass)
end
obj
end
end
Registry = EncodedRegistry
DefaultRegistry = EncodedRegistry.new
RubyOriginalRegistry = EncodedRegistry.new(:allow_original_mapping => true)
end
end

View file

@ -0,0 +1,388 @@
# SOAP4R - Mapping factory.
# Copyright (C) 2000-2007 NAKAMURA, Hiroshi <nahi@ruby-lang.org>.
# This program is copyrighted free software by NAKAMURA, Hiroshi. You can
# redistribute it and/or modify it under the same terms of Ruby's license;
# either the dual license version in 2003, or any later version.
module SOAP
module Mapping
class Factory
include TraverseSupport
def initialize
# nothing to do
end
def obj2soap(soap_class, obj, info, map)
raise NotImplementError.new
# return soap_obj
end
def soap2obj(obj_class, node, info, map)
raise NotImplementError.new
# return convert_succeeded_or_not, obj
end
def setiv2obj(obj, node, map)
return if node.nil?
if obj.is_a?(Array)
setiv2ary(obj, node, map)
else
setiv2struct(obj, node, map)
end
end
def setiv2soap(node, obj, map)
if obj.class.class_variables.include?('@@schema_element')
setdefinediv2soap(node, obj, map)
else
# should we sort instance_variables? how?
obj.instance_variables.each do |var|
name = var.to_s.sub(/^@/, '').to_sym
elename = Mapping.name2elename(name)
node.add(elename,
Mapping._obj2soap(obj.instance_variable_get(var), map))
end
end
end
private
def setdefinediv2soap(ele, obj, map)
definition = Mapping.schema_definition_classdef(obj.class)
definition.elements.each do |eledef|
child = Mapping.get_attribute(obj, eledef.varname)
# extract method
if child.nil?
value = SOAPNil.new
elsif child.is_a?(XSD::NSDBase)
value = child
else
klass = Mapping.class_from_name(eledef.type)
if klass && klass.include?(::SOAP::SOAPBasetype)
value = klass.new(child)
else
# should check klass matches an actual object?
value = Mapping._obj2soap(child, map)
end
end
ele.add(eledef.elename.name, value)
end
end
def setiv2ary(obj, node, map)
node.each do |name, value|
Array.instance_method(:<<).bind(obj).call(Mapping._soap2obj(value, map))
end
end
def setiv2struct(obj, node, map)
vars = {}
node.each do |name, value|
vars[Mapping.elename2name(name)] = Mapping._soap2obj(value, map)
end
Mapping.set_attributes(obj, vars)
end
def anonymous_class?(obj)
name = obj.class.name
name.nil? or name.empty? # 1.8 returns ""
end
end
class StringFactory_ < Factory
def initialize(allow_original_mapping = false)
super()
@allow_original_mapping = allow_original_mapping
end
def obj2soap(soap_class, obj, info, map)
if !@allow_original_mapping and !obj.instance_variables.empty?
return nil
end
begin
encoded = XSD::Charset.encoding_conv(obj, Mapping.external_ces,
XSD::Charset.encoding)
soap_obj = soap_class.new(encoded)
rescue XSD::ValueSpaceError
return nil
end
mark_marshalled_obj(obj, soap_obj)
soap_obj
end
def soap2obj(obj_class, node, info, map)
obj = Mapping.create_empty_object(obj_class)
decoded = XSD::Charset.encoding_conv(node.data, XSD::Charset.encoding,
Mapping.external_ces)
obj.replace(decoded)
mark_unmarshalled_obj(node, obj)
return true, obj
end
end
class FixnumFactory_ < Factory
def obj2soap(soap_class, obj, info, map)
soap_obj = nil
begin
soap_obj = soap_class.new(obj)
rescue XSD::ValueSpaceError
return nil
end
soap_obj
end
def soap2obj(obj_class, node, info, map)
obj = node.data
return true, obj
end
end
class BasetypeFactory_ < Factory
def initialize(allow_original_mapping = false)
super()
@allow_original_mapping = allow_original_mapping
end
def obj2soap(soap_class, obj, info, map)
if !@allow_original_mapping and !obj.instance_variables.empty?
return nil
end
soap_obj = nil
begin
soap_obj = soap_class.new(obj)
rescue XSD::ValueSpaceError
return nil
end
if @allow_original_mapping
# Basetype except String should not be multiref-ed in SOAP/1.1.
mark_marshalled_obj(obj, soap_obj)
end
soap_obj
end
def soap2obj(obj_class, node, info, map)
obj = node.data
mark_unmarshalled_obj(node, obj)
return true, obj
end
end
class DateTimeFactory_ < Factory
def initialize(allow_original_mapping = false)
super()
@allow_original_mapping = allow_original_mapping
end
def obj2soap(soap_class, obj, info, map)
if !@allow_original_mapping and
Time === obj and !obj.instance_variables.empty?
return nil
end
soap_obj = nil
begin
soap_obj = soap_class.new(obj)
rescue XSD::ValueSpaceError
return nil
end
mark_marshalled_obj(obj, soap_obj)
soap_obj
end
def soap2obj(obj_class, node, info, map)
if node.respond_to?(:to_obj)
obj = node.to_obj(obj_class)
return false if obj.nil?
mark_unmarshalled_obj(node, obj)
return true, obj
else
return false
end
end
end
class Base64Factory_ < Factory
def obj2soap(soap_class, obj, info, map)
return nil unless obj.instance_variables.empty?
soap_obj = soap_class.new(obj)
mark_marshalled_obj(obj, soap_obj) if soap_obj
soap_obj
end
def soap2obj(obj_class, node, info, map)
obj = node.string
mark_unmarshalled_obj(node, obj)
return true, obj
end
end
class URIFactory_ < Factory
def obj2soap(soap_class, obj, info, map)
soap_obj = soap_class.new(obj)
mark_marshalled_obj(obj, soap_obj) if soap_obj
soap_obj
end
def soap2obj(obj_class, node, info, map)
obj = node.data
mark_unmarshalled_obj(node, obj)
return true, obj
end
end
class ArrayFactory_ < Factory
def initialize(allow_original_mapping = false)
super()
@allow_original_mapping = allow_original_mapping
end
# [[1], [2]] is converted to Array of Array, not 2-D Array.
# To create M-D Array, you must call Mapping.ary2md.
def obj2soap(soap_class, obj, info, map)
if !@allow_original_mapping and !obj.instance_variables.empty?
return nil
end
arytype = Mapping.obj2element(obj)
if arytype.name
arytype.namespace ||= RubyTypeNamespace
else
arytype = XSD::AnyTypeName
end
soap_obj = SOAPArray.new(ValueArrayName, 1, arytype)
mark_marshalled_obj(obj, soap_obj)
obj.each do |item|
soap_obj.add(Mapping._obj2soap(item, map))
end
soap_obj
end
def soap2obj(obj_class, node, info, map)
obj = Mapping.create_empty_object(obj_class)
mark_unmarshalled_obj(node, obj)
node.soap2array(obj) do |elem|
elem ? Mapping._soap2obj(elem, map) : nil
end
return true, obj
end
end
class TypedArrayFactory_ < Factory
def initialize(allow_original_mapping = false)
super()
@allow_original_mapping = allow_original_mapping
end
def obj2soap(soap_class, obj, info, map)
if !@allow_original_mapping and !obj.instance_variables.empty?
return nil
end
arytype = info[:type] || info[0]
soap_obj = SOAPArray.new(ValueArrayName, 1, arytype)
mark_marshalled_obj(obj, soap_obj)
obj.each do |var|
soap_obj.add(Mapping._obj2soap(var, map))
end
soap_obj
end
def soap2obj(obj_class, node, info, map)
if node.rank > 1
return false
end
arytype = info[:type] || info[0]
unless node.arytype == arytype
return false
end
obj = Mapping.create_empty_object(obj_class)
mark_unmarshalled_obj(node, obj)
node.soap2array(obj) do |elem|
elem ? Mapping._soap2obj(elem, map) : nil
end
return true, obj
end
end
class TypedStructFactory_ < Factory
def obj2soap(soap_class, obj, info, map)
type = info[:type] || info[0]
soap_obj = soap_class.new(type)
mark_marshalled_obj(obj, soap_obj)
if obj.class <= SOAP::Marshallable
setiv2soap(soap_obj, obj, map)
else
# allow to serialize an instance of unmarked class
setiv2soap(soap_obj, obj, map)
end
soap_obj
end
def soap2obj(obj_class, node, info, map)
type = info[:type] || info[0]
unless node.type == type
return false
end
obj = Mapping.create_empty_object(obj_class)
mark_unmarshalled_obj(node, obj)
setiv2obj(obj, node, map)
return true, obj
end
end
MapQName = XSD::QName.new(ApacheSOAPTypeNamespace, 'Map')
class HashFactory_ < Factory
def initialize(allow_original_mapping = false)
super()
@allow_original_mapping = allow_original_mapping
end
def obj2soap(soap_class, obj, info, map)
if !@allow_original_mapping and !obj.instance_variables.empty?
return nil
end
if !obj.default.nil? or
(obj.respond_to?(:default_proc) and obj.default_proc)
return nil
end
soap_obj = SOAPStruct.new(MapQName)
mark_marshalled_obj(obj, soap_obj)
obj.each do |key, value|
elem = SOAPStruct.new
elem.add("key", Mapping._obj2soap(key, map))
elem.add("value", Mapping._obj2soap(value, map))
# ApacheAxis allows only 'item' here.
soap_obj.add("item", elem)
end
soap_obj
end
def soap2obj(obj_class, node, info, map)
unless node.type == MapQName
return false
end
if node.class == SOAPStruct and node.key?('default')
return false
end
obj = Mapping.create_empty_object(obj_class)
mark_unmarshalled_obj(node, obj)
if node.class == SOAPStruct
node.each do |key, value|
obj[Mapping._soap2obj(value['key'], map)] =
Mapping._soap2obj(value['value'], map)
end
else
node.each do |value|
obj[Mapping._soap2obj(value['key'], map)] =
Mapping._soap2obj(value['value'], map)
end
end
return true, obj
end
end
end
end

View file

@ -0,0 +1,391 @@
# SOAP4R - literal mapping registry.
# Copyright (C) 2000-2007 NAKAMURA, Hiroshi <nahi@ruby-lang.org>.
# This program is copyrighted free software by NAKAMURA, Hiroshi. You can
# redistribute it and/or modify it under the same terms of Ruby's license;
# either the dual license version in 2003, or any later version.
require 'soap/baseData'
require 'soap/mapping/mapping'
require 'soap/mapping/typeMap'
require 'xsd/codegen/gensupport'
require 'xsd/namedelements'
module SOAP
module Mapping
class LiteralRegistry
include RegistrySupport
attr_accessor :excn_handler_obj2soap
attr_accessor :excn_handler_soap2obj
def initialize
super()
@excn_handler_obj2soap = nil
@excn_handler_soap2obj = nil
end
def obj2soap(obj, qname, obj_class = nil)
soap_obj = nil
if obj.is_a?(SOAPElement)
soap_obj = obj
else
soap_obj = any2soap(obj, qname, obj_class)
end
return soap_obj if soap_obj
if @excn_handler_obj2soap
soap_obj = @excn_handler_obj2soap.call(obj) { |yield_obj|
Mapping.obj2soap(yield_obj, nil, nil, MAPPING_OPT)
}
return soap_obj if soap_obj
end
raise MappingError.new("cannot map #{obj.class.name} as #{qname}")
end
# node should be a SOAPElement
def soap2obj(node, obj_class = nil)
cause = nil
begin
return any2obj(node, obj_class)
rescue MappingError
cause = $!
end
if @excn_handler_soap2obj
begin
return @excn_handler_soap2obj.call(node) { |yield_node|
Mapping.soap2obj(yield_node, nil, nil, MAPPING_OPT)
}
rescue Exception
end
end
raise MappingError.new("cannot map #{node.elename.name}/#{node.type.name} to Ruby object", cause)
end
private
MAPPING_OPT = { :no_reference => true }
def definedobj2soap(obj, definition)
obj2soap(obj, definition.elename, definition.mapped_class)
end
def any2soap(obj, qname, obj_class)
ele = nil
if obj.is_a?(SOAP::Mapping::Object)
return mappingobj2soap(obj, qname)
end
class_definition = schema_definition_from_class(obj.class)
if class_definition.nil? and obj_class
class_definition = schema_definition_from_class(obj_class)
end
elename_definition = schema_definition_from_elename(qname)
if !class_definition and !elename_definition
# no definition found
return anyobj2soap(obj, qname)
end
if !class_definition or !elename_definition
# use found one
return stubobj2soap(obj, qname, class_definition || elename_definition)
end
# found both:
if class_definition.class_for == elename_definition.class_for
# if two definitions are for the same class, give qname a priority.
return stubobj2soap(obj, qname, elename_definition)
end
# it should be a derived class
return stubobj2soap(obj, qname, class_definition)
end
def anyobj2soap(obj, qname)
ele = nil
case obj
when Hash
ele = SOAPElement.from_obj(obj, nil)
ele.elename = qname
when Array
# treat as a list of simpletype
ele = SOAPElement.new(qname, obj.join(" "))
when XSD::QName
ele = SOAPElement.new(qname)
ele.text = obj
else
# expected to be a basetype or an anyType.
# SOAPStruct, etc. is used instead of SOAPElement.
begin
ele = Mapping.obj2soap(obj, nil, nil, MAPPING_OPT)
ele.elename = qname
rescue MappingError
ele = SOAPElement.new(qname, obj.to_s)
end
end
add_attributes2soap(obj, ele)
ele
end
def stubobj2soap(obj, qname, definition)
if obj.nil?
ele = SOAPNil.new
ele.elename = qname
elsif obj.is_a?(::String)
ele = SOAPElement.new(qname, obj)
else
ele = SOAPElement.new(qname)
end
ele.qualified = definition.qualified
if definition.type
ele.type = definition.type
if definition.basetype or Mapping.root_type_hint
Mapping.reset_root_type_hint
ele.force_typed = true
end
end
if qname.nil? and definition.elename
ele.elename = definition.elename
end
return ele if obj.nil?
stubobj2soap_elements(obj, ele, definition.elements)
add_definedattributes2soap(obj, ele, definition)
ele
end
def stubobj2soap_elements(obj, ele, definition, is_choice = false)
added = false
case definition
when SchemaSequenceDefinition, SchemaEmptyDefinition
definition.each do |eledef|
ele_added = stubobj2soap_elements(obj, ele, eledef, is_choice)
added = true if ele_added
end
when SchemaChoiceDefinition
definition.each do |eledef|
added = stubobj2soap_elements(obj, ele, eledef, true)
break if added
end
else
added = true
if definition.as_any?
any = Mapping.get_attributes_for_any(obj)
SOAPElement.from_objs(any).each do |child|
ele.add(child)
end
elsif obj.respond_to?(:each) and definition.as_array?
obj.each do |item|
ele.add(definedobj2soap(item, definition))
end
else
child = Mapping.get_attribute(obj, definition.varname)
if child.nil? and (is_choice or definition.minoccurs == 0)
added = false
else
if child.respond_to?(:each) and definition.as_array?
if child.empty?
added = false
else
child.each do |item|
ele.add(definedobj2soap(item, definition))
end
end
else
ele.add(definedobj2soap(child, definition))
end
end
end
end
added
end
def mappingobj2soap(obj, qname)
ele = SOAPElement.new(qname)
obj.__xmlele.each do |key, value|
if value.is_a?(::Array)
value.each do |item|
ele.add(obj2soap(item, key))
end
else
ele.add(obj2soap(value, key))
end
end
obj.__xmlattr.each do |key, value|
ele.extraattr[key] = value
end
ele
end
def any2obj(node, obj_class = nil)
is_compound = node.is_a?(::SOAP::SOAPCompoundtype)
# trust xsi:type first
if is_compound and node.type
definition = schema_definition_from_type(node.type)
end
# element name next
definition ||= schema_definition_from_elename(node.elename)
# class defined in parent type last
if obj_class
definition ||= schema_definition_from_class(obj_class)
end
if definition
obj_class = definition.class_for
end
if is_compound
if definition
return elesoap2stubobj(node, obj_class, definition)
elsif node.is_a?(::SOAP::SOAPNameAccessible)
return elesoap2plainobj(node)
end
end
obj = Mapping.soap2obj(node, nil, obj_class, MAPPING_OPT)
add_attributes2obj(node, obj)
obj
end
def elesoap2stubobj(node, obj_class, definition)
obj = nil
if obj_class == ::String
obj = node.text
elsif obj_class < ::String and node.respond_to?(:text)
obj = obj_class.new(node.text)
else
obj = Mapping.create_empty_object(obj_class)
add_elesoap2stubobj(node, obj, definition)
end
add_attributes2stubobj(node, obj, definition)
obj
end
def elesoap2plainobj(node)
obj = nil
if !node.have_member
obj = base2obj(node, ::SOAP::SOAPString)
else
obj = anytype2obj(node)
add_elesoap2plainobj(node, obj)
end
add_attributes2obj(node, obj)
obj
end
def anytype2obj(node)
if node.is_a?(::SOAP::SOAPBasetype)
return node.data
end
::SOAP::Mapping::Object.new
end
def add_elesoap2stubobj(node, obj, definition)
vars = {}
node.each do |name, value|
item = definition.elements.find_element(value.elename)
if item
child = elesoapchild2obj(value, item)
else
# unknown element is treated as anyType.
child = any2obj(value)
end
if item and item.as_array?
(vars[name] ||= []) << child
elsif vars.key?(name)
vars[name] = [vars[name], child].flatten
else
vars[name] = child
end
end
if obj.is_a?(::Array) and is_stubobj_elements_for_array(vars)
Array.instance_method(:replace).bind(obj).call(vars.values[0])
else
Mapping.set_attributes(obj, vars)
end
end
def elesoapchild2obj(value, eledef)
if eledef.mapped_class
if eledef.mapped_class.include?(::SOAP::SOAPBasetype)
base2obj(value, eledef.mapped_class)
else
any2obj(value, eledef.mapped_class)
end
else
child_definition = schema_definition_from_elename(eledef.elename)
if child_definition
any2obj(value, child_definition.class_for)
else
# untyped element is treated as anyType.
any2obj(value)
end
end
end
def add_attributes2stubobj(node, obj, definition)
return if obj.nil? or node.extraattr.empty?
if attributes = definition.attributes
define_xmlattr(obj)
attributes.each do |qname, class_name|
child = node.extraattr[qname]
next if child.nil?
if class_name
klass = Mapping.class_from_name(class_name)
if klass.include?(::SOAP::SOAPBasetype)
child = klass.to_data(child)
end
end
obj.__xmlattr[qname] = child
define_xmlattr_accessor(obj, qname)
end
end
end
def add_elesoap2plainobj(node, obj)
node.each do |name, value|
obj.__add_xmlele_value(value.elename, any2obj(value))
end
end
def add_attributes2obj(node, obj)
return if obj.nil? or node.extraattr.empty?
define_xmlattr(obj)
node.extraattr.each do |qname, value|
obj.__xmlattr[qname] = value
define_xmlattr_accessor(obj, qname)
end
end
# Mapping.define_attr_accessor calls define_method with proc and it exhausts
# much memory for each singleton Object. just instance_eval instead of it.
def define_xmlattr_accessor(obj, qname)
# untaint depends GenSupport.safemethodname
name = Mapping.safemethodname('xmlattr_' + qname.name).untaint
unless obj.respond_to?(name)
# untaint depends QName#dump
qnamedump = qname.dump.untaint
obj.instance_eval <<-EOS
def #{name}
@__xmlattr[#{qnamedump}]
end
def #{name}=(value)
@__xmlattr[#{qnamedump}] = value
end
EOS
end
end
# Mapping.define_attr_accessor calls define_method with proc and it exhausts
# much memory for each singleton Object. just instance_eval instead of it.
def define_xmlattr(obj)
obj.instance_variable_set('@__xmlattr', {})
unless obj.respond_to?(:__xmlattr)
obj.instance_eval <<-EOS
def __xmlattr
@__xmlattr
end
EOS
end
end
end
end
end

View file

@ -0,0 +1,577 @@
# SOAP4R - Ruby type mapping utility.
# Copyright (C) 2000-2007 NAKAMURA Hiroshi <nahi@ruby-lang.org>.
# This program is copyrighted free software by NAKAMURA, Hiroshi. You can
# redistribute it and/or modify it under the same terms of Ruby's license;
# either the dual license version in 2003, or any later version.
require 'xsd/codegen/gensupport'
require 'soap/mapping/schemadefinition'
module SOAP
module Mapping
RubyTypeNamespace = 'http://www.ruby-lang.org/xmlns/ruby/type/1.6'
RubyTypeInstanceNamespace =
'http://www.ruby-lang.org/xmlns/ruby/type-instance'
RubyCustomTypeNamespace = 'http://www.ruby-lang.org/xmlns/ruby/type/custom'
ApacheSOAPTypeNamespace = 'http://xml.apache.org/xml-soap'
module TraverseSupport
def mark_marshalled_obj(obj, soap_obj)
raise if obj.nil?
Thread.current[:SOAPMapping][:MarshalKey][obj.__id__] = soap_obj
end
def mark_unmarshalled_obj(node, obj)
return if obj.nil?
# node.id is not Object#id but SOAPReference#id
Thread.current[:SOAPMapping][:MarshalKey][node.id] = obj
end
end
EMPTY_OPT = {}.freeze
def self.obj2soap(obj, registry = nil, type = nil, opt = EMPTY_OPT)
registry ||= Mapping::DefaultRegistry
soap_obj = nil
protect_mapping(opt) do
soap_obj = _obj2soap(obj, registry, type)
end
soap_obj
end
def self.objs2soap(objs, registry = nil, types = nil, opt = EMPTY_OPT)
registry ||= Mapping::DefaultRegistry
ary = []
protect_mapping(opt) do
0.upto(objs.length - 1) do |idx|
type = types ? types[idx] : nil
soap = _obj2soap(objs[idx], registry, type)
ary << soap
end
end
ary
end
def self.soap2obj(node, registry = nil, klass = nil, opt = EMPTY_OPT)
registry ||= Mapping::DefaultRegistry
obj = nil
protect_mapping(opt) do
obj = _soap2obj(node, registry, klass)
end
obj
end
def self.ary2soap(ary, type_ns = XSD::Namespace, typename = XSD::AnyTypeLiteral, registry = nil, opt = EMPTY_OPT)
registry ||= Mapping::DefaultRegistry
type = XSD::QName.new(type_ns, typename)
soap_ary = SOAPArray.new(ValueArrayName, 1, type)
protect_mapping(opt) do
ary.each do |ele|
soap_ary.add(_obj2soap(ele, registry, type))
end
end
soap_ary
end
def self.ary2md(ary, rank, type_ns = XSD::Namespace, typename = XSD::AnyTypeLiteral, registry = nil, opt = EMPTY_OPT)
registry ||= Mapping::DefaultRegistry
type = XSD::QName.new(type_ns, typename)
md_ary = SOAPArray.new(ValueArrayName, rank, type)
protect_mapping(opt) do
add_md_ary(md_ary, ary, [], registry)
end
md_ary
end
def self.fault2exception(fault, registry = nil)
registry ||= Mapping::DefaultRegistry
detail = ""
if fault.detail
begin
fault.detail.type ||= XSD::QName::EMPTY
detail = soap2obj(fault.detail, registry) || ""
rescue MappingError
detail = fault.detail
end
end
if detail.is_a?(Mapping::SOAPException)
begin
e = detail.to_e
remote_backtrace = e.backtrace
e.set_backtrace(nil)
raise e # ruby sets current caller as local backtrace of e => e2.
rescue Exception => e
e.set_backtrace(remote_backtrace + e.backtrace[1..-1])
raise
end
else
fault.detail = detail
fault.set_backtrace(
if detail.is_a?(Array)
detail
else
[detail.to_s]
end
)
raise
end
end
def self._obj2soap(obj, registry, type = nil)
if obj.respond_to?(:to_xmlpart)
SOAPRawData.new(obj)
elsif defined?(::REXML) and obj.is_a?(::REXML::Element)
SOAPRawData.new(SOAPREXMLElementWrap.new(obj))
elsif referent = Thread.current[:SOAPMapping][:MarshalKey][obj.__id__] and
!Thread.current[:SOAPMapping][:NoReference]
SOAPReference.new(referent)
elsif registry
registry.obj2soap(obj, type)
else
raise MappingError.new("no mapping registry given")
end
end
def self._soap2obj(node, registry, klass = nil)
if node.nil?
return nil
elsif node.is_a?(SOAPReference)
target = node.__getobj__
# target.id is not Object#id but SOAPReference#id
if referent = Thread.current[:SOAPMapping][:MarshalKey][target.id] and
!Thread.current[:SOAPMapping][:NoReference]
return referent
else
return _soap2obj(target, registry, klass)
end
end
return registry.soap2obj(node, klass)
end
def self.create_empty_object(klass)
klass.allocate
end
# Allow only (Letter | '_') (Letter | Digit | '-' | '_')* here.
# Caution: '.' is not allowed here.
# To follow XML spec., it should be NCName.
# (denied chars) => .[0-F][0-F]
# ex. a.b => a.2eb
#
def self.name2elename(name)
name = name.to_s
name.gsub(/([^a-zA-Z0-9:_\-]+)/n) {
'.' << $1.unpack('H2' * $1.size).join('.')
}.gsub(/::/n, '..').to_sym
end
def self.elename2name(name)
name.gsub(/\.\./n, '::').gsub(/((?:\.[0-9a-fA-F]{2})+)/n) {
[$1.delete('.')].pack('H*')
}
end
def self.const_from_name(name, lenient = false)
const = ::Object
name.sub(/\A::/, '').split('::').each do |const_str|
if /\A[A-Z]/ =~ const_str
begin
if const.const_defined?(const_str)
const = const.const_get(const_str)
next
end
rescue NameError
end
end
if lenient
const_str = Mapping.safeconstname(const_str)
if const.const_defined?(const_str)
const = const.const_get(const_str)
next
end
end
return nil
end
const
end
def self.class_from_name(name, lenient = false)
unless lenient
const = const_from_name_nonlenient(name)
else
const = const_from_name(name, true)
end
if const.is_a?(::Class)
const
else
nil
end
end
def self.module_from_name(name, lenient = false)
unless lenient
const = const_from_name_nonlenient(name)
else
const = const_from_name(name, true)
end
if const.is_a?(::Module)
const
else
nil
end
end
def self.const_from_name_nonlenient(name)
if Thread.current[:SOAPMapping]
Thread.current[:SOAPMapping][:ConstFromName][name] ||=
const_from_name(name)
else
const_from_name(name)
end
end
def self.class2qname(klass)
name = schema_type_definition(klass)
namespace = schema_ns_definition(klass)
XSD::QName.new(namespace, name)
end
def self.class2element(klass)
name = schema_type_definition(klass) ||
Mapping.name2elename(klass.name)
namespace = schema_ns_definition(klass) || RubyCustomTypeNamespace
XSD::QName.new(namespace, name)
end
def self.obj2element(obj)
name = namespace = nil
ivars = obj.instance_variables
if ivars.include?('@schema_type')
name = obj.instance_variable_get('@schema_type')
end
if ivars.include?('@schema_ns')
namespace = obj.instance_variable_get('@schema_ns')
end
if !name or !namespace
class2qname(obj.class)
else
XSD::QName.new(namespace, name)
end
end
def self.to_qname(obj, ns = nil)
if obj.is_a?(XSD::QName)
obj
else
XSD::QName.new(ns, obj)
end
end
def self.define_singleton_method(obj, name, &block)
sclass = (class << obj; self; end)
sclass.class_eval {
define_method(name, &block)
}
end
def self.get_attributes(obj)
if obj.is_a?(::Hash)
obj
else
rs = {}
obj.instance_variables.each do |ele|
rs[ele.sub(/^@/, '')] = obj.instance_variable_get(ele)
end
rs
end
end
EMPTY_ATTRIBUTES = {}.freeze
def self.get_attributes_for_any(obj)
if obj.respond_to?(:__xmlele_any)
obj.__xmlele_any || EMPTY_ATTRIBUTES
else
get_attributes(obj)
end
end
def self.get_attribute(obj, attr_name)
case obj
when ::SOAP::Mapping::Object
return obj[attr_name]
when ::Hash
return obj[attr_name] || obj[attr_name.intern]
else
if obj.respond_to?(attr_name)
return obj.__send__(attr_name)
end
iv = obj.instance_variables
name = Mapping.safevarname(attr_name)
if iv.include?("@#{name}")
return obj.instance_variable_get("@#{name}")
elsif iv.include?("@#{attr_name}")
return obj.instance_variable_get("@#{attr_name}")
end
if obj.respond_to?(name)
return obj.__send__(name)
end
nil
end
end
def self.set_attributes(obj, values)
case obj
when ::SOAP::Mapping::Object
values.each do |attr_name, value|
obj.__add_xmlele_value(attr_name, value)
end
else
values.each do |attr_name, value|
# untaint depends GenSupport.safevarname
name = Mapping.safevarname(attr_name).untaint
setter = name + "="
if obj.respond_to?(setter)
obj.__send__(setter, value)
else
obj.instance_variable_set('@' + name, value)
begin
unless obj.respond_to?(name)
obj.instance_eval <<-EOS
def #{name}
@#{name}
end
EOS
end
unless self.respond_to?(name + "=")
obj.instance_eval <<-EOS
def #{name}=(value)
@#{name} = value
end
EOS
end
rescue TypeError
# singleton class may not exist (e.g. Float)
end
end
end
end
end
def self.safeconstname(name)
Thread.current[:SOAPMapping][:SafeConstName][name] ||=
XSD::CodeGen::GenSupport.safeconstname(name)
end
def self.safemethodname(name)
Thread.current[:SOAPMapping][:SafeMethodName][name] ||=
XSD::CodeGen::GenSupport.safemethodname(name)
end
def self.safevarname(name)
Thread.current[:SOAPMapping][:SafeVarName][name] ||=
XSD::CodeGen::GenSupport.safevarname(name)
end
def self.root_type_hint
Thread.current[:SOAPMapping][:RootTypeHint]
end
def self.reset_root_type_hint
Thread.current[:SOAPMapping][:RootTypeHint] = false
end
def self.external_ces
Thread.current[:SOAPMapping][:ExternalCES]
end
def self.schema_ns_definition(klass)
class_schema_variable(:schema_ns, klass)
end
def self.schema_name_definition(klass)
class_schema_variable(:schema_name, klass)
end
def self.schema_type_definition(klass)
class_schema_variable(:schema_type, klass)
end
def self.schema_qualified_definition(klass)
class_schema_variable(:schema_qualified, klass)
end
def self.schema_element_definition(klass)
class_schema_variable(:schema_element, klass)
end
def self.schema_attribute_definition(klass)
class_schema_variable(:schema_attribute, klass)
end
def self.schema_definition_classdef(klass)
if Thread.current[:SOAPMapping][:SchemaDefinition].key?(klass)
return Thread.current[:SOAPMapping][:SchemaDefinition][klass]
end
schema_ns = schema_ns_definition(klass)
schema_name = schema_name_definition(klass)
schema_type = schema_type_definition(klass)
qualified = schema_qualified_definition(klass)
elements = schema_element_definition(klass)
attributes = schema_attribute_definition(klass)
return nil if schema_name.nil? and schema_type.nil?
schema_name = Mapping.to_qname(schema_name, schema_ns) if schema_name
schema_type = Mapping.to_qname(schema_type, schema_ns) if schema_type
definition = create_schema_definition(klass,
:schema_name => schema_name,
:schema_type => schema_type,
:is_anonymous => false,
:schema_qualified => qualified,
:schema_element => elements,
:schema_attribute => attributes
)
Thread.current[:SOAPMapping][:SchemaDefinition][klass] = definition
definition
end
def self.create_schema_definition(klass, definition)
schema_ns = definition[:schema_ns]
schema_name = definition[:schema_name]
schema_type = definition[:schema_type]
is_anonymous = definition[:is_anonymous]
schema_basetype = definition[:schema_basetype]
schema_qualified = definition[:schema_qualified]
schema_element = definition[:schema_element]
schema_attributes = definition[:schema_attribute]
definition = SchemaDefinition.new(klass, schema_name, schema_type, is_anonymous, schema_qualified)
definition.basetype = schema_basetype
definition.attributes = schema_attributes
if schema_element
if schema_element.respond_to?(:is_concrete_definition) and
schema_element.is_concrete_definition
definition.elements = schema_element
else
default_ns = schema_name.namespace if schema_name
default_ns ||= schema_type.namespace if schema_type
definition.elements = parse_schema_definition(schema_element, default_ns)
if klass < ::Array
definition.elements.set_array
end
end
end
definition
end
# returns SchemaComplexTypeDefinition
def self.parse_schema_definition(schema_element, default_ns)
definition = nil
if schema_element[0] == :choice
schema_element.shift
definition = SchemaChoiceDefinition.new
else
definition = SchemaSequenceDefinition.new
end
schema_element.each do |ele|
element_definition = parse_schema_element_definition(ele, default_ns)
definition << element_definition
end
definition
end
# returns SchemaElementDefinition
def self.parse_schema_element_definition(schema_element, default_ns)
if schema_element[0] == :choice
parse_schema_definition(schema_element, default_ns)
elsif schema_element[0].is_a?(Array)
parse_schema_definition(schema_element, default_ns)
else
varname, info, occurrence = schema_element
mapped_class_str, elename = info
if occurrence
minoccurs, maxoccurs = occurrence
else
minoccurs, maxoccurs = 1, 1
end
as_any = as_array = false
if /\[\]$/ =~ mapped_class_str
mapped_class_str = mapped_class_str.sub(/\[\]$/, '')
if mapped_class_str.empty?
mapped_class_str = nil
end
as_array = true
end
if mapped_class_str
mapped_class = Mapping.class_from_name(mapped_class_str)
if mapped_class.nil?
warn("cannot find mapped class: #{mapped_class_str}")
end
end
if elename == XSD::AnyTypeName
as_any = true
elsif elename.nil?
elename = XSD::QName.new(default_ns, varname)
end
SchemaElementDefinition.new(
varname, mapped_class, elename, minoccurs, maxoccurs, as_any, as_array)
end
end
class << Mapping
public
def protect_threadvars(*symbols)
backup = {}
begin
symbols.each do |sym|
backup[sym] = Thread.current[sym]
end
yield
ensure
symbols.each do |sym|
Thread.current[sym] = backup[sym]
end
end
end
private
def class_schema_variable(sym, klass)
var = "@@#{sym}"
klass.class_variables.include?(var) ? klass.class_eval(var) : nil
end
def protect_mapping(opt)
protect_threadvars(:SOAPMapping) do
data = Thread.current[:SOAPMapping] = {}
data[:MarshalKey] = {}
data[:ExternalCES] = opt[:external_ces] || XSD::Charset.encoding
data[:NoReference] = opt[:no_reference]
data[:RootTypeHint] = opt[:root_type_hint]
data[:SchemaDefinition] = {}
data[:SafeConstName] = {}
data[:SafeMethodName] = {}
data[:SafeVarName] = {}
data[:ConstFromName] = {}
yield
end
end
def add_md_ary(md_ary, ary, indices, registry)
for idx in 0..(ary.size - 1)
if ary[idx].is_a?(Array)
add_md_ary(md_ary, ary[idx], indices + [idx], registry)
else
md_ary[*(indices + [idx])] = _obj2soap(ary[idx], registry)
end
end
end
end
end
end

View file

@ -0,0 +1,295 @@
# SOAP4R - Mapping registry.
# Copyright (C) 2000-2007 NAKAMURA, Hiroshi <nahi@ruby-lang.org>.
# This program is copyrighted free software by NAKAMURA, Hiroshi. You can
# redistribute it and/or modify it under the same terms of Ruby's license;
# either the dual license version in 2003, or any later version.
require 'soap/baseData'
require 'soap/mapping/mapping'
module SOAP
module Marshallable
# @@type_ns = Mapping::RubyCustomTypeNamespace
end
module Mapping
module MappedException; end
RubyTypeName = XSD::QName.new(RubyTypeInstanceNamespace, 'rubyType')
RubyExtendName = XSD::QName.new(RubyTypeInstanceNamespace, 'extends')
RubyIVarName = XSD::QName.new(RubyTypeInstanceNamespace, 'ivars')
# For anyType object: SOAP::Mapping::Object not ::Object
class Object
def initialize
@__xmlele_type = {}
@__xmlele = []
@__xmlattr = {}
end
def inspect
sprintf("#<%s:0x%x%s>", self.class.name, __id__,
@__xmlele.collect { |name, value| " #{name}=#{value.inspect}" }.join)
end
def __xmlattr
@__xmlattr
end
def __xmlele
@__xmlele
end
def [](qname)
qname = Mapping.to_qname(qname)
@__xmlele.each do |k, v|
return v if k == qname
end
# fallback
@__xmlele.each do |k, v|
return v if k.name == qname.name
end
nil
end
def []=(qname, value)
qname = Mapping.to_qname(qname)
found = false
@__xmlele.each do |pair|
if pair[0] == qname
found = true
pair[1] = value
end
end
unless found
__define_attr_accessor(qname)
@__xmlele << [qname, value]
end
@__xmlele_type[qname] = :single
end
def __add_xmlele_value(qname, value)
found = false
@__xmlele.map! do |k, v|
if k == qname
found = true
[k, __set_xmlele_value(k, v, value)]
else
[k, v]
end
end
unless found
__define_attr_accessor(qname)
@__xmlele << [qname, value]
@__xmlele_type[qname] = :single
end
value
end
def marshal_load(dumpobj)
__import(dumpobj)
end
private
# Mapping.define_attr_accessor calls define_method with proc and it exhausts
# much memory for each singleton Object. just instance_eval instead of it.
def __define_attr_accessor(qname)
# untaint depends GenSupport.safemethodname
name = Mapping.safemethodname(qname.name).untaint
# untaint depends on QName#dump
qnamedump = qname.dump.untaint
singleton = false
unless self.respond_to?(name)
singleton = true
instance_eval <<-EOS
def #{name}
self[#{qnamedump}]
end
EOS
end
unless self.respond_to?(name + "=")
singleton = true
instance_eval <<-EOS
def #{name}=(value)
self[#{qnamedump}] = value
end
EOS
end
if singleton && !self.respond_to?(:marshal_dump)
instance_eval <<-EOS
def marshal_dump
__export
end
EOS
end
end
def __set_xmlele_value(key, org, value)
case @__xmlele_type[key]
when :multi
org << value
org
when :single
@__xmlele_type[key] = :multi
[org, value]
else
raise RuntimeError.new("unknown type")
end
end
def __export
dumpobj = ::SOAP::Mapping::Object.new
dumpobj.__xmlele.replace(@__xmlele)
dumpobj.__xmlattr.replace(@__xmlattr)
dumpobj
end
def __import(dumpobj)
@__xmlele_type = {}
@__xmlele = []
@__xmlattr = {}
dumpobj.__xmlele.each do |qname, value|
__add_xmlele_value(qname, value)
end
@__xmlattr.replace(dumpobj.__xmlattr)
end
end
class MappingError < Error; end
module RegistrySupport
def initialize
super()
@class_schema_definition = {}
@class_elename_schema_definition = {}
@elename_schema_definition = {}
@type_schema_definition = {}
end
def register(definition)
obj_class = definition[:class]
definition = Mapping.create_schema_definition(obj_class, definition)
# give complexType definition a priority explicitly
if !@class_schema_definition[obj_class] or definition.type
@class_schema_definition[obj_class] = definition
end
if definition.elename and !definition.is_anonymous?
@class_elename_schema_definition[obj_class] = definition
@elename_schema_definition[definition.elename] = definition
end
if definition.type
@type_schema_definition[definition.type] = definition
end
end
def schema_definition_from_class(klass)
@class_schema_definition[klass] || Mapping.schema_definition_classdef(klass)
end
def elename_schema_definition_from_class(klass)
@class_elename_schema_definition[klass]
end
def schema_definition_from_elename(qname)
@elename_schema_definition[qname]
end
def schema_definition_from_type(type)
@type_schema_definition[type]
end
def find_node_definition(node)
schema_definition_from_type(node.type) ||
schema_definition_from_elename(node.elename) ||
find_schema_definition(node.elename.name) ||
find_schema_definition(node.type.name)
end
def find_schema_definition(name)
return nil unless name
typestr = Mapping.safeconstname(name)
obj_class = Mapping.class_from_name(typestr)
if obj_class
schema_definition_from_class(obj_class)
end
end
def add_attributes2soap(obj, ele)
if definition = Mapping.schema_definition_classdef(obj.class)
add_definedattributes2soap(obj, ele, definition)
elsif obj.respond_to?(:__xmlattr)
obj.__xmlattr.each do |key, value|
ele.extraattr[key] = value
end
end
end
def add_definedattributes2soap(obj, ele, typedef)
if typedef.attributes
typedef.attributes.each do |qname, param|
value = get_xmlattr_value(obj, qname)
ele.extraattr[qname] = value unless value.nil?
end
end
end
def get_xmlattr_value(obj, qname)
attrname = 'xmlattr_' + qname.name
value = Mapping.get_attribute(obj, attrname)
if value.nil?
attrname = Mapping.safemethodname('xmlattr_' + qname.name)
value = Mapping.get_attribute(obj, attrname)
end
value
end
def base2soap(obj, type, qualified = nil)
return SOAPNil.new if obj.nil?
soap_obj = nil
if type <= XSD::XSDString
str = XSD::Charset.encoding_conv(obj.to_s, Mapping.external_ces,
XSD::Charset.encoding)
soap_obj = type.new(str)
else
soap_obj = type.new(obj)
end
soap_obj.qualified = qualified
soap_obj
end
def base2obj(value, klass)
v = if value.respond_to?(:data)
value.data
elsif value.respond_to?(:text)
value.text
else
nil
end
if value.is_a?(klass)
v
else
klass.to_data(v)
end
end
def is_stubobj_elements_for_array(vars)
vars.keys.size == 1 and vars.values[0].is_a?(::Array)
end
end
end
end

View file

@ -0,0 +1,446 @@
# SOAP4R - Ruby type mapping factory.
# Copyright (C) 2000-2007 NAKAMURA, Hiroshi <nahi@ruby-lang.org>.
# This program is copyrighted free software by NAKAMURA, Hiroshi. You can
# redistribute it and/or modify it under the same terms of Ruby's license;
# either the dual license version in 2003, or any later version.
#require 'continuation'
module SOAP
module Mapping
class RubytypeFactory < Factory
TYPE_STRING = XSD::QName.new(RubyTypeNamespace, 'String')
TYPE_TIME = XSD::QName.new(RubyTypeNamespace, 'Time')
TYPE_ARRAY = XSD::QName.new(RubyTypeNamespace, 'Array')
TYPE_REGEXP = XSD::QName.new(RubyTypeNamespace, 'Regexp')
TYPE_RANGE = XSD::QName.new(RubyTypeNamespace, 'Range')
TYPE_CLASS = XSD::QName.new(RubyTypeNamespace, 'Class')
TYPE_MODULE = XSD::QName.new(RubyTypeNamespace, 'Module')
TYPE_SYMBOL = XSD::QName.new(RubyTypeNamespace, 'Symbol')
TYPE_STRUCT = XSD::QName.new(RubyTypeNamespace, 'Struct')
TYPE_HASH = XSD::QName.new(RubyTypeNamespace, 'Map')
def initialize(config = {})
@config = config
@allow_untyped_struct = @config.key?(:allow_untyped_struct) ?
@config[:allow_untyped_struct] : true
@allow_original_mapping = @config.key?(:allow_original_mapping) ?
@config[:allow_original_mapping] : false
@string_factory = StringFactory_.new(true)
@basetype_factory = BasetypeFactory_.new(true)
@datetime_factory = DateTimeFactory_.new(true)
@array_factory = ArrayFactory_.new(true)
@hash_factory = HashFactory_.new(true)
end
def obj2soap(soap_class, obj, info, map)
param = nil
case obj
when ::String
unless @allow_original_mapping
return nil
end
param = @string_factory.obj2soap(SOAPString, obj, info, map)
if obj.class != String
param.extraattr[RubyTypeName] = obj.class.name
end
addiv2soapattr(param, obj, map)
when ::Time
unless @allow_original_mapping
return nil
end
param = @datetime_factory.obj2soap(SOAPDateTime, obj, info, map)
if obj.class != Time
param.extraattr[RubyTypeName] = obj.class.name
end
addiv2soapattr(param, obj, map)
when ::Array
unless @allow_original_mapping
return nil
end
param = @array_factory.obj2soap(nil, obj, info, map)
if obj.class != Array
param.extraattr[RubyTypeName] = obj.class.name
end
addiv2soapattr(param, obj, map)
when ::NilClass
unless @allow_original_mapping
return nil
end
param = @basetype_factory.obj2soap(SOAPNil, obj, info, map)
addiv2soapattr(param, obj, map)
when ::FalseClass, ::TrueClass
unless @allow_original_mapping
return nil
end
param = @basetype_factory.obj2soap(SOAPBoolean, obj, info, map)
addiv2soapattr(param, obj, map)
when ::Integer
unless @allow_original_mapping
return nil
end
param = @basetype_factory.obj2soap(SOAPInt, obj, info, map)
param ||= @basetype_factory.obj2soap(SOAPInteger, obj, info, map)
param ||= @basetype_factory.obj2soap(SOAPDecimal, obj, info, map)
addiv2soapattr(param, obj, map)
when ::Float
unless @allow_original_mapping
return nil
end
param = @basetype_factory.obj2soap(SOAPDouble, obj, info, map)
if obj.class != Float
param.extraattr[RubyTypeName] = obj.class.name
end
addiv2soapattr(param, obj, map)
when ::Hash
unless @allow_original_mapping
return nil
end
if obj.respond_to?(:default_proc) && obj.default_proc
raise TypeError.new("cannot dump hash with default proc")
end
param = SOAPStruct.new(TYPE_HASH)
mark_marshalled_obj(obj, param)
if obj.class != Hash
param.extraattr[RubyTypeName] = obj.class.name
end
obj.each do |key, value|
elem = SOAPStruct.new # Undefined type.
elem.add("key", Mapping._obj2soap(key, map))
elem.add("value", Mapping._obj2soap(value, map))
param.add("item", elem)
end
param.add('default', Mapping._obj2soap(obj.default, map))
addiv2soapattr(param, obj, map)
when ::Regexp
unless @allow_original_mapping
return nil
end
param = SOAPStruct.new(TYPE_REGEXP)
mark_marshalled_obj(obj, param)
if obj.class != Regexp
param.extraattr[RubyTypeName] = obj.class.name
end
param.add('source', SOAPBase64.new(obj.source))
options = obj.options
param.add('options', SOAPInt.new(options))
addiv2soapattr(param, obj, map)
when ::Range
unless @allow_original_mapping
return nil
end
param = SOAPStruct.new(TYPE_RANGE)
mark_marshalled_obj(obj, param)
if obj.class != Range
param.extraattr[RubyTypeName] = obj.class.name
end
param.add('begin', Mapping._obj2soap(obj.begin, map))
param.add('end', Mapping._obj2soap(obj.end, map))
param.add('exclude_end', SOAP::SOAPBoolean.new(obj.exclude_end?))
addiv2soapattr(param, obj, map)
when ::Class
unless @allow_original_mapping
return nil
end
if obj.to_s[0] == ?#
raise TypeError.new("can't dump anonymous class #{obj}")
end
param = SOAPStruct.new(TYPE_CLASS)
mark_marshalled_obj(obj, param)
param.add('name', SOAPString.new(obj.name))
addiv2soapattr(param, obj, map)
when ::Module
unless @allow_original_mapping
return nil
end
if obj.to_s[0] == ?#
raise TypeError.new("can't dump anonymous module #{obj}")
end
param = SOAPStruct.new(TYPE_MODULE)
mark_marshalled_obj(obj, param)
param.add('name', SOAPString.new(obj.name))
addiv2soapattr(param, obj, map)
when ::Symbol
unless @allow_original_mapping
return nil
end
param = SOAPStruct.new(TYPE_SYMBOL)
mark_marshalled_obj(obj, param)
param.add('id', SOAPString.new(obj.id2name))
addiv2soapattr(param, obj, map)
when ::Struct
unless @allow_original_mapping
# treat it as an user defined class. [ruby-talk:104980]
#param = unknownobj2soap(soap_class, obj, info, map)
param = SOAPStruct.new(XSD::AnyTypeName)
mark_marshalled_obj(obj, param)
obj.members.each do |member|
param.add(Mapping.name2elename(member),
Mapping._obj2soap(obj[member], map))
end
else
param = SOAPStruct.new(TYPE_STRUCT)
mark_marshalled_obj(obj, param)
param.add('type', ele_type = SOAPString.new(obj.class.to_s))
ele_member = SOAPStruct.new
obj.members.each do |member|
ele_member.add(Mapping.name2elename(member),
Mapping._obj2soap(obj[member], map))
end
param.add('member', ele_member)
addiv2soapattr(param, obj, map)
end
when ::IO, ::Binding, ::Continuation, ::Data, ::Dir, ::File::Stat,
::MatchData, Method, ::Proc, ::Process::Status, ::Thread,
::ThreadGroup, ::UnboundMethod
return nil
when ::SOAP::Mapping::Object
param = SOAPStruct.new(XSD::AnyTypeName)
mark_marshalled_obj(obj, param)
obj.__xmlele.each do |key, value|
param.add(key.name, Mapping._obj2soap(value, map))
end
obj.__xmlattr.each do |key, value|
param.extraattr[key] = value
end
when ::Exception
typestr = Mapping.name2elename(obj.class.to_s)
param = SOAPStruct.new(XSD::QName.new(RubyTypeNamespace, typestr))
mark_marshalled_obj(obj, param)
param.add('message', Mapping._obj2soap(obj.message, map))
param.add('backtrace', Mapping._obj2soap(obj.backtrace, map))
addiv2soapattr(param, obj, map)
else
param = unknownobj2soap(soap_class, obj, info, map)
end
param
end
def soap2obj(obj_class, node, info, map)
rubytype = node.extraattr[RubyTypeName]
if rubytype or node.type.namespace == RubyTypeNamespace
rubytype2obj(node, info, map, rubytype)
elsif node.type == XSD::AnyTypeName or node.type == XSD::AnySimpleTypeName
anytype2obj(node, info, map)
else
unknowntype2obj(node, info, map)
end
end
private
def addiv2soapattr(node, obj, map)
return if obj.instance_variables.empty?
ivars = SOAPStruct.new # Undefined type.
setiv2soap(ivars, obj, map)
node.extraattr[RubyIVarName] = ivars
end
def unknownobj2soap(soap_class, obj, info, map)
if anonymous_class?(obj)
raise TypeError.new("can't dump anonymous class #{obj}")
end
singleton_class = class << obj; self; end
if !obj.singleton_methods(true).empty? or
!singleton_class.instance_variables.empty?
raise TypeError.new("singleton can't be dumped #{obj}")
end
if !(singleton_class.ancestors - obj.class.ancestors).empty?
typestr = Mapping.name2elename(obj.class.to_s)
type = XSD::QName.new(RubyTypeNamespace, typestr)
else
type = Mapping.class2element(obj.class)
end
param = SOAPStruct.new(type)
mark_marshalled_obj(obj, param)
setiv2soap(param, obj, map)
param
end
def rubytype2obj(node, info, map, rubytype)
klass = rubytype ? Mapping.class_from_name(rubytype) : nil
obj = nil
case node
when SOAPString
return @string_factory.soap2obj(klass || String, node, info, map)
when SOAPDateTime
#return @datetime_factory.soap2obj(klass || Time, node, info, map)
klass ||= Time
t = node.to_time
arg = [t.year, t.month, t.mday, t.hour, t.min, t.sec, t.usec]
obj = t.gmt? ? klass.gm(*arg) : klass.local(*arg)
mark_unmarshalled_obj(node, obj)
return true, obj
when SOAPArray
return @array_factory.soap2obj(klass || Array, node, info, map)
when SOAPNil, SOAPBoolean, SOAPInt, SOAPInteger, SOAPDecimal, SOAPDouble
return @basetype_factory.soap2obj(nil, node, info, map)
when SOAPStruct
return rubytypestruct2obj(node, info, map, rubytype)
else
raise
end
end
def rubytypestruct2obj(node, info, map, rubytype)
klass = rubytype ? Mapping.class_from_name(rubytype) : nil
obj = nil
case node.type
when TYPE_HASH
klass = rubytype ? Mapping.class_from_name(rubytype) : Hash
obj = Mapping.create_empty_object(klass)
mark_unmarshalled_obj(node, obj)
node.each do |key, value|
next unless key == 'item'
obj[Mapping._soap2obj(value['key'], map)] =
Mapping._soap2obj(value['value'], map)
end
if node.key?('default')
obj.default = Mapping._soap2obj(node['default'], map)
end
when TYPE_REGEXP
klass = rubytype ? Mapping.class_from_name(rubytype) : Regexp
obj = Mapping.create_empty_object(klass)
mark_unmarshalled_obj(node, obj)
source = node['source'].string
options = node['options'].data || 0
Regexp.instance_method(:initialize).bind(obj).call(source, options)
when TYPE_RANGE
klass = rubytype ? Mapping.class_from_name(rubytype) : Range
obj = Mapping.create_empty_object(klass)
mark_unmarshalled_obj(node, obj)
first = Mapping._soap2obj(node['begin'], map)
last = Mapping._soap2obj(node['end'], map)
exclude_end = node['exclude_end'].data
Range.instance_method(:initialize).bind(obj).call(first, last, exclude_end)
when TYPE_CLASS
obj = Mapping.class_from_name(node['name'].data)
when TYPE_MODULE
obj = Mapping.class_from_name(node['name'].data)
when TYPE_SYMBOL
obj = node['id'].data.intern
when TYPE_STRUCT
typestr = Mapping.elename2name(node['type'].data)
klass = Mapping.class_from_name(typestr)
if klass.nil?
return false
end
unless klass <= ::Struct
return false
end
obj = Mapping.create_empty_object(klass)
mark_unmarshalled_obj(node, obj)
node['member'].each do |name, value|
obj[Mapping.elename2name(name)] = Mapping._soap2obj(value, map)
end
else
return unknowntype2obj(node, info, map)
end
return true, obj
end
def anytype2obj(node, info, map)
case node
when SOAPBasetype
return true, node.data
when SOAPStruct
klass = ::SOAP::Mapping::Object
obj = klass.new
mark_unmarshalled_obj(node, obj)
node.each do |name, value|
obj.__add_xmlele_value(XSD::QName.new(nil, name),
Mapping._soap2obj(value, map))
end
unless node.extraattr.empty?
obj.instance_variable_set('@__xmlattr', node.extraattr)
end
return true, obj
else
return false
end
end
def unknowntype2obj(node, info, map)
case node
when SOAPBasetype
return true, node.data
when SOAPArray
return @array_factory.soap2obj(Array, node, info, map)
when SOAPStruct
obj = unknownstruct2obj(node, info, map)
return true, obj if obj
if !@allow_untyped_struct
return false
end
return anytype2obj(node, info, map)
else
# Basetype which is not defined...
return false
end
end
def unknownstruct2obj(node, info, map)
unless node.type.name
return nil
end
typestr = Mapping.elename2name(node.type.name)
klass = Mapping.class_from_name(typestr)
if klass.respond_to?(:soap_marshallable) and !klass.soap_marshallable
return nil
end
if klass.nil? and @allow_untyped_struct
klass = Mapping.class_from_name(typestr, true) # lenient
end
if klass.nil?
return nil
end
if klass <= ::Exception
return exception2obj(klass, node, map)
end
klass_type = Mapping.class2qname(klass)
return nil unless node.type.match(klass_type)
obj = nil
begin
obj = Mapping.create_empty_object(klass)
rescue
# type name "data" tries Data.new which raises TypeError
nil
end
mark_unmarshalled_obj(node, obj)
setiv2obj(obj, node, map)
obj
end
def exception2obj(klass, node, map)
message = Mapping._soap2obj(node['message'], map)
backtrace = Mapping._soap2obj(node['backtrace'], map)
obj = Mapping.create_empty_object(klass)
obj = obj.exception(message)
mark_unmarshalled_obj(node, obj)
obj.set_backtrace(backtrace)
obj
end
# Only creates empty array. Do String#replace it with real string.
def array2obj(node, map, rubytype)
klass = rubytype ? Mapping.class_from_name(rubytype) : Array
obj = Mapping.create_empty_object(klass)
mark_unmarshalled_obj(node, obj)
obj
end
# Only creates empty string. Do String#replace it with real string.
def string2obj(node, map, rubytype)
klass = rubytype ? Mapping.class_from_name(rubytype) : String
obj = Mapping.create_empty_object(klass)
mark_unmarshalled_obj(node, obj)
obj
end
end
end
end

View file

@ -0,0 +1,170 @@
# SOAP4R - Ruby type mapping schema definition utility.
# Copyright (C) 2000-2007 NAKAMURA Hiroshi <nahi@ruby-lang.org>.
# This program is copyrighted free software by NAKAMURA, Hiroshi. You can
# redistribute it and/or modify it under the same terms of Ruby's license;
# either the dual license version in 2003, or any later version.
require 'xsd/codegen/gensupport'
module SOAP
module Mapping
class SchemaElementDefinition
attr_reader :varname, :mapped_class, :elename, :minoccurs, :maxoccurs
def initialize(varname, mapped_class, elename, minoccurs, maxoccurs,
as_any, as_array)
@varname = varname
@mapped_class = mapped_class
@elename = elename
@minoccurs = minoccurs
@maxoccurs = maxoccurs
@as_any = as_any
@as_array = as_array
end
def as_any?
@as_any
end
def as_array?
@as_array
end
end
module SchemaComplexTypeDefinition
include Enumerable
def initialize
@content = []
@element_cache = {}
end
def is_concrete_definition
true
end
def <<(ele)
@content << ele
end
def each
@content.each do |ele|
yield ele
end
end
def size
@content.size
end
def as_any?
false
end
def as_array?
false
end
def find_element(qname)
@element_cache[qname] ||= search_element(qname)
end
private
def search_element(qname)
each do |ele|
if ele.respond_to?(:find_element)
found = ele.find_element(qname)
return found if found
else
# relaxed match
if ele.elename == qname or
(qname.namespace.nil? and ele.elename.name == qname.name)
return ele
end
end
end
nil
end
end
class SchemaEmptyDefinition
include SchemaComplexTypeDefinition
def initialize
super()
@content.freeze
end
end
class SchemaSequenceDefinition
include SchemaComplexTypeDefinition
def initialize
super()
end
def choice?
false
end
# override
def as_array?
@as_array ||= false
end
def set_array
@as_array = true
end
end
class SchemaChoiceDefinition
include SchemaComplexTypeDefinition
def initialize
super()
end
def choice?
true
end
end
class SchemaDefinition
EMPTY = SchemaEmptyDefinition.new
attr_reader :class_for
attr_reader :elename, :type
attr_reader :qualified
attr_accessor :basetype
attr_accessor :attributes
attr_accessor :elements
def initialize(class_for, elename, type, anonymous, qualified)
@class_for = class_for
@elename = elename
@type = type
@anonymous = anonymous
@qualified = qualified
@basetype = nil
@elements = EMPTY
@attributes = nil
end
def is_anonymous?
@anonymous
end
def choice?
@elements.choice?
end
end
end
end

View file

@ -0,0 +1,105 @@
# SOAP4R - Base type mapping definition
# Copyright (C) 2000-2007 NAKAMURA, Hiroshi <nahi@ruby-lang.org>.
# This program is copyrighted free software by NAKAMURA, Hiroshi. You can
# redistribute it and/or modify it under the same terms of Ruby's license;
# either the dual license version in 2003, or any later version.
module SOAP
TypeMap = {
XSD::XSDAnySimpleType::Type => SOAPAnySimpleType,
XSD::XSDString::Type => SOAPString,
XSD::XSDNormalizedString::Type => SOAPNormalizedString,
XSD::XSDToken::Type => SOAPToken,
XSD::XSDLanguage::Type => SOAPLanguage,
XSD::XSDNMTOKEN::Type => SOAPNMTOKEN,
XSD::XSDNMTOKENS::Type => SOAPNMTOKENS,
XSD::XSDName::Type => SOAPName,
XSD::XSDNCName::Type => SOAPNCName,
XSD::XSDID::Type => SOAPID,
XSD::XSDIDREF::Type => SOAPIDREF,
XSD::XSDIDREFS::Type => SOAPIDREFS,
XSD::XSDENTITY::Type => SOAPENTITY,
XSD::XSDENTITIES::Type => SOAPENTITIES,
XSD::XSDBoolean::Type => SOAPBoolean,
XSD::XSDDecimal::Type => SOAPDecimal,
XSD::XSDFloat::Type => SOAPFloat,
XSD::XSDDouble::Type => SOAPDouble,
XSD::XSDDuration::Type => SOAPDuration,
XSD::XSDDateTime::Type => SOAPDateTime,
XSD::XSDTime::Type => SOAPTime,
XSD::XSDDate::Type => SOAPDate,
XSD::XSDGYearMonth::Type => SOAPGYearMonth,
XSD::XSDGYear::Type => SOAPGYear,
XSD::XSDGMonthDay::Type => SOAPGMonthDay,
XSD::XSDGDay::Type => SOAPGDay,
XSD::XSDGMonth::Type => SOAPGMonth,
XSD::XSDHexBinary::Type => SOAPHexBinary,
XSD::XSDBase64Binary::Type => SOAPBase64,
XSD::XSDAnyURI::Type => SOAPAnyURI,
XSD::XSDQName::Type => SOAPQName,
XSD::XSDInteger::Type => SOAPInteger,
XSD::XSDNonPositiveInteger::Type => SOAPNonPositiveInteger,
XSD::XSDNegativeInteger::Type => SOAPNegativeInteger,
XSD::XSDLong::Type => SOAPLong,
XSD::XSDInt::Type => SOAPInt,
XSD::XSDShort::Type => SOAPShort,
XSD::XSDByte::Type => SOAPByte,
XSD::XSDNonNegativeInteger::Type => SOAPNonNegativeInteger,
XSD::XSDUnsignedLong::Type => SOAPUnsignedLong,
XSD::XSDUnsignedInt::Type => SOAPUnsignedInt,
XSD::XSDUnsignedShort::Type => SOAPUnsignedShort,
XSD::XSDUnsignedByte::Type => SOAPUnsignedByte,
XSD::XSDPositiveInteger::Type => SOAPPositiveInteger,
# soap4r does not use soapenc types actively but it should be accepted.
SOAP::SOAPString::SOAPENCType => SOAPString,
SOAP::SOAPNormalizedString::Type => SOAPNormalizedString,
SOAP::SOAPToken::Type => SOAPToken,
SOAP::SOAPLanguage::Type => SOAPLanguage,
SOAP::SOAPNMTOKEN::Type => SOAPNMTOKEN,
SOAP::SOAPNMTOKENS::Type => SOAPNMTOKENS,
SOAP::SOAPName::Type => SOAPName,
SOAP::SOAPNCName::Type => SOAPNCName,
SOAP::SOAPID::Type => SOAPID,
SOAP::SOAPIDREF::Type => SOAPIDREF,
SOAP::SOAPIDREFS::Type => SOAPIDREFS,
SOAP::SOAPENTITY::Type => SOAPENTITY,
SOAP::SOAPENTITIES::Type => SOAPENTITIES,
SOAP::SOAPBoolean::SOAPENCType => SOAPBoolean,
SOAP::SOAPDecimal::SOAPENCType => SOAPDecimal,
SOAP::SOAPFloat::SOAPENCType => SOAPFloat,
SOAP::SOAPDouble::SOAPENCType => SOAPDouble,
SOAP::SOAPDuration::SOAPENCType => SOAPDuration,
SOAP::SOAPDateTime::SOAPENCType => SOAPDateTime,
SOAP::SOAPTime::SOAPENCType => SOAPTime,
SOAP::SOAPDate::SOAPENCType => SOAPDate,
SOAP::SOAPGYearMonth::SOAPENCType => SOAPGYearMonth,
SOAP::SOAPGYear::SOAPENCType => SOAPGYear,
SOAP::SOAPGMonthDay::SOAPENCType => SOAPGMonthDay,
SOAP::SOAPGDay::SOAPENCType => SOAPGDay,
SOAP::SOAPGMonth::SOAPENCType => SOAPGMonth,
SOAP::SOAPHexBinary::SOAPENCType => SOAPHexBinary,
SOAP::SOAPBase64::SOAPENCType => SOAPBase64,
SOAP::SOAPAnyURI::SOAPENCType => SOAPAnyURI,
SOAP::SOAPQName::SOAPENCType => SOAPQName,
SOAP::SOAPInteger::SOAPENCType => SOAPInteger,
SOAP::SOAPNonPositiveInteger::SOAPENCType => SOAPNonPositiveInteger,
SOAP::SOAPNegativeInteger::SOAPENCType => SOAPNegativeInteger,
SOAP::SOAPLong::SOAPENCType => SOAPLong,
SOAP::SOAPInt::SOAPENCType => SOAPInt,
SOAP::SOAPShort::SOAPENCType => SOAPShort,
SOAP::SOAPByte::SOAPENCType => SOAPByte,
SOAP::SOAPNonNegativeInteger::SOAPENCType => SOAPNonNegativeInteger,
SOAP::SOAPUnsignedLong::SOAPENCType => SOAPUnsignedLong,
SOAP::SOAPUnsignedInt::SOAPENCType => SOAPUnsignedInt,
SOAP::SOAPUnsignedShort::SOAPENCType => SOAPUnsignedShort,
SOAP::SOAPUnsignedByte::SOAPENCType => SOAPUnsignedByte,
SOAP::SOAPPositiveInteger::SOAPENCType => SOAPPositiveInteger,
}
end

View file

@ -0,0 +1,211 @@
# SOAP4R - WSDL encoded mapping registry.
# Copyright (C) 2000-2007 NAKAMURA, Hiroshi <nahi@ruby-lang.org>.
# This program is copyrighted free software by NAKAMURA, Hiroshi. You can
# redistribute it and/or modify it under the same terms of Ruby's license;
# either the dual license version in 2003, or any later version.
require 'xsd/qname'
require 'xsd/namedelements'
require 'soap/baseData'
require 'soap/mapping/mapping'
require 'soap/mapping/typeMap'
module SOAP
module Mapping
class WSDLEncodedRegistry < EncodedRegistry
attr_reader :definedelements
attr_reader :definedtypes
def initialize(definedtypes = XSD::NamedElements::Empty)
super()
@definedtypes = definedtypes
# @definedelements = definedelements needed?
# For mapping AnyType element.
@rubytype_factory = RubytypeFactory.new(
:allow_untyped_struct => true,
:allow_original_mapping => true
)
end
def obj2soap(obj, qname = nil)
soap_obj = nil
if type = @definedtypes[qname]
soap_obj = obj2typesoap(obj, type)
else
soap_obj = any2soap(obj, qname)
end
return soap_obj if soap_obj
if @excn_handler_obj2soap
soap_obj = @excn_handler_obj2soap.call(obj) { |yield_obj|
Mapping._obj2soap(yield_obj, self)
}
return soap_obj if soap_obj
end
if qname
raise MappingError.new("cannot map #{obj.class.name} as #{qname}")
else
raise MappingError.new("cannot map #{obj.class.name} to SOAP/OM")
end
end
# map anything for now: must refer WSDL while mapping. [ToDo]
def soap2obj(node, obj_class = nil)
cause = nil
begin
unless obj_class
typestr = Mapping.safeconstname(node.elename.name)
obj_class = Mapping.class_from_name(typestr)
end
return Mapping._soap2obj(node, Mapping::DefaultRegistry, obj_class)
rescue MappingError
cause = $!
end
if @excn_handler_soap2obj
begin
return @excn_handler_soap2obj.call(node) { |yield_node|
Mapping._soap2obj(yield_node, self)
}
rescue Exception
end
end
raise MappingError.new("cannot map #{node.type.name} to Ruby object", cause)
end
private
def any2soap(obj, qname)
ele = nil
if obj.nil?
ele = SOAPNil.new
elsif qname.nil? or qname == XSD::AnyTypeName
ele = @rubytype_factory.obj2soap(nil, obj, nil, self)
elsif obj.is_a?(XSD::NSDBase)
ele = soap2soap(obj, qname)
elsif type = TypeMap[qname]
ele = base2soap(obj, type)
end
add_attributes2soap(obj, ele) unless ele.nil?
ele
end
def soap2soap(obj, type_qname)
if obj.is_a?(SOAPBasetype)
obj
elsif obj.is_a?(SOAPStruct) && (type = @definedtypes[type_qname])
soap_obj = obj
mark_marshalled_obj(obj, soap_obj)
elements2soap(obj, soap_obj, type.elements)
soap_obj
elsif obj.is_a?(SOAPArray) && (type = @definedtypes[type_qname])
soap_obj = obj
contenttype = type.child_type
mark_marshalled_obj(obj, soap_obj)
obj.replace do |ele|
Mapping._obj2soap(ele, self, contenttype)
end
soap_obj
else
nil
end
end
def obj2typesoap(obj, type)
if type.is_a?(::WSDL::XMLSchema::SimpleType)
simpleobj2soap(obj, type)
else
complexobj2soap(obj, type)
end
end
def simpleobj2soap(obj, type)
return SOAPNil.new unless obj
type.check_lexical_format(obj)
if type.base
ele = base2soap(obj, TypeMap[type.base])
ele.type = type.name
elsif type.list
value = obj.is_a?(Array) ? obj.join(" ") : obj.to_s
ele = base2soap(value, SOAP::SOAPString)
else
raise MappingError.new("unsupported simpleType: #{type}")
end
ele
end
def complexobj2soap(obj, type)
case type.compoundtype
when :TYPE_STRUCT
struct2soap(obj, type.name, type)
when :TYPE_ARRAY
array2soap(obj, type.name, type)
when :TYPE_MAP
map2soap(obj, type.name, type)
when :TYPE_SIMPLE
simpleobj2soap(obj, type.simplecontent)
when :TYPE_EMPTY
raise MappingError.new("should be empty") unless obj.nil?
SOAPNil.new
else
raise MappingError.new("unknown compound type: #{type.compoundtype}")
end
end
def struct2soap(obj, type_qname, type)
return SOAPNil.new if obj.nil? # ToDo: check nillable.
soap_obj = SOAPStruct.new(type_qname)
mark_marshalled_obj(obj, soap_obj)
elements2soap(obj, soap_obj, type.elements)
soap_obj
end
def array2soap(obj, type_qname, type)
return SOAPNil.new if obj.nil? # ToDo: check nillable.
arytype = type.child_type
soap_obj = SOAPArray.new(ValueArrayName, 1, arytype)
unless obj.nil?
mark_marshalled_obj(obj, soap_obj)
obj.each do |item|
soap_obj.add(Mapping._obj2soap(item, self, arytype))
end
end
soap_obj
end
MapKeyName = XSD::QName.new(nil, "key")
MapValueName = XSD::QName.new(nil, "value")
def map2soap(obj, type_qname, type)
return SOAPNil.new if obj.nil? # ToDo: check nillable.
keytype = type.child_type(MapKeyName) || XSD::AnyTypeName
valuetype = type.child_type(MapValueName) || XSD::AnyTypeName
soap_obj = SOAPStruct.new(MapQName)
unless obj.nil?
mark_marshalled_obj(obj, soap_obj)
obj.each do |key, value|
elem = SOAPStruct.new
elem.add("key", Mapping._obj2soap(key, self, keytype))
elem.add("value", Mapping._obj2soap(value, self, valuetype))
# ApacheAxis allows only 'item' here.
soap_obj.add("item", elem)
end
end
soap_obj
end
def elements2soap(obj, soap_obj, elements)
elements.each do |element|
name = element.name.name
child_obj = Mapping.get_attribute(obj, name)
soap_obj.add(name,
Mapping._obj2soap(child_obj, self, element.type || element.name))
end
end
end
end
end

View file

@ -0,0 +1,248 @@
# SOAP4R - WSDL literal mapping registry.
# Copyright (C) 2000-2007 NAKAMURA, Hiroshi <nahi@ruby-lang.org>.
# This program is copyrighted free software by NAKAMURA, Hiroshi. You can
# redistribute it and/or modify it under the same terms of Ruby's license;
# either the dual license version in 2003, or any later version.
require 'soap/baseData'
require 'soap/mapping/mapping'
require 'soap/mapping/literalregistry'
require 'soap/mapping/typeMap'
require 'xsd/codegen/gensupport'
require 'xsd/namedelements'
module SOAP
module Mapping
class WSDLLiteralRegistry < LiteralRegistry
attr_reader :definedelements
attr_reader :definedtypes
def initialize(definedtypes = XSD::NamedElements::Empty,
definedelements = XSD::NamedElements::Empty)
super()
@definedtypes = definedtypes
@definedelements = definedelements
end
def obj2soap(obj, qname, obj_class = nil)
soap_obj = nil
if obj.is_a?(SOAPElement)
soap_obj = obj
elsif eledef = @definedelements[qname]
soap_obj = obj2elesoap(obj, eledef)
elsif type = @definedtypes[qname]
soap_obj = obj2typesoap(obj, type)
else
soap_obj = any2soap(obj, qname, obj_class)
end
return soap_obj if soap_obj
if @excn_handler_obj2soap
soap_obj = @excn_handler_obj2soap.call(obj) { |yield_obj|
Mapping.obj2soap(yield_obj, nil, nil, MAPPING_OPT)
}
return soap_obj if soap_obj
end
raise MappingError.new("cannot map #{obj.class.name} as #{qname}")
end
# node should be a SOAPElement
def soap2obj(node, obj_class = nil)
cause = nil
begin
return any2obj(node, obj_class)
rescue MappingError
cause = $!
end
if @excn_handler_soap2obj
begin
return @excn_handler_soap2obj.call(node) { |yield_node|
Mapping.soap2obj(yield_node, nil, nil, MAPPING_OPT)
}
rescue Exception
end
end
if node.respond_to?(:type)
raise MappingError.new("cannot map #{node.type.name} to Ruby object", cause)
else
raise MappingError.new("cannot map #{node.elename.name} to Ruby object", cause)
end
end
private
def obj2elesoap(obj, eledef)
ele = nil
qualified = (eledef.elementform == 'qualified')
if obj.is_a?(SOAPNil)
ele = obj
elsif eledef.type
if type = @definedtypes[eledef.type]
ele = obj2typesoap(obj, type)
elsif type = TypeMap[eledef.type]
ele = base2soap(obj, type)
else
raise MappingError.new("cannot find type #{eledef.type}")
end
elsif eledef.local_complextype
ele = obj2typesoap(obj, eledef.local_complextype)
elsif eledef.local_simpletype
ele = obj2typesoap(obj, eledef.local_simpletype)
else
raise MappingError.new('illegal schema?')
end
ele.elename = eledef.name
ele.qualified = qualified
ele
end
def obj2typesoap(obj, type)
ele = nil
if type.is_a?(::WSDL::XMLSchema::SimpleType)
ele = simpleobj2soap(obj, type)
else # complexType
if type.simplecontent
ele = simpleobj2soap(obj, type.simplecontent)
else
ele = complexobj2soap(obj, type)
end
ele.type = type.name
if type.base or Mapping.root_type_hint
Mapping.reset_root_type_hint
ele.force_typed = true
end
add_definedattributes2soap(obj, ele, type)
end
ele
end
def simpleobj2soap(obj, type)
type.check_lexical_format(obj)
return SOAPNil.new if obj.nil?
if type.base
ele = base2soap(obj, TypeMap[type.base])
elsif type.list
value = obj.is_a?(Array) ? obj.join(" ") : obj.to_s
ele = base2soap(value, SOAP::SOAPString)
else
raise MappingError.new("unsupported simpleType: #{type}")
end
ele
end
def complexobj2soap(obj, type)
ele = SOAPElement.new(type.name)
complexobj2sequencesoap(obj, ele, type, type.choice?, type.choice?)
ele
end
def complexobj2sequencesoap(obj, soap, type, nillable, is_choice)
added = false
type.elements.each do |child_ele|
case child_ele
when WSDL::XMLSchema::Any
any = Mapping.get_attributes_for_any(obj)
SOAPElement.from_objs(any).each do |child|
soap.add(child)
end
ele_added = true
when WSDL::XMLSchema::Element
ele_added = complexobj2soapchildren(obj, soap, child_ele, nillable)
when WSDL::XMLSchema::Sequence
ele_added = complexobj2sequencesoap(obj, soap, child_ele, nillable, false)
when WSDL::XMLSchema::Choice
ele_added = complexobj2sequencesoap(obj, soap, child_ele, true, true)
else
raise MappingError.new("unknown type: #{child_ele}")
end
added = true if ele_added
break if is_choice and ele_added
end
added
end
def complexobj2soapchildren(obj, soap, child_ele, nillable = false)
if child_ele.map_as_array?
complexobj2soapchildren_array(obj, soap, child_ele, nillable)
else
complexobj2soapchildren_single(obj, soap, child_ele, nillable)
end
end
def complexobj2soapchildren_array(obj, soap, child_ele, nillable)
child = Mapping.get_attribute(obj, child_ele.name.name)
if child.nil? and obj.is_a?(::Array)
child = obj
end
if child.nil?
return false if nillable
if child_soap = nil2soap(child_ele)
soap.add(child_soap)
return true
else
return false
end
end
unless child.respond_to?(:each)
return false
end
child.each do |item|
if item.is_a?(SOAPElement)
soap.add(item)
else
child_soap = obj2elesoap(item, child_ele)
soap.add(child_soap)
end
end
true
end
def complexobj2soapchildren_single(obj, soap, child_ele, nillable)
child = Mapping.get_attribute(obj, child_ele.name.name)
case child
when NilClass
return false if nillable
if child_soap = nil2soap(child_ele)
soap.add(child_soap)
true
else
false
end
when SOAPElement
soap.add(child)
true
else
child_soap = obj2elesoap(child, child_ele)
soap.add(child_soap)
true
end
end
def nil2soap(ele)
if ele.nillable
obj2elesoap(nil, ele) # add an empty element
elsif ele.minoccurs == 0
nil # intends no element
else
warn("nil not allowed: #{ele.name.name}")
nil
end
end
def add_definedattributes2soap(obj, ele, typedef)
if typedef.attributes
typedef.attributes.each do |at|
value = get_xmlattr_value(obj, at.name)
ele.extraattr[at.name] = value unless value.nil?
end
end
end
end
end
end

View file

@ -0,0 +1,59 @@
# SOAP4R - Marshalling/Unmarshalling Ruby's object using SOAP Encoding.
# Copyright (C) 2000-2007 NAKAMURA, Hiroshi <nahi@ruby-lang.org>.
# This program is copyrighted free software by NAKAMURA, Hiroshi. You can
# redistribute it and/or modify it under the same terms of Ruby's license;
# either the dual license version in 2003, or any later version.
require "soap/mapping"
require "soap/processor"
module SOAP
module Marshal
# Trying xsd:dateTime data to be recovered as aTime.
MarshalMappingRegistry = Mapping::EncodedRegistry.new(
:allow_original_mapping => true)
MarshalMappingRegistry.add(
Time,
::SOAP::SOAPDateTime,
::SOAP::Mapping::EncodedRegistry::DateTimeFactory
)
class << self
public
def dump(obj, io = nil)
marshal(obj, MarshalMappingRegistry, io)
end
def load(stream)
unmarshal(stream, MarshalMappingRegistry)
end
def marshal(obj, mapping_registry = MarshalMappingRegistry, io = nil)
elename = Mapping.name2elename(obj.class.to_s)
soap_obj = Mapping.obj2soap(obj, mapping_registry)
body = SOAPBody.new
body.add(elename, soap_obj)
env = SOAPEnvelope.new(nil, body)
SOAP::Processor.marshal(env, {}, io)
end
def unmarshal(stream, mapping_registry = MarshalMappingRegistry)
env = SOAP::Processor.unmarshal(stream)
if env.nil?
raise ArgumentError.new("Illegal SOAP marshal format.")
end
Mapping.soap2obj(env.body.root_node, mapping_registry)
end
end
end
end
SOAPMarshal = SOAP::Marshal

View file

@ -0,0 +1,241 @@
# SOAP4R - MIME Message implementation.
# Copyright (C) 2002 Jamie Herre.
# This program is copyrighted free software by NAKAMURA, Hiroshi. You can
# redistribute it and/or modify it under the same terms of Ruby's license;
# either the dual license version in 2003, or any later version.
require 'soap/attachment'
module SOAP
# Classes for MIME message handling. Should be put somewhere else!
# Tried using the 'tmail' module but found that I needed something
# lighter in weight.
class MIMEMessage
class MIMEMessageError < StandardError; end
MultipartContentType = 'multipart/\w+'
class Header
attr_accessor :str, :key, :root
def initialize
@attrs = {}
end
def [](key)
@attrs[key]
end
def []=(key, value)
@attrs[key] = value
end
def to_s
@key + ": " + @str
end
end
class Headers < Hash
def self.parse(str)
new.parse(str)
end
def parse(str)
header_cache = nil
str.each do |line|
case line
when /^\A[^\: \t]+:\s*.+$/
parse_line(header_cache) if header_cache
header_cache = line.sub(/\r?\n\z/, '')
when /^\A\s+(.*)$/
# a continuous line at the beginning line crashes here.
header_cache << line
else
raise RuntimeError.new("unexpected header: #{line.inspect}")
end
end
parse_line(header_cache) if header_cache
self
end
def parse_line(line)
if /^\A([^\: \t]+):\s*(.+)\z/ =~ line
header = parse_rhs($2.strip)
header.key = $1.strip
self[header.key.downcase] = header
else
raise RuntimeError.new("unexpected header line: #{line.inspect}")
end
end
def parse_rhs(str)
a = str.split(/;+\s+/)
header = Header.new
header.str = str
header.root = a.shift
a.each do |pair|
if pair =~ /(\w+)\s*=\s*"?([^"]+)"?/
header[$1.downcase] = $2
else
raise RuntimeError.new("unexpected header component: #{pair.inspect}")
end
end
header
end
def add(key, value)
if key != nil and value != nil
header = parse_rhs(value)
header.key = key
self[key.downcase] = header
end
end
def to_s
self.values.collect { |hdr|
hdr.to_s
}.join("\r\n")
end
end
class Part
attr_accessor :headers, :body
def initialize
@headers = Headers.new
@headers.add("Content-Transfer-Encoding", "8bit")
@body = nil
@contentid = nil
end
def self.parse(str)
new.parse(str)
end
def parse(str)
headers, body = str.split(/\r\n\r\n/, 2)
if headers != nil and body != nil
@headers = Headers.parse(headers)
@body = body.sub(/\r\n\z/, '')
else
raise RuntimeError.new("unexpected part: #{str.inspect}")
end
self
end
def contentid
if @contentid == nil and @headers.key?('content-id')
@contentid = @headers['content-id'].str
@contentid = $1 if @contentid =~ /^<(.+)>$/
end
@contentid
end
alias content body
def to_s
@headers.to_s + "\r\n\r\n" + @body
end
end
def initialize
@parts = []
@headers = Headers.new
@root = nil
@boundary = nil
end
def self.parse(head, str)
new.parse(head, str)
end
attr_reader :parts, :headers
def close
@headers.add(
"Content-Type",
"multipart/related; type=\"text/xml\"; boundary=\"#{boundary}\"; start=\"#{@parts[0].contentid}\""
)
end
def parse(head, str)
@headers = Headers.parse(head + "\r\n" + "From: jfh\r\n")
boundary = @headers['content-type']['boundary']
if boundary != nil
parts = str.split(/--#{Regexp.quote(boundary)}\s*(?:\r\n|--\r\n)/)
part = parts.shift # preamble must be ignored.
@parts = parts.collect { |part| Part.parse(part) }
else
@parts = [Part.parse(str)]
end
if @parts.length < 1
raise MIMEMessageError.new("This message contains no valid parts!")
end
self
end
def root
if @root == nil
start = @headers['content-type']['start']
@root = (start && @parts.find { |prt| prt.contentid == start }) ||
@parts[0]
end
@root
end
def boundary
if @boundary == nil
@boundary = "----=Part_" + __id__.to_s + rand.to_s
end
@boundary
end
def add_part(content)
part = Part.new
part.headers.add("Content-Type",
"text/xml; charset=" + XSD::Charset.xml_encoding_label)
part.headers.add("Content-ID", Attachment.contentid(part))
part.body = content
@parts.unshift(part)
end
def add_attachment(attach)
part = Part.new
part.headers.add("Content-Type", attach.contenttype)
part.headers.add("Content-ID", attach.mime_contentid)
part.body = attach.content
@parts.unshift(part)
end
def has_parts?
(@parts.length > 0)
end
def headers_str
@headers.to_s
end
def content_str
str = ''
@parts.each do |prt|
str << "--" + boundary + "\r\n"
str << prt.to_s + "\r\n"
end
str << '--' + boundary + "--\r\n"
str
end
def to_s
str = headers_str + "\r\n\r\n" + conent_str
end
end
end

View file

@ -0,0 +1,42 @@
# SOAP4R - Nested exception implementation
# Copyright (C) 2000-2007 NAKAMURA, Hiroshi <nahi@ruby-lang.org>.
# This program is copyrighted free software by NAKAMURA, Hiroshi. You can
# redistribute it and/or modify it under the same terms of Ruby's license;
# either the dual license version in 2003, or any later version.
module SOAP
module NestedException
attr_reader :cause
attr_reader :original_backtraace
def initialize(msg = nil, cause = nil)
super(msg)
@cause = cause
@original_backtrace = nil
end
def set_backtrace(backtrace)
if @cause and @cause.respond_to?(:backtrace)
@original_backtrace = backtrace
=begin
# for agressive backtrace abstraction: 'here' only should not be good
here = @original_backtrace[0]
backtrace = Array[*@cause.backtrace]
backtrace[0] = "#{backtrace[0]}: #{@cause} (#{@cause.class})"
backtrace.unshift(here)
=end
# just join the nested backtrace at the tail of backtrace
caused = Array[*@cause.backtrace]
caused[0] = "#{caused[0]}: #{@cause} (#{@cause.class}) [NESTED]"
backtrace += caused
end
super(backtrace)
end
end
end

View file

@ -0,0 +1,241 @@
# SOAP4R - net/http wrapper
# Copyright (C) 2000-2007 NAKAMURA, Hiroshi <nahi@ruby-lang.org>.
# This program is copyrighted free software by NAKAMURA, Hiroshi. You can
# redistribute it and/or modify it under the same terms of Ruby's license;
# either the dual license version in 2003, or any later version.
require 'net/http'
require 'soap/filter/filterchain'
module SOAP
class NetHttpClient
SSLEnabled = begin
require 'net/https'
true
rescue LoadError
false
end
attr_reader :proxy
attr_accessor :no_proxy
attr_accessor :debug_dev
attr_accessor :ssl_config # ignored for now.
attr_accessor :protocol_version # ignored for now.
attr_accessor :connect_timeout
attr_accessor :send_timeout # ignored for now.
attr_accessor :receive_timeout
attr_reader :test_loopback_response
attr_reader :request_filter # ignored for now.
def initialize(proxy = nil, agent = nil)
@proxy = proxy ? URI.parse(proxy) : nil
@agent = agent
@debug_dev = nil
@test_loopback_response = []
@request_filter = Filter::FilterChain.new
@session_manager = SessionManager.new
@no_proxy = @ssl_config = @protocol_version = nil
@connect_timeout = @send_timeout = @receive_timeout = nil
end
def proxy=(proxy)
if proxy.nil?
@proxy = nil
else
if proxy.is_a?(URI)
@proxy = proxy
else
@proxy = URI.parse(proxy)
end
if @proxy.scheme == nil or @proxy.scheme.downcase != 'http' or
@proxy.host == nil or @proxy.port == nil
raise ArgumentError.new("unsupported proxy `#{proxy}'")
end
end
reset_all
@proxy
end
def set_auth(uri, user_id, passwd)
raise NotImplementedError.new("auth is not supported under soap4r + net/http.")
end
def set_basic_auth(uri, user_id, passwd)
# net/http does not handle url.
@basic_auth = [user_id, passwd]
raise NotImplementedError.new("basic_auth is not supported under soap4r + net/http.")
end
def set_cookie_store(filename)
raise NotImplementedError.new
end
def save_cookie_store(filename)
raise NotImplementedError.new
end
def reset(url)
# no persistent connection. ignored.
end
def reset_all
# no persistent connection. ignored.
end
def post(url, req_body, header = {})
post_redirect(url, req_body, header, 10)
end
def get_content(url, header = {})
if str = @test_loopback_response.shift
return str
end
unless url.is_a?(URI)
url = URI.parse(url)
end
extra = header.dup
extra['User-Agent'] = @agent if @agent
res = start(url) { |http|
http.get(url.request_uri, extra)
}
res.body
end
private
def post_redirect(url, req_body, header, redirect_count)
if str = @test_loopback_response.shift
if @debug_dev
@debug_dev << "= Request\n\n"
@debug_dev << req_body
@debug_dev << "\n\n= Response\n\n"
@debug_dev << str
end
status = 200
reason = nil
contenttype = 'text/xml'
content = str
return Response.new(status, reason, contenttype, content)
return str
end
unless url.is_a?(URI)
url = URI.parse(url)
end
extra = header.dup
extra['User-Agent'] = @agent if @agent
res = start(url) { |http|
if @debug_dev
@debug_dev << "= Request\n\n"
@debug_dev << req_body << "\n"
end
http.post(url.request_uri, req_body, extra)
}
case res
when Net::HTTPRedirection
if redirect_count > 0
post_redirect(res['location'], req_body, header,
redirect_count - 1)
else
raise ArgumentError.new("Too many redirects")
end
else
Response.from_httpresponse(res)
end
end
def start(url)
http = create_connection(url)
response = nil
http.start { |worker|
response = yield(worker)
worker.finish
}
if @debug_dev
@debug_dev << "\n\n= Response\n\n"
@debug_dev << response.body << "\n"
end
response
end
def create_connection(url)
proxy_host = proxy_port = nil
unless no_proxy?(url)
proxy_host = @proxy.host
proxy_port = @proxy.port
end
http = Net::HTTP::Proxy(proxy_host, proxy_port).new(url.host, url.port)
if http.respond_to?(:set_debug_output)
http.set_debug_output(@debug_dev)
end
http.open_timeout = @connect_timeout if @connect_timeout
http.read_timeout = @receive_timeout if @receive_timeout
case url
when URI::HTTPS then
if SSLEnabled
http.use_ssl = true
else
raise RuntimeError.new("Cannot connect to #{url} (OpenSSL is not installed.)")
end
when URI::HTTP then
# OK
else
raise RuntimeError.new("Cannot connect to #{url} (Not HTTP.)")
end
http
end
NO_PROXY_HOSTS = ['localhost']
def no_proxy?(uri)
if !@proxy or NO_PROXY_HOSTS.include?(uri.host)
return true
end
unless @no_proxy
return false
end
@no_proxy.scan(/([^:,]+)(?::(\d+))?/) do |host, port|
if /(\A|\.)#{Regexp.quote(host)}\z/i =~ uri.host &&
(!port || uri.port == port.to_i)
return true
end
end
false
end
class SessionManager
attr_accessor :connect_timeout
attr_accessor :send_timeout
attr_accessor :receive_timeout
end
class Response
attr_reader :status
attr_reader :reason
attr_reader :contenttype
attr_reader :content
def initialize(status, reason, contenttype, content)
@status = status
@reason = reason
@contenttype = contenttype
@content = content
end
def self.from_httpresponse(res)
status = res.code.to_i
reason = res.message
contenttype = res['content-type']
content = res.body
new(status, reason, contenttype, content)
end
end
end
end

34
vendor/gems/soap4r-1.5.8/lib/soap/ns.rb vendored Normal file
View file

@ -0,0 +1,34 @@
# SOAP4R - SOAP Namespace library
# Copyright (C) 2000-2007 NAKAMURA, Hiroshi <nahi@ruby-lang.org>.
# This program is copyrighted free software by NAKAMURA, Hiroshi. You can
# redistribute it and/or modify it under the same terms of Ruby's license;
# either the dual license version in 2003, or any later version.
require 'xsd/datatypes'
require 'xsd/ns'
require 'soap/soap'
module SOAP
class NS < XSD::NS
KNOWN_TAG = XSD::NS::KNOWN_TAG.dup.update(
SOAP::EnvelopeNamespace => 'env'
)
def initialize(tag2ns = nil)
super(tag2ns)
end
private
def default_known_tag
KNOWN_TAG
end
end
end

View file

@ -0,0 +1,252 @@
# SOAP4R - SOAP XML Instance Parser library.
# Copyright (C) 2000-2007 NAKAMURA, Hiroshi <nahi@ruby-lang.org>.
# This program is copyrighted free software by NAKAMURA, Hiroshi. You can
# redistribute it and/or modify it under the same terms of Ruby's license;
# either the dual license version in 2003, or any later version.
require 'xsd/xmlparser'
require 'soap/soap'
require 'soap/ns'
require 'soap/baseData'
require 'soap/encodingstyle/handler'
module SOAP
class Parser
include SOAP
class ParseError < Error; end
class FormatDecodeError < ParseError; end
class UnexpectedElementError < ParseError; end
private
class ParseFrame
attr_reader :node
attr_reader :name
attr_reader :ns
attr_reader :encodingstyle
attr_reader :handler
class NodeContainer
def initialize(node)
@node = node
end
def node
@node
end
def replace_node(node)
@node = node
end
end
public
def initialize(ns, name, node, encodingstyle, handler)
@ns = ns
@name = name
@node = NodeContainer.new(node)
@encodingstyle = encodingstyle
@handler = handler
end
# to avoid memory consumption
def update(ns, name, node, encodingstyle, handler)
@ns = ns
@name = name
@node.replace_node(node)
@encodingstyle = encodingstyle
@handler = handler
self
end
end
public
attr_accessor :envelopenamespace
attr_accessor :default_encodingstyle
attr_accessor :decode_typemap
attr_accessor :allow_unqualified_element
def initialize(opt = {})
@opt = opt
@parser = XSD::XMLParser.create_parser(self, opt)
@parsestack = nil
@recycleframe = nil
@lastnode = nil
@handlers = {}
@envelopenamespace = opt[:envelopenamespace] || EnvelopeNamespace
@default_encodingstyle = opt[:default_encodingstyle] || EncodingNamespace
@decode_typemap = opt[:decode_typemap] || nil
@allow_unqualified_element = opt[:allow_unqualified_element] || false
end
def charset
@parser.charset
end
def parse(string_or_readable)
@parsestack = []
@lastnode = nil
@handlers.each do |uri, handler|
handler.decode_prologue
end
@parser.do_parse(string_or_readable)
unless @parsestack.empty?
raise FormatDecodeError.new("Unbalanced tag in XML.")
end
@handlers.each do |uri, handler|
handler.decode_epilogue
end
@lastnode
end
def start_element(name, raw_attrs)
lastframe = @parsestack.last
ns = parent = parent_encodingstyle = nil
if lastframe
ns = lastframe.ns
parent = lastframe.node
parent_encodingstyle = lastframe.encodingstyle
else
ns = SOAP::NS.new
parent = ParseFrame::NodeContainer.new(nil)
parent_encodingstyle = nil
end
# ns might be the same
ns, raw_attrs = XSD::XMLParser.filter_ns(ns, raw_attrs)
attrs = decode_attrs(ns, raw_attrs)
encodingstyle = attrs[AttrEncodingStyleName]
# Children's encodingstyle is derived from its parent.
if encodingstyle.nil?
if parent.node.is_a?(SOAPHeader)
encodingstyle = LiteralNamespace
else
encodingstyle = parent_encodingstyle || @default_encodingstyle
end
end
handler = find_handler(encodingstyle)
unless handler
raise FormatDecodeError.new("Unknown encodingStyle: #{ encodingstyle }.")
end
node = decode_tag(ns, name, attrs, parent, handler)
if @recycleframe
@parsestack << @recycleframe.update(ns, name, node, encodingstyle, handler)
@recycleframe = nil
else
@parsestack << ParseFrame.new(ns, name, node, encodingstyle, handler)
end
end
def characters(text)
# Ignore Text outside of SOAP Envelope.
if lastframe = @parsestack.last
# Need not to be cloned because character does not have attr.
decode_text(lastframe.ns, text, lastframe.handler)
end
end
def end_element(name)
lastframe = @parsestack.pop
unless name == lastframe.name
raise UnexpectedElementError.new("Closing element name '#{ name }' does not match with opening element '#{ lastframe.name }'.")
end
decode_tag_end(lastframe.ns, lastframe.node, lastframe.handler)
@lastnode = lastframe.node.node
@recycleframe = lastframe
end
private
def decode_tag(ns, name, attrs, parent, handler)
ele = ns.parse(name)
# Envelope based parsing.
if ((ele.namespace == @envelopenamespace) ||
(@allow_unqualified_element && ele.namespace.nil?))
o = decode_soap_envelope(ns, ele, attrs, parent)
return o if o
end
# Encoding based parsing.
return handler.decode_tag(ns, ele, attrs, parent)
end
def decode_tag_end(ns, node, handler)
return handler.decode_tag_end(ns, node)
end
def decode_attrs(ns, attrs)
extraattr = {}
attrs.each do |key, value|
qname = ns.parse_local(key)
extraattr[qname] = value
end
extraattr
end
def decode_text(ns, text, handler)
handler.decode_text(ns, text)
end
def decode_soap_envelope(ns, ele, attrs, parent)
o = nil
if ele.name == EleEnvelope
o = SOAPEnvelope.new
if ext = @opt[:external_content]
ext.each do |k, v|
o.external_content[k] = v
end
end
elsif ele.name == EleHeader
return nil unless parent.node.is_a?(SOAPEnvelope)
o = SOAPHeader.new
parent.node.header = o
elsif ele.name == EleBody
return nil unless parent.node.is_a?(SOAPEnvelope)
o = SOAPBody.new
parent.node.body = o
elsif ele.name == EleFault
if parent.node.is_a?(SOAPBody)
o = SOAPFault.new
parent.node.fault = o
elsif parent.node.is_a?(SOAPEnvelope)
# live.com server returns SOAPFault as a direct child of SOAPEnvelope.
# support it even if it's not spec compliant.
warn("Fault must be a child of Body.")
body = SOAPBody.new
parent.node.body = body
o = SOAPFault.new
body.fault = o
else
return nil
end
end
o.extraattr.update(attrs) if o
o
end
def find_handler(encodingstyle)
unless @handlers.key?(encodingstyle)
handler_factory = SOAP::EncodingStyle::Handler.handler(encodingstyle) ||
SOAP::EncodingStyle::Handler.handler(EncodingNamespace)
handler = handler_factory.new(@parser.charset)
handler.decode_typemap = @decode_typemap
handler.decode_prologue
@handlers[encodingstyle] = handler
end
@handlers[encodingstyle]
end
end
end

View file

@ -0,0 +1,66 @@
# SOAP4R - marshal/unmarshal interface.
# Copyright (C) 2000-2007 NAKAMURA, Hiroshi <nahi@ruby-lang.org>.
# This program is copyrighted free software by NAKAMURA, Hiroshi. You can
# redistribute it and/or modify it under the same terms of Ruby's license;
# either the dual license version in 2003, or any later version.
require 'xsd/datatypes'
require 'soap/soap'
require 'soap/element'
require 'soap/parser'
require 'soap/generator'
require 'soap/encodingstyle/soapHandler'
require 'soap/encodingstyle/literalHandler'
require 'soap/encodingstyle/aspDotNetHandler'
module SOAP
module Processor
@@default_parser_option = {}
class << self
public
def marshal(env, opt = {}, io = nil)
generator = create_generator(opt)
marshalled_str = generator.generate(env, io)
unless env.external_content.empty?
opt[:external_content] = env.external_content
end
marshalled_str
end
def unmarshal(stream, opt = {})
parser = create_parser(opt)
parser.parse(stream)
end
def default_parser_option=(rhs)
@@default_parser_option = rhs
end
def default_parser_option
@@default_parser_option
end
private
def create_generator(opt)
Generator.new(opt)
end
def create_parser(opt)
if opt.empty?
opt = @@default_parser_option
end
::SOAP::Parser.new(opt)
end
end
end
end

View file

@ -0,0 +1,333 @@
# soap/property.rb: SOAP4R - Property implementation.
# Copyright (C) 2000-2007 NAKAMURA, Hiroshi <nahi@ruby-lang.org>.
# This program is copyrighted free software by NAKAMURA, Hiroshi. You can
# redistribute it and/or modify it under the same terms of Ruby's license;
# either the dual license version in 2003, or any later version.
module SOAP
# Property stream format:
#
# line separator is \r?\n. 1 line per a property.
# line which begins with '#' is a comment line. empty line is ignored, too.
# key/value separator is ':' or '='.
# '\' as escape character. but line separator cannot be escaped.
# \s at the head/tail of key/value are trimmed.
#
# '[' + key + ']' indicates property section. for example,
#
# [aaa.bbb]
# ccc = ddd
# eee.fff = ggg
# []
# aaa.hhh = iii
#
# is the same as;
#
# aaa.bbb.ccc = ddd
# aaa.bbb.eee.fff = ggg
# aaa.hhh = iii
#
class Property
FrozenError = (RUBY_VERSION >= "1.9.0") ? RuntimeError : TypeError
include Enumerable
module Util
def const_from_name(fqname)
fqname.split("::").inject(Kernel) { |klass, name| klass.const_get(name) }
end
module_function :const_from_name
def require_from_name(fqname)
require File.join(fqname.split("::").collect { |ele| ele.downcase })
end
module_function :require_from_name
end
def self.load(stream)
new.load(stream)
end
def self.loadproperty(propname)
new.loadproperty(propname)
end
def initialize
@store = Hash.new
@hook = Hash.new
@self_hook = Array.new
@locked = false
end
KEY_REGSRC = '([^=:\\\\]*(?:\\\\.[^=:\\\\]*)*)'
DEF_REGSRC = '\\s*' + KEY_REGSRC + '\\s*[=:]\\s*(.*)'
COMMENT_REGEXP = Regexp.new("^(?:#.*|)$")
CATDEF_REGEXP = Regexp.new("^\\[\\s*#{KEY_REGSRC}\\s*\\]$")
LINE_REGEXP = Regexp.new("^#{DEF_REGSRC}$")
def load(stream)
key_prefix = ""
stream.readlines.each_with_index do |line, lineno|
line.sub!(/\r?\n\z/u, '')
case line
when COMMENT_REGEXP
next
when CATDEF_REGEXP
key_prefix = $1.strip
when LINE_REGEXP
key, value = $1.strip, $2.strip
key = "#{key_prefix}.#{key}" unless key_prefix.empty?
key, value = loadstr(key), loadstr(value)
self[key] = value
else
raise TypeError.new(
"property format error at line #{lineno + 1}: `#{line}'")
end
end
self
end
# find property from $:.
def loadproperty(propname)
return loadpropertyfile(propname) if File.file?(propname)
$:.each do |path|
if File.file?(file = File.join(path, propname))
return loadpropertyfile(file)
end
end
nil
end
# name: a Symbol, String or an Array
def [](name)
referent(name_to_a(name))
end
# name: a Symbol, String or an Array
# value: an Object
def []=(name, value)
name_pair = name_to_a(name).freeze
hooks = assign(name_pair, value)
hooks.each do |hook|
hook.call(name_pair, value)
end
value
end
# value: an Object
# key is generated by property
def <<(value)
self[generate_new_key] = value
end
# name: a Symbol, String or an Array; nil means hook to the root
# cascade: true/false; for cascading hook of sub key
# hook: block which will be called with 2 args, name and value
def add_hook(name = nil, cascade = false, &hook)
if name == nil or name == true or name == false
cascade = name
assign_self_hook(cascade, &hook)
else
assign_hook(name_to_a(name), cascade, &hook)
end
end
def each
@store.each do |key, value|
yield(key, value)
end
end
def empty?
@store.empty?
end
def keys
@store.keys
end
def values
@store.values
end
def lock(cascade = false)
if cascade
each_key do |key|
key.lock(cascade)
end
end
@locked = true
self
end
def unlock(cascade = false)
@locked = false
if cascade
each_key do |key|
key.unlock(cascade)
end
end
self
end
def locked?
@locked
end
protected
def deref_key(key)
check_lock(key)
ref = @store[key] ||= self.class.new
unless propkey?(ref)
raise ArgumentError.new("key `#{key}' already defined as a value")
end
ref
end
def local_referent(key)
check_lock(key)
if propkey?(@store[key]) and @store[key].locked?
raise FrozenError.new("cannot split any key from locked property")
end
@store[key]
end
def local_assign(key, value)
check_lock(key)
if @locked
if propkey?(value)
raise FrozenError.new("cannot add any key to locked property")
elsif propkey?(@store[key])
raise FrozenError.new("cannot override any key in locked property")
end
end
@store[key] = value
end
def local_hook(key, direct)
hooks = []
(@self_hook + (@hook[key] || NO_HOOK)).each do |hook, cascade|
hooks << hook if direct or cascade
end
hooks
end
def local_assign_hook(key, cascade, &hook)
check_lock(key)
@store[key] ||= nil
(@hook[key] ||= []) << [hook, cascade]
end
private
NO_HOOK = [].freeze
def referent(ary)
ary[0..-2].inject(self) { |ref, name|
ref.deref_key(to_key(name))
}.local_referent(to_key(ary.last))
end
def assign(ary, value)
ref = self
hook = NO_HOOK
ary[0..-2].each do |name|
key = to_key(name)
hook += ref.local_hook(key, false)
ref = ref.deref_key(key)
end
last_key = to_key(ary.last)
ref.local_assign(last_key, value)
hook + ref.local_hook(last_key, true)
end
def assign_hook(ary, cascade, &hook)
ary[0..-2].inject(self) { |ref, name|
ref.deref_key(to_key(name))
}.local_assign_hook(to_key(ary.last), cascade, &hook)
end
def assign_self_hook(cascade, &hook)
check_lock(nil)
@self_hook << [hook, cascade]
end
def each_key
self.each do |key, value|
if propkey?(value)
yield(value)
end
end
end
def check_lock(key)
if @locked and (key.nil? or !@store.key?(key))
raise FrozenError.new("cannot add any key to locked property")
end
end
def propkey?(value)
value.is_a?(::SOAP::Property)
end
def name_to_a(name)
case name
when Symbol
[name]
when String
name.scan(/[^.\\]+(?:\\.[^.\\])*/u) # split with unescaped '.'
when Array
name
else
raise ArgumentError.new("Unknown name #{name}(#{name.class})")
end
end
def to_key(name)
name.to_s.downcase
end
def generate_new_key
if @store.empty?
"0"
else
(key_max + 1).to_s
end
end
def key_max
(@store.keys.max { |l, r| l.to_s.to_i <=> r.to_s.to_i }).to_s.to_i
end
def loadpropertyfile(file)
puts "find property at #{file}" if $DEBUG
File.open(file) do |f|
load(f)
end
end
def loadstr(str)
str.gsub(/\\./u) { |c| eval("\"#{c}\"") }
end
end
end
# for ruby/1.6.
unless Enumerable.method_defined?(:inject)
module Enumerable
def inject(init)
result = init
each do |item|
result = yield(result, item)
end
result
end
end
end

View file

@ -0,0 +1,14 @@
# SOAP4R - Proxy library.
# Copyright (C) 2000-2007 NAKAMURA, Hiroshi <nahi@ruby-lang.org>.
# This program is copyrighted free software by NAKAMURA, Hiroshi. You can
# redistribute it and/or modify it under the same terms of Ruby's license;
# either the dual license version in 2003, or any later version.
require 'soap/rpc/proxy'
module SOAP
SOAPProxy = RPC::Proxy
end

Some files were not shown because too many files have changed in this diff Show more