From 91555a512246414e51b95c0b9aed5e8856aeede5 Mon Sep 17 00:00:00 2001 From: takehito sato Date: Tue, 29 Nov 2011 22:22:37 +0900 Subject: [PATCH] Switch to genarate more than one language --- as3test/contract.rb | 44 --- {lib/rpcoder => as3test}/param.rb | 0 as3test/rpcoder_exporter.rb | 43 +++ {lib => as3test}/templates/API.erb | 0 {lib => as3test}/templates/APIDummy.erb | 0 {lib => as3test}/templates/APIInterface.erb | 0 {lib => as3test}/templates/Type.erb | 0 contract.rb | 70 +++++ contract_php.rb | 70 +++++ csharptest/param.rb | 129 ++++++++ csharptest/rpcoder_exporter.rb | 46 +++ csharptest/templates/API.erb | 287 ++++++++++++++++++ csharptest/templates/Dummy.erb | 88 ++++++ csharptest/templates/DummyServer.erb | 113 +++++++ csharptest/templates/Extentions.erb | 41 +++ csharptest/templates/Interface.erb | 67 ++++ csharptest/templates/Type.erb | 22 ++ csharptest/templates/TypeJson.erb | 102 +++++++ jstest/param.rb | 17 ++ jstest/rpcoder_exporter.rb | 26 ++ jstest/templates/Api.erb | 29 ++ lib/camelizer.rb | 16 + lib/rpcoder.rb | 54 +--- lib/rpcoder/function.rb | 13 +- lib/rpcoder/type.rb | 3 +- phptest/param.rb | 90 ++++++ phptest/rpcoder_exporter.rb | 136 +++++++++ .../templates/php_ContractFunctionBase.erb | 85 ++++++ phptest/templates/php_ValidateType.erb | 113 +++++++ phptest/templates/php_api.erb | 104 +++++++ phptest/templates/php_func.erb | 229 ++++++++++++++ phptest/templates/php_path.erb | 15 + phptest/templates/php_type.erb | 153 ++++++++++ 33 files changed, 2116 insertions(+), 89 deletions(-) delete mode 100755 as3test/contract.rb rename {lib/rpcoder => as3test}/param.rb (100%) create mode 100644 as3test/rpcoder_exporter.rb rename {lib => as3test}/templates/API.erb (100%) rename {lib => as3test}/templates/APIDummy.erb (100%) rename {lib => as3test}/templates/APIInterface.erb (100%) rename {lib => as3test}/templates/Type.erb (100%) create mode 100755 contract.rb create mode 100755 contract_php.rb create mode 100644 csharptest/param.rb create mode 100644 csharptest/rpcoder_exporter.rb create mode 100644 csharptest/templates/API.erb create mode 100644 csharptest/templates/Dummy.erb create mode 100644 csharptest/templates/DummyServer.erb create mode 100644 csharptest/templates/Extentions.erb create mode 100644 csharptest/templates/Interface.erb create mode 100644 csharptest/templates/Type.erb create mode 100644 csharptest/templates/TypeJson.erb create mode 100644 jstest/param.rb create mode 100644 jstest/rpcoder_exporter.rb create mode 100644 jstest/templates/Api.erb create mode 100644 lib/camelizer.rb create mode 100644 phptest/param.rb create mode 100644 phptest/rpcoder_exporter.rb create mode 100644 phptest/templates/php_ContractFunctionBase.erb create mode 100644 phptest/templates/php_ValidateType.erb create mode 100644 phptest/templates/php_api.erb create mode 100644 phptest/templates/php_func.erb create mode 100755 phptest/templates/php_path.erb create mode 100644 phptest/templates/php_type.erb diff --git a/as3test/contract.rb b/as3test/contract.rb deleted file mode 100755 index 4bb452d..0000000 --- a/as3test/contract.rb +++ /dev/null @@ -1,44 +0,0 @@ -#!/usr/bin/env ruby -# encoding: utf-8 - -$LOAD_PATH.unshift(File.expand_path('../lib', File.dirname(__FILE__))) -require 'rpcoder' -require 'fileutils' - -####################################### -# API definition -####################################### - -RPCoder.name_space = 'com.oneup.rpcoder.generated' -RPCoder.api_class_name = 'API' - -RPCoder.type "Mail" do |t| - t.add_field :subject, :String - t.add_field :body, :String -end - -RPCoder.function "getMail" do |f| - f.path = "/mails/:id" # => ("/mails/" + id) - f.method = "GET" - f.add_return_type :mail, "Mail" - f.add_param :id, "int" - f.description = 'メールを取得' -end - -RPCoder.function "sendMail" do |f| - f.path = "/mails" # => ("/mails/" + id) - f.method = "POST" - f.add_param :subject, "String" - f.add_param :body, "String" - f.description = 'メールを送信' -end - -RPCoder.function "getError" do |f| - f.path = "/error/:statusCode" - f.method = "GET" - f.add_param :statusCode, :int - f.description = 'Get Error' -end - -dir = File.expand_path('src', File.dirname(__FILE__)) -RPCoder.export(dir) diff --git a/lib/rpcoder/param.rb b/as3test/param.rb similarity index 100% rename from lib/rpcoder/param.rb rename to as3test/param.rb diff --git a/as3test/rpcoder_exporter.rb b/as3test/rpcoder_exporter.rb new file mode 100644 index 0000000..d5708a0 --- /dev/null +++ b/as3test/rpcoder_exporter.rb @@ -0,0 +1,43 @@ +module RPCoder + class << self + def export() + class_dir = dir_to_export_classes(@output_path) + FileUtils.mkdir_p(class_dir) + + [ + {:path => File.join(class_dir, api_class_name.split('.').last + "Interface.as"), :content => render_functions_interface}, + {:path => File.join(class_dir, api_class_name.split('.').last + ".as"), :content => render_functions}, + {:path => File.join(class_dir, api_class_name.split('.').last + "Dummy.as"), :content => render_functions_dummy}, + ].each do |hash| + puts "API: #{hash[:path]}" + File.open(hash[:path], "w") { |file| file << hash[:content] } + end + types.each { |type| export_type(type, File.join(class_dir, "#{type.name}.as")) } + end + + def render_functions_interface + render_erb(template_path('APIInterface'), binding) + end + + def render_functions + render_erb(template_path('API'), binding) + end + + def render_functions_dummy + render_erb(template_path('APIDummy'), binding) + end + + def export_type(type, path) + puts "Type: #{path}" + File.open(path, "w") { |file| file << render_type(type) } + end + + def render_type(type) + render_erb(template_path('Type'), binding) + end + + def dir_to_export_classes(dir) + File.join(dir, *name_space.split('.')) + end + end +end diff --git a/lib/templates/API.erb b/as3test/templates/API.erb similarity index 100% rename from lib/templates/API.erb rename to as3test/templates/API.erb diff --git a/lib/templates/APIDummy.erb b/as3test/templates/APIDummy.erb similarity index 100% rename from lib/templates/APIDummy.erb rename to as3test/templates/APIDummy.erb diff --git a/lib/templates/APIInterface.erb b/as3test/templates/APIInterface.erb similarity index 100% rename from lib/templates/APIInterface.erb rename to as3test/templates/APIInterface.erb diff --git a/lib/templates/Type.erb b/as3test/templates/Type.erb similarity index 100% rename from lib/templates/Type.erb rename to as3test/templates/Type.erb diff --git a/contract.rb b/contract.rb new file mode 100755 index 0000000..506f872 --- /dev/null +++ b/contract.rb @@ -0,0 +1,70 @@ +#!/usr/bin/env ruby +# encoding: utf-8 + +abort 'environment parameter "PL" required' unless ENV['PL'] +lang_path = ':LANG'.sub(':LANG',ENV['PL']) +$LOAD_PATH.unshift(File.expand_path(lang_path, File.dirname(__FILE__))) + +$LOAD_PATH.unshift(File.expand_path('./lib', File.dirname(__FILE__))) +require 'rpcoder' +require 'fileutils' + +####################################### +# API definition +####################################### + +RPCoder.output_path = File.expand_path("#{lang_path}/src", File.dirname(__FILE__)) +RPCoder.templates_path = lang_path, 'templates' +RPCoder.name_space = 'com.aiming.rpcoder.generated' +RPCoder.api_class_name = 'API' + +# Constants ------------------------------------------------------------------------------------------------------------ +# コントラクトバージョンのパラメータ名 +CONTRACT_VERSION_VARIABLE_NAME = 'contract_version' + +# ユーザ登録 +CONTRACT_USER_NAME_LENGTH_MAX = 16 # 登録名の最大長 (半角16,全角8文字) + +# 生産兵数 +CONTRACT_TRAIN_TROOP_COUNT_MAX = 9999 # 最大数 +CONTRACT_TRAIN_TROOP_COUNT_MIN = 1 # 最小数 + +# マップ +CONTRACT_MAP_MAX = 500 # 最大サイズ +CONTRACT_MAP_MIN = -500 # 最小サイズ + +# マップブックマーク +CONTRACT_BOOKMARK_MAP_NAME_LENGTH_MAX = 14 # 登録名の最大長 + + +# Type ----------------------------------------------------------------------------------------------------------------- +RPCoder.type "Mail" do |t| + t.add_field :subject, :String + t.add_field :body, :String +end + +# Functions ------------------------------------------------------------------------------------------------------------ +RPCoder.function "getMail" do |f| + f.path = "/mails/:id" # => ("/mails/" + id) + f.method = "GET" + f.add_return_type :mail, "Mail" + f.add_param :id, "int" + f.description = 'メールを取得' +end + +RPCoder.function "sendMail" do |f| + f.path = "/mails" # => ("/mails/" + id) + f.method = "POST" + f.add_param :subject, "String" + f.add_param :body, "String" + f.description = 'メールを送信' +end + +RPCoder.function "getError" do |f| + f.path = "/error/:statusCode" + f.method = "GET" + f.add_param :statusCode, :int + f.description = 'Get Error' +end + +RPCoder.export() diff --git a/contract_php.rb b/contract_php.rb new file mode 100755 index 0000000..ca41cd7 --- /dev/null +++ b/contract_php.rb @@ -0,0 +1,70 @@ +#!/usr/bin/env ruby +# encoding: utf-8 + +abort 'environment parameter "PL" required' unless ENV['PL'] +lang_path = ':LANG'.sub(':LANG',ENV['PL']) +$LOAD_PATH.unshift(File.expand_path(lang_path, File.dirname(__FILE__))) + +$LOAD_PATH.unshift(File.expand_path('./lib', File.dirname(__FILE__))) +require 'rpcoder' +require 'fileutils' + +####################################### +# API definition +####################################### + +RPCoder.output_path = File.expand_path("#{lang_path}/src", File.dirname(__FILE__)) +RPCoder.templates_path = lang_path, 'templates' +RPCoder.name_space = 'com.aiming.rpcoder.generated' +RPCoder.api_class_name = 'API' + +# Constants ------------------------------------------------------------------------------------------------------------ +# コントラクトバージョンのパラメータ名 +CONTRACT_VERSION_VARIABLE_NAME = 'contract_version' + +# ユーザ登録 +CONTRACT_USER_NAME_LENGTH_MAX = 16 # 登録名の最大長 (半角16,全角8文字) + +# 生産兵数 +CONTRACT_TRAIN_TROOP_COUNT_MAX = 9999 # 最大数 +CONTRACT_TRAIN_TROOP_COUNT_MIN = 1 # 最小数 + +# マップ +CONTRACT_MAP_MAX = 500 # 最大サイズ +CONTRACT_MAP_MIN = -500 # 最小サイズ + +# マップブックマーク +CONTRACT_BOOKMARK_MAP_NAME_LENGTH_MAX = 14 # 登録名の最大長 + + +# Type ----------------------------------------------------------------------------------------------------------------- +RPCoder.type "Mail" do |t| + t.add_field :subject, :String + t.add_field :body, :String +end + +# Functions ------------------------------------------------------------------------------------------------------------ +RPCoder.function "getMail" do |f| + f.path = "/getmails.php" # => ("/mails/" + id) + f.method = "GET" + f.add_return_type :mail, "Mail" + f.add_param :id, "int" + f.description = 'メールを取得' +end + +RPCoder.function "sendMail" do |f| + f.path = "/sendmails.php" # => ("/mails/" + id) + f.method = "POST" + f.add_param :subject, "String" + f.add_param :body, "String" + f.description = 'メールを送信' +end + +RPCoder.function "getError" do |f| + f.path = "/error.php" + f.method = "GET" + f.add_param :statusCode, :int + f.description = 'Get Error' +end + +RPCoder.export() diff --git a/csharptest/param.rb b/csharptest/param.rb new file mode 100644 index 0000000..87bd695 --- /dev/null +++ b/csharptest/param.rb @@ -0,0 +1,129 @@ +require 'camelizer' + +module RPCoder + class Param + def self.original_types + [:int, :Int, :double, :Double, :string, :String, :bool, :Boolean, :Array] + end + + attr_accessor :name, :type, :options + def initialize(name, type, options = {}) + @name = name + @type = type + @options = options + end + + def array? + options[:array?] + end + + def optional? + options[:require] == false + end + + def array_or_type + if array? + "List<#{to_c_sharp_type}>" + else + to_c_sharp_type + end + end + + def to_c_sharp_type + case type.to_sym + when :int + if self.optional? + return :int? + else + return :int + end + when :Int + if self.optional? + return :int? + else + return :int + end + when :double + if self.optional? + return :double? + else + return :double + end + when :Double + if self.optional? + return :double? + else + return :double + end + when :string + return :string + when :String + return :string + when :bool + if self.optional? + return :bool? + else + return :bool + end + when :Boolean + if self.optional? + return :bool? + else + return :bool + end + else + return type + end + end + + def to_json_type? + case type.to_sym + when :int + return "IsInt" + when :Int + return "IsInt" + when :double + return "IsDouble" + when :Double + return "IsDouble" + when :string + return "IsString" + when :String + return "IsString" + when :bool + return "IsBoolean" + when :Boolean + return "IsBoolean" + else + return type + end + end + + def original_type? + Param.original_types.include?(type.to_sym) + end + + def double? + self.to_json_type? == "IsDouble" + end + + def instance_creator(elem = 'elem', options = {}) + elem = element_accessor(elem, options) + if original_type? + elem + else + "new #{type}(#{elem})" + end + end + + def element_accessor(elem = 'elem', options = {}) + if options[:object?] + "object['#{elem}']" + else + elem + end + end + + end +end + diff --git a/csharptest/rpcoder_exporter.rb b/csharptest/rpcoder_exporter.rb new file mode 100644 index 0000000..6fd2184 --- /dev/null +++ b/csharptest/rpcoder_exporter.rb @@ -0,0 +1,46 @@ +# csharp rpcoder +module RPCoder + class << self + @@templates = ["API", "Interface", "Dummy", "DummyServer", "Extentions"] + @@extra_templates = [] + + def export() + class_dir = dir_to_export_classes(@output_path) + FileUtils.mkdir_p(class_dir) + @@templates.map { |name| + create_binding(name) + }.concat(@@extra_templates).each do |hash| + path = get_export_file_name(class_dir, hash[:path]) + puts "API: #{path}" + File.open(path, "w") { |file| file << render_erb(hash[:template], binding) } + end + types.each { |type| export_type(type, 'Type', File.join(class_dir, "#{type.name}.cs")) } + types.each { |type| export_type(type, 'TypeJson', File.join(class_dir, "#{type.name}.json.cs")) } + end + + def create_binding(name) + {:path => name, :template => template_path(name) } + end + + def get_export_file_name(class_dir, name) + File.join(class_dir, api_class_name.split('.').last + name + ".cs") + end + + def export_type(type, template_name, path) + puts "Type: #{path}" + File.open(path, "w") { |file| file << render_type(type, template_name) } + end + + def render_type(type, template_name) + render_erb(template_path(template_name), binding) + end + + def dir_to_export_classes(dir) + File.join(dir, *name_space.split('.')) + end + +# def add_template(item_name, template_path) +# @@extra_templates << {:path => item_name, :template => template_path} +# end + end +end diff --git a/csharptest/templates/API.erb b/csharptest/templates/API.erb new file mode 100644 index 0000000..9f63e6c --- /dev/null +++ b/csharptest/templates/API.erb @@ -0,0 +1,287 @@ +/* generated by rpcoder */ +using UnityEngine; +using LitJson; +using System; +using System.Collections; +using System.Collections.Generic; +using System.Text; + +namespace <%= name_space %> +{ + public class <%= api_class_name %> : <%= api_class_name %>Interface + { + public const string RESTART = "RESTART"; + public const string RETENTION = "RETENTION"; + public const string TRANSITION = "TRANSITION"; + public const string RELOAD = "RELOAD"; + + public string BaseUrl { get; set; } + public RpcoderLogger Logger { get; set; } + private void Log(string log) + { + if(Logger != null) Logger(log); + } + + public event Action Responded; + private void RaiseResponded(JsonData jsonData) + { + var d = Responded; + if (d != null) d(jsonData); + } + + public Func CustomValidator; + private string Validate(string source) + { + var d = CustomValidator; + if(d != null) + { + return CustomValidator(source); + } + else + { + return source; + } + } + + private Dictionary commonParameterList = new Dictionary(); + + public void AddCommonParameter(string key, string value) + { + commonParameterList[key] = value; + } + + public string RemoveCommonParameter(string key) + { + string returnValue = ""; + if (commonParameterList.ContainsKey(key)) + { + returnValue = commonParameterList[key]; + commonParameterList.Remove(key); + } + return returnValue; + } + + public string DefaultErrorMessage { get; set; } + public RpcoderErrorHandler ErrorHandler { get; set; } + + public <%= api_class_name %>(string baseUrl) + { + BaseUrl = baseUrl; + DefaultErrorMessage = ""; + JsonMapper.RegisterImporter((i) => (double?)i); + JsonMapper.RegisterImporter((i) => (double)i); + } + + private string AddQueryDelimiter(string source) + { + if(source == "") + { + source += "?"; + } + else + { + source += "&"; + } + return source; + } + + <%- functions.each do |func| -%> + <%- + response = func.name.camelize + "Response" + successHandler = "Action<" + response + ">" + params_excluded_error_handler = func.params.map{|i| [i.array_or_type, i.name.to_s.camelize(false)].join(' ') } + [successHandler + ' success'] + params = params_excluded_error_handler + ['RpcoderErrorHandler error'] + -%> + /// + /// <%= func.description %> + /// + <%- func.params.each do |param| -%> + /// <%= param.options[:description] %> + <%- end -%> + public IEnumerator <%= func.name.camelize %>(<%= params_excluded_error_handler.join(', ') %>) + { + return this.<%= func.name.camelize %>(<%= (func.params.map{|i| i.name.to_s.camelize(false) } + ['success']).join(', ') %>, null); + } + + /// + /// <%= func.description %> + /// + <%- func.params.each do |param| -%> + /// <%= param.options[:description] %> + <%- end -%> + public IEnumerator <%= func.name.camelize %>(<%= params.join(', ') %>) + { + string path = <%= func.path_parts.join(' + ') %>; + <%- if func.is_get? -%> + string argsPath = ""; + <%- if func.has_query_params? -%> + Dictionary args = new Dictionary(); + <%- func.query_params.each do |i| -%> + <%- if i.optional? -%> + if (<%= i.name.to_s.camelize(false) %> != null) + <%- end -%> + { + <%- if i.array? -%> + JsonData arrayData = new JsonData(); + for (int i = 0; i < <%= i.name.to_s.camelize(false) %>.Count; ++i) + { + arrayData.Add(<%= i.name.to_s.camelize(false) %>[i]); + } + args["<%= i.name.to_s %>"] = arrayData.ToJson(); + <%- else -%> + args["<%= i.name.to_s %>"] = <%= i.name.to_s.camelize(false) %>; + <%- end -%> + } + <%- end -%> + foreach(string key in args.Keys) + { + argsPath = AddQueryDelimiter(argsPath); + argsPath += WWW.EscapeURL(key) + "=" + WWW.EscapeURL(args[key].ToString()); + } + <%- end -%> + + foreach(string key in commonParameterList.Keys) + { + argsPath = AddQueryDelimiter(argsPath); + argsPath += WWW.EscapeURL(key) + "=" + WWW.EscapeURL(commonParameterList[key]); + } + string fullPath = this.BaseUrl + path + argsPath; + Log("request " + fullPath); + WWW www = new WWW(fullPath); + <%- else -%> + JsonData data = new JsonData(); + + <%- if func.has_query_params? -%> + <%- func.query_params.each do |i| -%> + <%- if i.optional? -%> + if (<%= i.name.to_s.camelize(false) %> != null) + <%- end -%> + { + <%- if i.array? -%> + JsonData arrayData = new JsonData(); + var count = <%= i.name.to_s.camelize(false) %>.Count; + if(count > 0) + { + for (int i = 0; i < count; ++i) + { + arrayData.Add(<%= i.name.to_s.camelize(false) %>[i]); + } + } + else + { + arrayData.Add(0); + arrayData.Clear(); + } + data["<%= i.name.to_s %>"] = arrayData; + <%- else -%> + data["<%= i.name.to_s %>"] = <%= i.name.to_s.camelize(false) %>; + <%- end -%> + } + <%- end -%> + <%- end -%> + + foreach(string key in commonParameterList.Keys) + { + data[key] = commonParameterList[key]; + } + + var json = data.ToJson(); + byte[] byteArray = Encoding.UTF8.GetBytes(json == String.Empty ? " " : json); + Log("request " + this.BaseUrl + path + " " + data.ToJson()); + + WWW www = new WWW(this.BaseUrl + path, byteArray); + <%- end -%> + yield return www; + + if(error == null) + { + error = ErrorHandler; + } + + if(www.error != null) + { + error(RETENTION, DefaultErrorMessage + (Debug.isDebugBuild ? "\n" + this.BaseUrl + path + " " + data.ToJson() + "\n" + www.error : ""), null); + } + else + { + Log("<%= func.name.camelize %> responded " + www.text); + try + { + var text = Validate(www.text); + JsonData jsonData = JsonMapper.ToObject(text); + <%= api_class_name %>.HandleDebugMessage(jsonData, Log); + try + { + <%= api_class_name %>.HandleError(jsonData, error); + } + catch(KeyNotFoundException) + { + try + { + <%- + args = [] + if func.has_return_type? + args = func.return_types.map {|param| + if param.array? + if param.original_type? + param.name.to_s.camelize + " = jsonData.ContainsKey(\"#{param.name}\") ? JsonMapper.ToObject<#{param.array_or_type}>(jsonData[\"#{param.name}\"].ToJson()) : null" + else + param.name.to_s.camelize + " = #{param.type}.CreateList(jsonData.GetValueOrDefault(\"#{param.name}\"))" + end + else + if param.original_type? + param.name.to_s.camelize + " = (#{param.array_or_type})jsonData.GetValueOrDefault(\"#{param.name}\")" + else + param.name.to_s.camelize + " = #{param.type}.Create(jsonData.GetValueOrDefault(\"#{param.name}\"))" + end + end + } + end + -%> + success(new <%= response %>(){<%= args.join(",\n\t\t\t\t\t\t\t") %>}); + RaiseResponded(jsonData); + } + catch(KeyNotFoundException) + { + error(RESTART, "Json parse Error", null); + } + } + } + catch(JsonException) + { + error(RESTART, "Json parse Error\n" + www.text, null); + } + } + } + + <%- end -%> + + public static void HandleError(JsonData jsonData, RpcoderErrorHandler error) + { + JsonData errorJsonData = jsonData["rpcoderError"]; + string errorType = (string)errorJsonData["errorType"]; + string message = ""; + JsonData messageJson; + if(errorJsonData.TryGetValue("message", out messageJson)) + { + message = (string)messageJson; + } + JsonData targetSceneJson; + string targetScene = null; + if(errorJsonData.TryGetValue("targetScene", out targetSceneJson)) + { + targetScene = (string)targetSceneJson; + } + error(errorType, message, targetScene); + } + + public static void HandleDebugMessage(JsonData jsonData, Action logger) + { + JsonData message; + if(jsonData.TryGetValue("debugMessage", out message)) + { + logger((string)message); + } + } + } +} diff --git a/csharptest/templates/Dummy.erb b/csharptest/templates/Dummy.erb new file mode 100644 index 0000000..c8ce239 --- /dev/null +++ b/csharptest/templates/Dummy.erb @@ -0,0 +1,88 @@ +/* generated by rpcoder */ +using System; +using System.Collections; +using System.Collections.Generic; + +namespace <%= name_space %> +{ + public class <%= api_class_name %>Dummy : <%= api_class_name %>Interface + { + public RpcoderErrorHandler ErrorHandler { get; set; } + public string BaseUrl { get; set; } + public RpcoderLogger Logger { get; set; } + public void AddCommonParameter(string key, string value) + { + } + public string RemoveCommonParameter(string key) + { + return ""; + } + + private RpcoderError error = null; + + private Dictionary dummySuccess = new Dictionary(); + + public void SetDummySuccess(string functionName, ArrayList success) + { + dummySuccess.Add(functionName, success); + } + + private ArrayList GetDummySuccess(string functionName) + { + return dummySuccess[functionName]; + } + + public void SetDummyError(RpcoderError error) + { + this.error = error; + } + + private bool IsError { get { return this.error != null; } } + + <%- functions.each do |func| -%> + <%- + response = func.name.camelize + "Response" + successHandler = "Action<" + response + ">" + params_excluded_error_handler = func.params.map{|i| [i.array_or_type, i.name.to_s.camelize(false)].join(' ') } + [successHandler + ' success'] + params = params_excluded_error_handler + ['RpcoderErrorHandler error'] + -%> + public IEnumerator <%= func.name.camelize %>(<%= params_excluded_error_handler.join(', ') %>) + { + return this.<%= func.name.camelize %>(<%= (func.params.map{|i| i.name.to_s.camelize(false) } + ['success']).join(', ') %>, null); + } + + public IEnumerator <%= func.name.camelize %>(<%= params.join(', ') %>) + { + if( !HandleError(error) ) + { + <%- if func.has_return_type? -%> + ArrayList arguments = GetDummySuccess("<%= func.name.camelize %>"); + <%- end-%> + <%- + index = -1 + -%> + success(new <%= response %>(){<%= func.return_types.map{|i| i.name.to_s.camelize + " = (#{i.array_or_type})arguments[#{index+=1}]" }.join(",\n\t\t\t\t\t") %>}); + } + yield return null; + } + + <%- end -%> + + private bool HandleError(RpcoderErrorHandler errorHandler) + { + if( IsError ) + { + if(errorHandler != null) + { + errorHandler(error.ErrorType, error.Message, error.TargetScene); + } + else + { + ErrorHandler(error.ErrorType, error.Message, error.TargetScene); + } + return true; + } + return false; + } + } +} diff --git a/csharptest/templates/DummyServer.erb b/csharptest/templates/DummyServer.erb new file mode 100644 index 0000000..b51e707 --- /dev/null +++ b/csharptest/templates/DummyServer.erb @@ -0,0 +1,113 @@ +/* generated by rpcoder */ +using System; +using System.Collections; +using System.Collections.Generic; + +namespace <%= name_space %> +{ + public class <%= api_class_name %>DummyServer : <%= api_class_name %>Interface + { + public <%= api_class_name %>DummyServer(<%= api_class_name %>Interface api) + { + _api = api; + } + + private <%= api_class_name %>Interface _api; + + public RpcoderErrorHandler ErrorHandler + { + get + { + return _api.ErrorHandler; + } + set + { + _api.ErrorHandler = value; + } + } + + public string BaseUrl + { + get + { + return _api.BaseUrl; + } + set + { + _api.BaseUrl = value; + } + } + + public RpcoderLogger Logger + { + get + { + return _api.Logger; + } + set + { + _api.Logger = value; + } + } + + public void AddCommonParameter(string key, string value) + { + _api.AddCommonParameter(key, value); + } + + public string RemoveCommonParameter(string key) + { + return _api.RemoveCommonParameter(key); + } + + <%- functions.each do |func| -%> + <%- + response = func.name.camelize + "Response" + successHandler = "Action<" + response + ">" + request_query = func.params.map{|i| [i.array_or_type, i.name.to_s.camelize(false)].join(' ') } + params_excluded_error_handler = request_query + [successHandler + ' success'] + params = params_excluded_error_handler + ['RpcoderErrorHandler error'] + -%> + public class <%= func.name.camelize %>Request + { + <%- func.params.each do |i| -%> + public <%= i.array_or_type %> <%= i.name.to_s.camelize %>; + <%- end -%> + public <%= successHandler %> Success; + public RpcoderErrorHandler Error; + } + + public Func<<%= func.name.camelize %>Request, IEnumerator> <%= func.name.camelize %>Logic { get; set; } + + public IEnumerator <%= func.name.camelize %>(<%= params_excluded_error_handler.join(', ') %>) + { + return this.<%= func.name.camelize %>(<%= (func.params.map{|i| i.name.to_s.camelize(false) } + ['success']).join(', ') %>, null); + } + + public IEnumerator <%= func.name.camelize %>(<%= params.join(', ') %>) + { + if(<%= func.name.camelize %>Logic != null) + { + return <%= func.name.camelize %>Logic(new <%= func.name.camelize %>Request(){ + <%- func.params.each do |i| -%> + <%= i.name.to_s.camelize %> = <%= i.name.to_s.camelize(false) %>, + <%- end -%> + Success = success, + Error = (error != null) ? error : ErrorHandler + }); + } + else + { + return _api.<%= func.name.camelize %>( + <%- func.params.each do |i| -%> + <%= i.name.to_s.camelize(false) %>, + <%- end -%> + success, + (error != null) ? error : ErrorHandler + ); + } + } + + <%- end -%> + } +} diff --git a/csharptest/templates/Extentions.erb b/csharptest/templates/Extentions.erb new file mode 100644 index 0000000..d2f44f1 --- /dev/null +++ b/csharptest/templates/Extentions.erb @@ -0,0 +1,41 @@ +using <%= name_space %>; +using System.Collections.Generic; + +namespace Aiming.TSS.Models.Tasks +{ + public static class ApiExtensions + { + <%- functions.each do |func| -%> + <%- + response = func.name.camelize + "Response" + params = func.params.map{|i| [i.array_or_type, i.name.to_s.camelize(false)].join(' ') }.join(', ') + params = ', ' + params if not params.empty? + args = func.params.map{|i| i.name.to_s.camelize(false) }.join(', ') + args = args + ', ' if not params.empty? + -%> + public static Task<<%= response %>> <%= func.name.camelize %>Async(this APIInterface api<%= params %>) + { + return new Task<<%= response %>>(success => api.<%= func.name.camelize %>(<%= args %>success, ThrowOnError)); + } + + <%- end -%> + + private static void ThrowOnError(string errorType, string message, string targetScene) + { + throw new RpcoderException(errorType, message, targetScene); + } + } + + public partial class RpcoderException : System.Exception + { + public string ErrorType { get; private set; } + public string TargetScene { get; private set; } + + public RpcoderException(string errorType, string message, string targetScene) + : base(message) + { + ErrorType = errorType; + TargetScene = targetScene; + } + } +} \ No newline at end of file diff --git a/csharptest/templates/Interface.erb b/csharptest/templates/Interface.erb new file mode 100644 index 0000000..926f34b --- /dev/null +++ b/csharptest/templates/Interface.erb @@ -0,0 +1,67 @@ +/* generated by rpcoder */ + +namespace <%= name_space %> +{ + using System; + using System.Collections; + using System.Collections.Generic; + + public delegate void RpcoderLogger(string log); + public delegate void RpcoderErrorHandler(string errorType, string message, string targetScene); + + public interface <%= api_class_name %>Interface + { + string BaseUrl { get; set; } + RpcoderLogger Logger { get; set; } + void AddCommonParameter(string key, string value); + string RemoveCommonParameter(string key); + RpcoderErrorHandler ErrorHandler{ get; set; } + + <%- functions.each do |func| -%> + <%- + successHandler = "Action<" + func.name.camelize + "Response>" + params_excluded_error_handler = func.params.map{|i| [i.array_or_type, i.name.to_s.camelize(false)].join(' ') } + [successHandler + ' success'] + params = params_excluded_error_handler + ['RpcoderErrorHandler error'] + -%> + /// + /// <%= func.description %> + /// + <%- func.params.each do |param| -%> + /// <%= param.options[:description] %> + <%- end -%> + IEnumerator <%= func.name.camelize %>(<%= params_excluded_error_handler.join(', ') %>); + + /// + /// <%= func.description %> + /// + <%- func.params.each do |param| -%> + /// <%= param.options[:description] %> + <%- end -%> + IEnumerator <%= func.name.camelize %>(<%= params.join(', ') %>); + + <%- end -%> + } + + <%- functions.each do |func| -%> + public class <%= func.name.camelize + "Response" %> + { + <%- func.return_types.each do |i| -%> + public <%= i.array_or_type %> <%= i.name.to_s.camelize %>; + <%- end -%> + } + + <%- end -%> + public class RpcoderError + { + public string ErrorType { get; private set; } + public string Message { get; private set; } + public string TargetScene { get; private set; } + + public RpcoderError(string errorType, string message, string targetScene) + { + ErrorType = errorType; + Message = message; + TargetScene = targetScene; + } + } +} diff --git a/csharptest/templates/Type.erb b/csharptest/templates/Type.erb new file mode 100644 index 0000000..b976d05 --- /dev/null +++ b/csharptest/templates/Type.erb @@ -0,0 +1,22 @@ +/* generated by rpcoder */ + +namespace <%= name_space %> +{ + using System.Collections.Generic; + + public partial class <%= type.name %> + { + <%- type.fields.each do |field| -%> + public <%= field.array_or_type %> <%= field.name.to_s.camelize %> { get; set; } + <%- end -%> + + public <%= type.name %>() {} + + public <%= type.name %>(<%= type.fields.map {|i| "#{i.array_or_type} #{i.name.to_s.camelize(false)}" }.join(', ') %>) + { + <%- type.fields.each do |field| -%> + this.<%= field.name.to_s.camelize %> = <%= field.name.to_s.camelize(false) %>; + <%- end -%> + } + } +} diff --git a/csharptest/templates/TypeJson.erb b/csharptest/templates/TypeJson.erb new file mode 100644 index 0000000..4d546bb --- /dev/null +++ b/csharptest/templates/TypeJson.erb @@ -0,0 +1,102 @@ +/* generated by rpcoder */ + +namespace <%= name_space %> +{ + using System.Collections.Generic; + using System.Linq; + using LitJson; + + public partial class <%= type.name %> + { + public override string ToString() + { + var items = new[] + { + <%- type.fields.each do |x| -%> + "\"<%= x.name.to_s %>\":" + + <%- if x.array? then -%> + "[" + string.Join(",", <%= x.name.to_s.camelize %>.Select(i => i.ToString()).ToArray()) + "]", + <%- elsif x.array_or_type.to_s == "string" then -%> + "\"" + <%= x.name.to_s.camelize %> + "\"", + <%- elsif x.array_or_type.to_s == "bool" then -%> + (<%= x.name.to_s.camelize %> ? "true" : "false"), + <%- elsif x.optional? -%> + ((<%= x.name.to_s.camelize %> != null) ? <%= x.name.to_s.camelize %>.ToString() : "null"), + <%- else -%> + <%= x.name.to_s.camelize %>.ToString(), + <%- end -%> + <%- end -%> + }; + + return "{" + string.Join(",", items) + "}"; + } + + public static <%= type.name %> Create(JsonData jsonData) + { + if(jsonData == null) return null; + <%= type.name %> createdData = new <%= type.name %>(); + + <%- type.fields.each do |field| -%> + { + JsonData temp; + if(jsonData.TryGetValue("<%= field.name.to_s %>", out temp)) + { + <%- if field.original_type? -%> + <%- if field.array? -%> + createdData.<%= field.name.to_s.camelize %> = JsonMapper.ToObject<<%= field.array_or_type %>>(temp.ToJson()); + <%- else -%> + <%- if field.double? -%> + if(temp.<%= field.to_json_type? %>) + { + createdData.<%= field.name.to_s.camelize %> = (<%= field.array_or_type %>)temp; + } + else + { + createdData.<%= field.name.to_s.camelize %> = <%- if field.optional? -%>(int?)<%- else -%>(int)<%- end -%>temp; + } + <%- else -%> + createdData.<%= field.name.to_s.camelize %> = (<%= field.array_or_type %>)temp; + <%- end -%> + <%- end -%> + <%- else -%> + <%- if field.array? -%> + createdData.<%= field.name.to_s.camelize %> = <%= name_space %>.<%= field.type %>.CreateList(temp); + <%- else -%> + createdData.<%= field.name.to_s.camelize %> = <%= name_space %>.<%= field.type %>.Create(temp); + <%- end -%> + <%- end -%> + } + <%- if field.optional? -%> + else + createdData.<%= field.name.to_s.camelize %> = null; + <%- end -%> + } + + <%- end -%> + + return createdData; + } + + public static List<<%= type.name %>> CreateList(JsonData jsonData) + { + if(jsonData == null) return null; + List<<%= type.name %>> createdList = new List<<%= type.name %>>(); + for(int i = 0; i < jsonData.Count; ++i) + { + createdList.Add(<%= type.name %>.Create(jsonData[i])); + } + + return createdList; + } + + public static <%= type.name %> Parse(string json) + { + return Create(LitJson.JsonMapper.ToObject(json)); + } + + public static List<<%= type.name %>> ParseList(string json) + { + return CreateList(LitJson.JsonMapper.ToObject(json)); + } + } +} diff --git a/jstest/param.rb b/jstest/param.rb new file mode 100644 index 0000000..3d0c602 --- /dev/null +++ b/jstest/param.rb @@ -0,0 +1,17 @@ +# encoding: utf-8 +# js +module RPCoder + class Param + def self.original_types + [:int, :String, :Boolean, :Array, :Hash] + end + + attr_accessor :name, :type, :options + def initialize(name, type, options = {}) + @name = name + @type = type + @options = options + end + + end +end diff --git a/jstest/rpcoder_exporter.rb b/jstest/rpcoder_exporter.rb new file mode 100644 index 0000000..c8eba12 --- /dev/null +++ b/jstest/rpcoder_exporter.rb @@ -0,0 +1,26 @@ +# encoding: utf-8 +# js rpcoder +module RPCoder + class << self + def export() + # Api毎にファイル化する --------------------------------------------- + erb_name = 'Api' + parent_path = 'app/assets/javascripts/apis' + + functions.each do |func| + dir_path = File.join(@output_path, parent_path) # 出力先パスを生成する + FileUtils.mkdir_p(dir_path) # 出力先ディレクトリがなければ作成する + + file_path = File.join(dir_path, func.name.downcase + "_api.js.coffee") + puts "Api #{erb_name} : #{file_path}" + File.open(file_path, "w") do |file| file << render_function(func, erb_name) end + end + + end + + # function用のファイル生成 + def render_function(func, erb_name) + render_erb(template_path("#{erb_name}"), binding) + end + end +end diff --git a/jstest/templates/Api.erb b/jstest/templates/Api.erb new file mode 100644 index 0000000..4c02b2a --- /dev/null +++ b/jstest/templates/Api.erb @@ -0,0 +1,29 @@ +# -*- coding: utf-8 -*- +# +# <%= func.description.to_s %> +# + +class <%= name_space %>.<%= func.name %>Api extends <%= name_space %>.ApiBase + url: "<%= func.path.to_s %>" + method: "<%= func.method.to_s %>" + + onSuccess: (data, status_text, xhr) -> + super(data, status_text, xhr) + if data['status'] == 'ok' +<%- if func.name == 'Resource' -%> + models = data['models'] + _.each models, (val) -> + model = BSC.Pool.get().get val['name'] + if model instanceof BSC.CollectionBase + model.reset val['list'] # call Backbone.Collection.reset + else + model.set val['list'] # call Backbone.Model.set + model.trigger('update') +<%- else -%> +<%- end -%> + else + # TODO onSuccessでngってありえる? + + onError: (xhr, status_text, errorThrown) -> + # + super(xhr, status_text, errorThrown) diff --git a/lib/camelizer.rb b/lib/camelizer.rb new file mode 100644 index 0000000..b83abcd --- /dev/null +++ b/lib/camelizer.rb @@ -0,0 +1,16 @@ +class String + def camelize(first_letter_in_uppercase = true) + if first_letter_in_uppercase + self. + gsub(/\/(.?)/) { "::#{$1.upcase}" }. + gsub(/(?:^|_)(.)/) { $1.upcase } + else + camelized = self.camelize + camelized[0].chr.downcase + camelized[1..self.size-1] + end + end + + def camelize!(first_letter_in_uppercase = true) + self.replace(self.camelize first_letter_in_uppercase) + end +end diff --git a/lib/rpcoder.rb b/lib/rpcoder.rb index e74a9f2..fb1a64d 100644 --- a/lib/rpcoder.rb +++ b/lib/rpcoder.rb @@ -1,11 +1,21 @@ # encoding: utf-8 require 'erb' +require 'rpcoder_exporter' require 'rpcoder/function' require 'rpcoder/type' module RPCoder class << self + @templates_path + @output_path + def templates_path=(templates_path) + @templates_path = templates_path + end + def output_path=(output_path) + @output_path = output_path + end + def name_space=(name_space) @name_space = name_space end @@ -46,52 +56,12 @@ def function(name) func end - def export(dir) - class_dir = dir_to_export_classes(dir) - FileUtils.mkdir_p(class_dir) - - [ - {:path => File.join(class_dir, api_class_name.split('.').last + "Interface.as"), :content => render_functions_interface}, - {:path => File.join(class_dir, api_class_name.split('.').last + ".as"), :content => render_functions}, - {:path => File.join(class_dir, api_class_name.split('.').last + "Dummy.as"), :content => render_functions_dummy}, - ].each do |hash| - puts "API: #{hash[:path]}" - File.open(hash[:path], "w") { |file| file << hash[:content] } - end - types.each { |type| export_type(type, File.join(class_dir, "#{type.name}.as")) } - end - - def render_functions_interface - render_erb('APIInterface.erb', binding) - end - - def render_functions - render_erb('API.erb', binding) - end - - def render_functions_dummy - render_erb('APIDummy.erb', binding) - end - - def export_type(type, path) - puts "Type: #{path}" - File.open(path, "w") { |file| file << render_type(type) } - end - - def render_type(type) - render_erb('Type.erb', binding) - end - def render_erb(template, _binding) - ERB.new(File.read(template_path(template)), nil, '-').result(_binding) + ERB.new(File.read(template), nil, '-').result(_binding) end def template_path(name) - File.join File.dirname(__FILE__), 'templates', name - end - - def dir_to_export_classes(dir) - File.join(dir, *name_space.split('.')) + File.join @templates_path, name + '.erb' end def clear diff --git a/lib/rpcoder/function.rb b/lib/rpcoder/function.rb index 314cf93..b1f9ee3 100644 --- a/lib/rpcoder/function.rb +++ b/lib/rpcoder/function.rb @@ -1,4 +1,5 @@ -require 'rpcoder/param' +require 'param' +require 'camelizer' module RPCoder class Function @@ -8,7 +9,7 @@ class Function def params @params ||= [] end - + def return_types @return_types ||= [] end @@ -39,6 +40,14 @@ def query_params params.select { |i| !param_strs.include?(i.name.to_s) } end + def has_query_params? + !query_params.empty? + end + + def is_get? + true if method == "GET" + end + def add_return_type(name, type, options = {}) return_types << Param.new(name, type, options) end diff --git a/lib/rpcoder/type.rb b/lib/rpcoder/type.rb index e2805a3..acfd66d 100644 --- a/lib/rpcoder/type.rb +++ b/lib/rpcoder/type.rb @@ -1,4 +1,5 @@ -require 'rpcoder/param' +require 'param' +require 'camelizer' module RPCoder class Type diff --git a/phptest/param.rb b/phptest/param.rb new file mode 100644 index 0000000..3628e64 --- /dev/null +++ b/phptest/param.rb @@ -0,0 +1,90 @@ +module RPCoder + class Param + def self.original_types + [:int, :String, :Boolean, :Array, :Double] + end + + attr_accessor :name, :type, :options + def initialize(name, type, options = {}) + @name = name + @type = type + @options = options + end + + def array? + options[:array?] + end + + def array_or_type + if array? + "Array" + else + type + end + end + + def original_type? + Param.original_types.include?(type.to_sym) + end + alias :builtin_type? :original_type? + + def array_param + Param.new(name, options[:array_type]) + end + + def instance_creator(elem = 'elem', options = {}) + elem = element_accessor(elem, options) + if original_type? + elem + else + "new #{type}(#{elem})" + end + end + + def element_accessor(elem = 'elem', options = {}) + if options[:object?] + "object['#{elem}']" + else + elem + end + end + + def option_require? + if options[:require].nil? + return true + else + options[:require] + end + end + + def option_escape? + options[:htmlescape] + end + + # パラメータのオプションをphp配列に変換する + def options_to_php_array + str = "" + + options.each do |key, val| + case key.to_s + when :array?.to_s + # array?のとき、[?]を取り除いて追加する + str += "'array' => #{val.to_s}, " + when "require", "max", "min", "length", "ngword", "htmlescape" + # 数値や真偽のとき、何もせずに追加する + str += "'#{key.to_s}' => #{val.to_s}, " + else + # 文字などのとき、値をクォーテーションで囲み追加する + str += "'#{key.to_s}' => '#{val.to_s}', " + end + end + + unless str.empty? + # 空でないとき + str = "array(#{str.sub(/, $/, '')})" # 最後の[, ]を取り除いて配列化 + end + + str + end + end +end diff --git a/phptest/rpcoder_exporter.rb b/phptest/rpcoder_exporter.rb new file mode 100644 index 0000000..70561b7 --- /dev/null +++ b/phptest/rpcoder_exporter.rb @@ -0,0 +1,136 @@ +# encoding: utf-8 +# php rpcoder +module RPCoder + class << self + def export() + + # コントラクトバージョンの取得 ------------------------------------------- + version = "" + contract_path = File.join($PROGRAM_NAME) + open("| git hash-object #{contract_path}") do |f| + version = f.gets.strip # gitコマンドから、コントラクトファイルの最新コミットハッシュを取得する + puts "This Contract Hash (Version) is \"#{version}\"" + end + + # function毎にファイル化する --------------------------------------------- + {"func" => "lib/Contract/Function", "api" => "public_html/api"}.each do |erb_name, parent_path| + if "public_html" === parent_path.split("/").fetch(0) + # public_html配下のとき、path.phpを作成するフラグを立てる + require_pathphp_flg = true + end + dir_path_back = '' # 前回処理したfuncのdir_path + + functions.each do |func| + project_path = File.join(parent_path.split("/"), func.path.to_s.sub(/[^\/]*\.php/, "").sub(/:.*$/, "").split("/")) # プロジェクトのディレクトリ構造 + dir_path = File.join(@output_path, project_path) # 出力先パスを生成する + FileUtils.mkdir_p(dir_path) # 出力先ディレクトリがなければ作成する + + if true === require_pathphp_flg + # public_html下にあるとき + unless dir_path === dir_path_back + # 前回と違うパスのとき + make_pathphp(dir_path, project_path) # 出力先ディレクトリにpath.phpがなければ作成する + end + dir_path_back = dir_path # 前回処理パスを更新する + + # func.pathの通りに生成する + file_path = File.join(@output_path, parent_path.split("/"), func.path.to_s.sub(/:.*$/, "").split("/")) + else + # func.pathのファイル名をfunc.nameに変えて生成する + file_path = File.join(dir_path, func.name + ".php") + end + + puts "PHP #{erb_name} : #{file_path}" + File.open(file_path, "w") do |file| file << render_funcphp(func, erb_name) end + end + end + + # type毎にファイル化する ------------------------------------------------- + {"type" => "lib/Contract/Type"}.each do |erb_name, parent_path| + types.each do |type| + dir_path = File.join(@output_path, parent_path.split("/")) # 出力先パスを生成する + file_path = File.join(dir_path, type.name + ".php") # 出力先ファイル名を生成する + FileUtils.mkdir_p(dir_path) # 出力先ディレクトリがなければ作成する + + puts "PHP #{erb_name} : #{file_path}" + File.open(file_path, "w") do |file| file << render_typephp(type, erb_name) end + end + end + + # functions/typeをひとつのファイルに (継承元になるようなファイルとして) 書き出す + {"ContractFunctionBase" => "lib/Contract", "ValidateType" => "lib/Contract"}.each do |erb_name, parent_path| + dir_path = File.join(@output_path, parent_path.split("/")) # 出力先パスを生成する + file_path = File.join(dir_path, erb_name + ".php") # 出力先ファイル名を生成する + FileUtils.mkdir_p(dir_path) # 出力先ディレクトリがなければ作成する + + puts "PHP #{erb_name} : #{file_path}" + File.open(file_path, "w") do |file| file << render_basephp(version, erb_name) end # コントラクトバージョンも渡す + end + end + + # function用のファイル生成 + def render_funcphp(func, erb_name) + # erb内で"func"にアクセス可能 + if "api" === erb_name + # php_api.erb内で"api_use_types"にアクセス可能 (このfunctionで使用されるtypeの配列) + api_use_types = get_use_types(func.return_types).uniq + end + + render_erb(template_path("php_#{erb_name}"), binding) + end + + # type用のファイル生成 + def render_typephp(type, erb_name) + # erb内で"type"にアクセス可能 + render_erb(template_path("php_#{erb_name}"), binding) + end + + # function/type共用のファイル生成 + def render_basephp(version, erb_name) + # erb内で"version"にアクセス可能 + render_erb(template_path("php_#{erb_name}"), binding) + end + + # + # path.phpがなければ作成 + # + # @param string dir_path_make ファイルを作成するディレクトリ + # @param string dir_path_depth 深さを算出するディレクトリ + # + def make_pathphp(dir_path_make, dir_path_depth) + file_path = File.join(dir_path_make, "path.php") + puts "path : #{file_path}" # 出力ファイルパスを表示 + File.open(file_path, "w") do |file| file << render_pathphp(dir_path_depth.split("/").size) end + end + + # path.php用のファイル生成 + def render_pathphp(depth) + # erb内で"depth"にアクセス可能 + render_erb(template_path("php_path"), binding) + end + + # + # 特定のfunctionで使用されるtypeの配列を取得する + # + # @param object fields 最初呼び出しではfunc.return_types、再帰呼び出しではtype.fields + # @return array use_types 使用されるtypeの配列 + # + def get_use_types(fields) + use_types = [] + + fields.each do |field| + unless field.builtin_type? + # 組み込み型でないとき + use_types.push(field.type.to_s) # それをとっておく + types.each do |type| + if type.name.to_s === field.type.to_s + use_types.concat(get_use_types(type.fields)) # 再帰 + end + end + end + end + + return use_types + end + end +end diff --git a/phptest/templates/php_ContractFunctionBase.erb b/phptest/templates/php_ContractFunctionBase.erb new file mode 100644 index 0000000..ecc1b5c --- /dev/null +++ b/phptest/templates/php_ContractFunctionBase.erb @@ -0,0 +1,85 @@ + + * @copyright infiniteloop + * @license 別紙契約内容を参照 + */ + +define('CONTRACT_VERSION', '<%= version %>'); // コントラクトバージョン +require(LIBPATH . 'lib/Contract/ValidateType.php'); + +class ContractFunctionBase extends ValidateType +{ + private $__posts; // POSTのJSONをデコードした配列 + protected $__add_resp; // レスポンスに増設する項目 + protected $__add_resp_vali_opts; // レスポンスに増設する項目のバリデーションルール + + /** + * コンストラクタ + */ + public function __construct() + { + parent::__construct(); // Validetaクラスのコンストラクタ (int32の設定, hard_validateの設定) + $this->hard_validate = false; // 厳密な型チェックを行なわない + + ini_set('html_errors', 0); // エラー時にhtml出力しない + } + + + + /** + * POSTのJSONをデコードして格納する + */ + protected function decodePostJson() + { + $json = file_get_contents("php://input"); // 生POSTを取得する ($_POSTで受け取れないため) + + if (!is_null($json)) { + $this->__posts = json_decode($json, true); // 配列化デコード + } else { + $this->setErr('POSTのJSONが取得できない (file_get_contents("php://input")で受け取る仕様)'); + } + } + + + + /** + * POSTのJSONをデコードした配列にアクセスする + * + * @param string $key 配列のキー + * @return mixed あるとき:対応するもの, ないとき:null + */ + protected function postJson($key) + { + if (isset($this->__posts[$key])) { + return $this->__posts[$key]; + } else { + return null; + } + } + + + + /** + * レスポンスに項目を増設する + * + * @param string $key 配列のキー + * @param string $value 配列の値 + * @param string $type $valueの型 + * @param array $options オプション配列 + */ + public function addResponse($key, $value, $type = null, $options = array()) + { + if (!is_null($type)) { + $this->__add_resp_vali_opts[$key] = array('type' => $type, 'options' => $options); + } + + $this->__add_resp[$key] = $value; + } +} diff --git a/phptest/templates/php_ValidateType.erb b/phptest/templates/php_ValidateType.erb new file mode 100644 index 0000000..68209dd --- /dev/null +++ b/phptest/templates/php_ValidateType.erb @@ -0,0 +1,113 @@ + + * @copyright infiniteloop + * @license 別紙契約内容を参照 + */ + +require(LIBPATH . 'lib/Class/Validate.php'); + +class ValidateType extends Validate +{ + /** + * コンストラクタ + */ + public function __construct() + { + parent::__construct(); // Validetaクラスのコンストラクタ (int32の設定) + $this->hard_validate = true; // 厳密な型チェックを行なう + } +<%- types.each do |type| -%> + + + + /** + * <%= type.name.to_s %>型のバリデーション + * + * @param array $data <%= type.name.to_s %>型の配列 + * @param array $options オプション配列 + */ + public function validateType<%= type.name.to_s %>($data, $options = array()) + { + try { + $this->validateRequire($data, $options); // 必須オプションを解決する + + foreach ($data as $row) { + <%- type.fields.each do |field| -%> + $this->error_headline = '<%= type.name.to_s %>-><%= field.name.to_s %>について : '; + <%- option_str = field.options_to_php_array -%> + <%- unless option_str.empty? -%> + <%- option_str = ", #{option_str}" -%> + <%- end -%> + <%- if field.builtin_type? -%> + <%- # 組み込み型であるとき -%> + $this->validate_<%= field.type.to_s.downcase %>($row['<%= field.name.to_s %>']<%= option_str %>); + <%- else -%> + <%- # 組み込み型でないとき -%> + $this->validateType<%= field.type.to_s %>($row['<%= field.name.to_s %>']<%= option_str %>); + <%- end -%> + <%- end -%> + } + } catch (RuntimeException $e) { + $this->setErr(sprintf('<%= type.name.to_s %>型バリデーションエラー %s', $e->getMessage())); + } + } +<%- end -%> + + + + /** + * 例外エラーメッセージのjson中身を配列で取得する + * + * @param string $message 例外エラーメッセージの接頭文 + * @return array|null エラーレスポンスjsonの配列, ないときはnull + */ + public function getExceptionJsonArray($message = 'hoge') + { + if (DEBUG_MODE) { + $errs = $this->getException(); + + if (0 < count($errs)) { + return array('debugMessage' => sprintf('%s : %s', $message, implode(', ', $errs))); + } + } + + return array(); + } + + + + /** + * 増設レスポンスをバリデートする + * + * @param string $error_headline エラーメッセージの接頭文 + */ + protected function validateAddResponse($resp_arr, $rule_arr, $error_headline = null) + { + foreach ($rule_arr as $key => $rule) { + $this->error_headline = sprintf('%s増設項目 key=%s について : ', $error_headline, $key); + if (!is_null($rule['type'])) { + switch ($rule['type']) { + case 'int': + $this->validate_int($resp_arr[$key], $rule['options']); + break; + case 'double': + $this->validate_double($resp_arr[$key], $rule['options']); + break; + case 'bool': + $this->validate_boolean($resp_arr[$key]); + break; + case 'string': + $this->validate_string($resp_arr[$key], $rule['options']); + break; + } + } + } + } +} diff --git a/phptest/templates/php_api.erb b/phptest/templates/php_api.erb new file mode 100644 index 0000000..5b6e8f1 --- /dev/null +++ b/phptest/templates/php_api.erb @@ -0,0 +1,104 @@ + + * + * @package tss + * @author 関根重幸 TODO 自分の情報に変更してください、初期ではrpcoder修正者の情報になっています + * @copyright infiniteloop + * @license 別紙契約内容を参照 + */ + +define('ACTION_TYPE', 'USER_CONTRACT_ACTION'); // *_init.phpを呼ぶ前に定義することで、セッション切れ時にエラーjsonを返してくれる +require('path.php'); +<%- if nil != func.path.to_s.index("master") -%> +define('NO_LOGIN_CHECK', true); // ログインチェックを行なわない +require(LIBPATH . 'lib/Init/user_init.php'); +<%- elsif nil != func.path.to_s.index("startup") -%> +require(LIBPATH . 'lib/Init/world_init.php'); // admin or user_init.phpにする必要がある場合があります +<%- elsif nil != func.path.to_s.index("admin_tools") -%> +require(LIBPATH . 'lib/Init/admin_init.php'); // user_init.phpにする必要がある場合があります +<%- else -%> +require(LIBPATH . 'lib/Init/user_init.php'); // admin_init.phpにする必要がある場合があります +<%- end -%> +require(LIBPATH . '<%= File.join("lib/Contract/Function/".split("/"), func.path.to_s.sub(/[^\/]*\.php/, "").sub(/:.*$/, "").split("/"), "#{func.name.to_s}.php") %>'); +<%- api_use_types.each do |type| -%> +require(LIBPATH . '<%= File.join("lib/Contract/Type/".split("/"), "#{type.to_s}.php") %>'); +<%- end -%> + +$contract = new ContractFunction_<%= func.name.to_s %>(); + +try { + $contract->initialize(); // リクエスト情報のバリデートおよびインスタンス変数に格納 +} catch (Exception $e) { +<%- if 1 != func.path.to_s.index('admin_tools') and 1 != func.path.to_s.index('master') and 1 != func.path.to_s.index('startup') -%> + CommonFunctions::printErrorJson(API_ERROR_TYPE_RELOAD, API_ERROR_MESSAGE_REQUEST, '', $contract->getExceptionJsonArray($e->getMessage())); +<%- else -%> + CommonFunctions::printErrorJson(API_ERROR_TYPE_RETENTION, _('送信情報が不正です'), '', $contract->getExceptionJsonArray($e->getMessage())); +<%- end -%> + exit(); +} + +// ここからは、開発者が追記したコード ------------------------------------------ +<%- unless func.params.empty? -%> +// TODO 個別のリクエストパラメータは「$contract->get('キー')」で、全部のリクエストパラメータは「$contract->getAll()」で参照可能となっております +// TODO 下記のリクエストパラメータを使用してください + <%- func.params.each do |param| -%> +// パラメータ名:<%= param.name.to_s %> 型:<%= param.type.to_s %> 規制:<%= param.options_to_php_array %> + <%- end -%> +<%- else -%> +// TODO このAPIはリクエストパラメータを使用しません +<%- end -%> +<%- if api_use_types.empty? -%> +// TODO このAPIはTypeを使用しません +<%- else -%> +// TODO 下記のTypeを使用して、送信データを作成してください +<%- end -%> +<%- api_use_types.each do |type| -%> +// $<%= type.to_s.downcase %> = new ContractType<%= type.to_s %>(); +<%- end -%> + +// ここまでは、開発者が追記したコード ------------------------------------------ + +<%- if 1 != func.path.to_s.index('master') -%> +// チュートリアル/クエスト状態確認 TODO startup/*.phpなど一部のAPIでは不要ですので、その場合は削除してください +$myur = MyUser::singleton(); // TODO 定義済みのときは、この行を削除してください +$quest_completed_flg = CommonFunctions::hasQuestCompletedForApi($myur->user_id); // TODO 確実に確認したいときは、第二引数に "true" を指定してください +if (!is_null($quest_completed_flg)) { + // nullでないとき、状態確認しているので、レスポンスJSONに増設する + $contract->addResponse('isQuestCompleted', $quest_completed_flg, 'bool'); +} + +<%- end -%> +<%- if func.has_return_type? -%> +$response_data = array( + <%- func.return_types.each do |return_type| -%> + <%- comment = " // TODO #{return_type.type.to_s}型" -%> + <%- unless return_type.builtin_type? -%> + <%- # 組み込み型でないとき -%> + <%- comment += "のインスタンス" -%> + <%- else -%> + <%- comment += "の値" -%> + <%- end -%> + <%- if return_type.array? -%> + <%- # 配列であるとき -%> + <%- comment += "の配列" -%> + <%- end -%> + '<%= return_type.name.to_s %>' => null,<%= comment %> + <%- end -%> +); +<%- else -%> +// TODO ここでは、$contract->displayに渡すべきものはありませんので、開発者の作業はこのコメントを消すことで終わります +<%- end -%> + +try { + $contract->display(<% if func.has_return_type? %>$response_data<% end %>); // レスポンス情報のバリデートおよびJsonEcho +} catch (Exception $e) { +<%- if 1 != func.path.to_s.index('admin_tools') and 1 != func.path.to_s.index('master') and 1 != func.path.to_s.index('startup') -%> + CommonFunctions::printErrorJson(API_ERROR_TYPE_RELOAD, API_ERROR_MESSAGE_RESPONSE, '', $contract->getExceptionJsonArray($e->getMessage())); +<%- else -%> + CommonFunctions::printErrorJson(API_ERROR_TYPE_RETENTION, _('返信情報の取得に失敗しました'), '', $contract->getExceptionJsonArray($e->getMessage())); +<%- end -%> + exit(); +} diff --git a/phptest/templates/php_func.erb b/phptest/templates/php_func.erb new file mode 100644 index 0000000..4c92f11 --- /dev/null +++ b/phptest/templates/php_func.erb @@ -0,0 +1,229 @@ +のバリデーションクラス + * rpcoder4phpで生成したファイルのため、特別な編集はこちらではなくphp_func.erbに行なうこと + * + * @package tss + * @author 関根 重幸 + * @copyright infiniteloop + * @license 別紙契約内容を参照 + */ + +require(LIBPATH . 'lib/Contract/ContractFunctionBase.php'); + +class ContractFunction_<%= func.name.to_s %> extends ContractFunctionBase +{ + /** + * コンストラクタ + */ + public function __construct() + { + parent::__construct(); // Validetaクラスのコンストラクタ (int32の設定, hard_validateの設定) + } + + + + /** + * 初期化 + * + * 受信データをバリデートし、インスタンス変数に値を格納する + */ + public function initialize() + { +<%- if 'post' === func.method.to_s.downcase -%> + <%- # POSTのとき、インスタンス変数に格納する -%> + $this->decodePostJson(); // POSTのJSONをデコードする + +<%- end -%> +<%- func.params.each do |param| -%> + $this->error_headline = '<%= func.method.to_s.upcase %>[\'<%= param.name.to_s %>\']について : '; + <%- if 'post' === func.method.to_s.downcase -%> + $value = $this->postJson('<%= param.name.to_s %>'); // POSTで取得する + <%- else -%> + $value = Request::get('<%= param.name.to_s %>'); // GETで取得する + <%- end -%> + <%- option_str = param.options_to_php_array # バリデートルール文字列 -%> + <%- unless option_str.empty? -%> + <%- option_str = ", #{option_str}" -%> + <%- end -%> + <%- if param.builtin_type? -%> + <%- # 組み込み型であるとき -%> + $this->validate_<%= param.type.to_s.downcase %>($value<%= option_str %>); + <%- else -%> + <%- # 組み込み型でないとき -%> + $this->validateType<%= param.type.to_s %>($value<%= option_str %>); + <%- end -%> + $this->set('<%= param.name.to_s %>', $value); + + <%- if CONTRACT_VERSION_VARIABLE_NAME === param.name.to_s -%> + if (CONTRACT_VERSION !== $this->get('<%= param.name.to_s %>')) { + // コントラクトバージョンが異なるとき + if (DEBUG_MODE) { + // デバッグモードのとき、例外エラーメッセージをクライアントに通知する + CommonFunctions::printErrorJson(API_ERROR_TYPE_RESTART, sprintf(_('コントラクトバージョンを更新してください。最新は "%s" です。'), CONTRACT_VERSION)); + } else { + CommonFunctions::printErrorJson(API_ERROR_TYPE_RESTART, _('ゲームのバージョンが最新ではありません。ストアでアップデートしてください。')); + } + exit(); + } + + <%- end -%> +<%- end -%> + $error_message = 'Contract_<%= func.name.to_s %> 受信データのバリデーションエラー'; + + // エラーがあれば、例外を発生させる + $this->checkException($error_message); + } + + + + /** + * 表示 + * +<%- if func.has_return_type? # 返すものがあるとき -%> + * 送信データをバリデートし、クライアントに送信する + * + * @param array $data 送信データの配列 +<%- else # 返すものがないとき -%> + * 送信データがないため、JSONの空オブジェクトをクライアントに返す +<%- end -%> + */ + public function display(<% if func.has_return_type? %>$data<% end %>) + { +<%- if func.has_return_type? # 返すものがあるとき -%> + if (DEBUG_MODE) { + // デバッグモードのとき、バリデートする + $validateType = new ValidateType(); + <%- escape_flg = false -%> + <%- func.return_types.each do |return_type| -%> + $validateType->error_headline = 'ContractFunction_<%= func.name.to_s %>->display(array(\'<%= return_type.name.to_s %>\'))について : '; + <%- option_str = return_type.options_to_php_array # バリデートルール文字列 -%> + <%- unless option_str.empty? -%> + <%- option_str = ", #{option_str}" -%> + <%- end -%> + <%- if return_type.builtin_type? -%> + <%- # 組み込み型であるとき -%> + $validateType->validate_<%= return_type.type.to_s.downcase %>($data['<%= return_type.name.to_s %>']<%= option_str %>); + <%- else -%> + <%- # 組み込み型でないとき -%> + <%- php_indent = "" -%> + <%- unless return_type.option_require? -%> + <%- # "必須である"オプションがfalseのとき -%> + if (!is_null($data['<%= return_type.name.to_s %>']) and '' !== $data['<%= return_type.name.to_s %>']) { + <%- php_indent = "\t" -%> + <%- end -%> + <%- if return_type.array? -%> + <%- # 配列であるとき -%> + <%= php_indent %>$array_data = array(); + <%= php_indent %>foreach ($data['<%= return_type.name.to_s %>'] as &$type) { + <%= php_indent %>$array_data[] = $type->generateArray(); + <%= php_indent %>} + <%= php_indent %>$validateType->validateType<%= return_type.type.to_s %>($array_data<%= option_str %>); + <%- else -%> + <%- # 配列でないとき -%> + <%= php_indent %>$validateType->validateType<%= return_type.type.to_s %>($data['<%= return_type.name.to_s %>']->generateArray()<%= option_str %>); + <%- end -%> + <%- unless return_type.option_require? -%> + } + <%- end -%> + <%- end -%> + + <%- end -%> + if (!is_null($this->__add_resp)) { + $validateType->validateAddResponse($this->__add_resp, $this->__add_resp_vali_opts, 'ContractFunction_<%= func.name.to_s %>->display '); + } + + $error_message = 'Contract_<%= func.name.to_s %> 送信データのバリデーションエラー'; + + // エラーがあれば、例外を発生させる + $validateType->checkException($error_message); + } + + if (!is_null($this->__add_resp)) { + // 追加項目があるとき + $json = json_encode(array_merge($this->generateArrayOptimize($data), $this->__add_resp)); + } else { + // 追加項目がないとき + $json = json_encode($this->generateArrayOptimize($data)); + } + <%- message_add = '(空でないJSONオブジェクトを返します)' -%> +<%- else # 返すものがないとき -%> + if (!is_null($this->__add_resp)) { + // 追加項目があるとき + if (DEBUG_MODE) { + // デバッグモードのとき、バリデートする + $validateType = new ValidateType(); + $validateType->validateAddResponse($this->__add_resp, $this->__add_resp_vali_opts, 'ContractFunction_<%= func.name.to_s %>->display '); + + $error_message = 'Contract_<%= func.name.to_s %> 送信データのバリデーションエラー'; + + // エラーがあれば、例外を発生させる + $validateType->checkException($error_message); + } + + $json = json_encode($this->__add_resp); + } else { + // 追加項目がないとき + $json = '{}'; // JSONの空オブジェクトを返す + } + <%- message_add = '(空のJSONオブジェクトを返します)' -%> +<%- end -%> + +<%- if 1 != func.path.to_s.index('admin_tools') and 1 != func.path.to_s.index('master') and 1 != func.path.to_s.index('startup') -%> + <%- # パスが'admin_tools'でも'master'でも'startup'でもないものから始まっているとき (1 !== なのは、func.pathが'/admin_tools/...'のようになっているため) -%> + $myur = MyUser::singleton(); + log_info('レスポンス情報 : ユーザ名[%s]ユーザID[%s] (チャネリング先の管理ID[%s]) に対して返信したJSON <%= message_add %> = %s', $myur->user_name, $myur->user_id, $myur->owner_id, $json); + +<%- end -%> + echo $json; + } + + + + /** + * 返信するJSONの元配列を生成する + * + * 1. nullの項目は除外する + * 2. TypeならばgenerateArrayWithEscapeする + * 3. 必要ならばhtmlescapeする + * + * @prama array $obj_array Typeのインスタンスを含む配列 + * @return array $data 実引数配列中のTypeのインスタンスを配列化したもの + */ + public function generateArrayOptimize($obj_array) + { + $data = array(); + +<%- func.return_types.each do |field| -%> + <%- arrow_method = "" # Typeが呼び出すメソッド -%> + <%- unless field.builtin_type? -%> + <%- # 組み込み型でないとき -%> + <%- arrow_method = "->generateArrayWithEscape()" -%> + <%- end -%> + if (!is_null($obj_array['<%= field.name.to_s %>'])) { + <%- if !arrow_method.empty? and field.array? -%> + <%- # 組み込み型でない、かつ、配列であるとき -%> + $data['<%= field.name.to_s %>'] = array(); + foreach ($obj_array['<%= field.name.to_s %>'] as &$type) { + $data['<%= field.name.to_s %>'][] = $type<%= arrow_method %>; + } + <%- else -%> + <%- esc_prefix = "" -%> + <%- esc_suffix = "" -%> + <%- if arrow_method.empty? -%> + <%- # 組み込み型であるとき -%> + <%- if field.option_escape? -%> + <%- esc_prefix = "$this->validateHtmlEscape(" -%> + <%- esc_suffix = ")" -%> + <%- end -%> + <%- end -%> + $data['<%= field.name.to_s %>'] = <%= esc_prefix %>$obj_array['<%= field.name.to_s %>']<%= arrow_method %><%= esc_suffix %>; + <%- end -%> + } +<%- end -%> + + return $data; + } +} diff --git a/phptest/templates/php_path.erb b/phptest/templates/php_path.erb new file mode 100755 index 0000000..cae5849 --- /dev/null +++ b/phptest/templates/php_path.erb @@ -0,0 +1,15 @@ + + * @copyright infiniteloop + * @license 別紙契約内容を参照 + */ + +chdir(dirname(__FILE__)); +define('LIBPATH', realpath('<%= "../" * depth %>') . '/'); diff --git a/phptest/templates/php_type.erb b/phptest/templates/php_type.erb new file mode 100644 index 0000000..3ec23b0 --- /dev/null +++ b/phptest/templates/php_type.erb @@ -0,0 +1,153 @@ +型のクラス + * rpcoder4phpで生成したファイルのため、特別な編集はこちらではなくphp_type.erbに行なうこと + * + * @package tss + * @author 関根 重幸 + * @copyright infiniteloop + * @license 別紙契約内容を参照 + */ + +class ContractType<%= type.name.to_s %> extends ValidateOption +{ +<%- type.fields.each do |field| -%> + <%- comment_str = "型: " # コメント文字列 -%> + <%- var_suffix = "" # 変数名の接尾語 -%> + <%- if field.builtin_type? -%> + <%- # 組み込み型であるとき -%> + <%- comment_str += "#{field.type.to_s.downcase} (組込型)" -%> + <%- else -%> + <%- # 組み込み型でないとき -%> + <%- comment_str += "#{field.type.to_s} (Type型)" -%> + <%- end -%> + <%- option_str = "" # バリデートルール文字列 -%> + <%- field.options.each do |key, val| -%> + <%- option_str += " #{key.to_s}=#{val.to_s}" -%> + <%- end -%> + <%- unless option_str.empty? -%> + <%- comment_str += ", 規約:#{option_str}" -%> + <%- end -%> + <%- if field.array? -%> + <%- # 配列であるとき -%> + <%- var_suffix += "_array" -%> + <%- end -%> + public $<%= field.name.to_s %><%= var_suffix %>; // <%= comment_str %> +<%- end -%> + + /** + * コンストラクタ + */ + public function __construct() + { + } + + + + /** + * インスタンス変数を配列に格納して返す + * + * 主に返信データのバリデート時に用いる + * + * @return array $data インスタンス変数を配列に格納したもの + */ + public function generateArray() + { + $data = array(); + +<%- type.fields.each do |field| -%> + <%- var_suffix = "" # 変数名の接尾語 -%> + <%- arrow_method = "" # Typeが呼び出すメソッド -%> + <%- unless field.builtin_type? -%> + <%- # 組み込み型でないとき -%> + <%- arrow_method = "->generateArray()" -%> + <%- end -%> + <%- if field.array? -%> + <%- # 配列であるとき -%> + <%- var_suffix += "_array" -%> + <%- end -%> + <%- if !arrow_method.empty? and field.array? -%> + <%- # 組み込み型でない、かつ、配列であるとき -%> + $data['<%= field.name.to_s %>'] = array(); + foreach ($this-><%= field.name.to_s %><%= var_suffix %> as &$type) { + if (is_null($type)) { + $data['<%= field.name.to_s %>'][] = null; + } else { + $data['<%= field.name.to_s %>'][] = $type<%= arrow_method %>; + } + } + <%- else -%> + <%- php_indent = "" -%> + <%- unless field.option_require? -%> + <%- # "必須である"オプションがfalseのとき -%> + if (!is_null($this-><%= field.name.to_s %>) and '' !== $this-><%= field.name.to_s %>) { + <%- php_indent = "\t" -%> + <%- end -%> + <%= php_indent %>$data['<%= field.name.to_s %>'] = $this-><%= field.name.to_s %><%= var_suffix %><%= arrow_method %>; + <%- unless field.option_require? -%> + } else { + $data['<%= field.name.to_s %>'] = null; + } + <%- end -%> + <%- end -%> +<%- end -%> + + return $data; + } + + + + /** + * インスタンス変数を必要に応じてhtmlescapeし、配列に格納して返す + * + * 主に返信データの生成時に用いる + * + * @return array $data インスタンス変数を配列に格納したもの + */ + public function generateArrayWithEscape() + { + $data = array(); + +<%- type.fields.each do |field| -%> + <%- var_suffix = "" # 変数名の接尾語 -%> + <%- arrow_method = "" # Typeが呼び出すメソッド -%> + <%- unless field.builtin_type? -%> + <%- # 組み込み型でないとき -%> + <%- arrow_method = "->generateArrayWithEscape()" -%> + <%- end -%> + <%- if field.array? -%> + <%- # 配列であるとき -%> + <%- var_suffix += "_array" -%> + <%- end -%> + if (!is_null($this-><%= field.name.to_s %><%= var_suffix %>)) { + <%- if !arrow_method.empty? and field.array? -%> + <%- # 組み込み型でない、かつ、配列であるとき -%> + $data['<%= field.name.to_s %>'] = array(); + foreach ($this-><%= field.name.to_s %><%= var_suffix %> as &$type) { + if (is_null($type)) { + $data['<%= field.name.to_s %>'][] = null; + } else { + $data['<%= field.name.to_s %>'][] = $type<%= arrow_method %>; + } + } + <%- elsif !arrow_method.empty? -%> + <%- # 組み込み型でない、かつ、配列でないとき -%> + $data['<%= field.name.to_s %>'] = $this-><%= field.name.to_s %><%= arrow_method %>; + <%- else -%> + <%- # 組み込み型であるとき -%> + <%- esc_prefix = "" -%> + <%- esc_suffix = "" -%> + <%- if field.option_escape? -%> + <%- esc_prefix = "$this->validateHtmlEscape(" -%> + <%- esc_suffix = ")" -%> + <%- end -%> + $data['<%= field.name.to_s %>'] = <%= esc_prefix %>$this-><%= field.name.to_s %><%= var_suffix %><%= esc_suffix %>; + <%- end -%> + } +<%- end -%> + + return $data; + } +}