/lib/thingfish/filter/yaml.rb

https://bitbucket.org/laika/thingfish · Ruby · 158 lines · 71 code · 33 blank · 54 comment · 3 complexity · 7b33c91fdff0f9d7cb16ee45a1cc0388 MD5 · raw file

  1. #!/usr/bin/env ruby
  2. require 'yaml'
  3. require 'thingfish'
  4. require 'thingfish/mixins'
  5. require 'thingfish/constants'
  6. require 'thingfish/acceptparam'
  7. require 'thingfish/filter'
  8. # A YAML-conversion filter for ThingFish. It converts Ruby objects in the body of responses
  9. # to YAML if the client accepts 'text/x-yaml'.
  10. #
  11. # == Synopsis
  12. #
  13. # plugins:
  14. # filters:
  15. # yaml: ~
  16. #
  17. # == Version
  18. #
  19. # $Id$
  20. #
  21. # == Authors
  22. #
  23. # * Michael Granger <ged@FaerieMUD.org>
  24. # * Mahlon E. Smith <mahlon@martini.nu>
  25. #
  26. # :include: LICENSE
  27. #
  28. #---
  29. #
  30. # Please see the file LICENSE in the top-level directory for licensing details.
  31. #
  32. class ThingFish::YAMLFilter < ThingFish::Filter
  33. include ThingFish::Loggable,
  34. ThingFish::Constants
  35. # VCS Revision
  36. VCSRev = %q$Rev$
  37. # The Array of types this filter is interested in
  38. HANDLED_TYPES = [ ThingFish::AcceptParam.parse(RUBY_MIMETYPE) ]
  39. HANDLED_TYPES.freeze
  40. # The YAML mime type.
  41. YAML_MIMETYPE = 'text/x-yaml'
  42. YAML_MIMETYPE.freeze
  43. #################################################################
  44. ### I N S T A N C E M E T H O D S
  45. #################################################################
  46. ### Set up a new Filter object
  47. def initialize( options={} ) # :notnew:
  48. super
  49. end
  50. ######
  51. public
  52. ######w
  53. ### Filter incoming requests, converting YAML to native ruby objects.
  54. def handle_request( request, response )
  55. # Only filter if the client sends what we can convert from.
  56. return unless request.content_type &&
  57. request.content_type.downcase == YAML_MIMETYPE
  58. # Absorb errors so filters can continue
  59. begin
  60. self.log.debug "Converting a %s request to %s" %
  61. [ YAML_MIMETYPE, RUBY_MIMETYPE ]
  62. request.body = YAML.load( request.body.read )
  63. rescue RuntimeError, YAML::ParseError => err
  64. self.log.error "%s while attempting to convert %p to a native ruby object: %s" %
  65. [ err.class.name, request.body, err.message ]
  66. self.log.debug err.backtrace.join("\n")
  67. else
  68. request.content_type = RUBY_MIMETYPE
  69. end
  70. end
  71. ### Convert outgoing ruby object responses into YAML.
  72. def handle_response( response, request )
  73. # Only filter if the client wants what we can convert to, and the response body
  74. # is something we know how to convert
  75. return unless request.explicitly_accepts?( YAML_MIMETYPE ) &&
  76. self.accept?( response.content_type )
  77. # Errors converting to yaml should result in a 500.
  78. self.log.debug "Converting a %s response to text/x-yaml" %
  79. [ response.content_type ]
  80. response.body = self.stringify_members( response.body ).to_yaml
  81. response.content_type = YAML_MIMETYPE
  82. end
  83. ### Return an Array of ThingFish::AcceptParam objects which describe which content types
  84. ### the filter is interested in. The default returns */*, which indicates that it is
  85. ### interested in all requests/responses.
  86. def handled_types
  87. return HANDLED_TYPES
  88. end
  89. ### Returns a Hash of information about the filter; this is of the form:
  90. ### {
  91. ### 'version' => [ 0, 60 ], # YAML.rb version
  92. ### 'supports' => [ [1,0], [1,1] ], # Supported YAML versions
  93. ### 'rev' => 460, # VCS rev of plugin
  94. ### }
  95. def info
  96. yaml_rb_version = YAML::VERSION.split('.').collect {|i| Integer(i) }
  97. supported_yaml_version = YAML::SUPPORTED_YAML_VERSIONS.collect do |v|
  98. v.split('.').collect {|i| Integer(i) }
  99. end
  100. return {
  101. 'version' => yaml_rb_version,
  102. 'supports' => supported_yaml_version,
  103. 'rev' => VCSRev[ /: (\w+)/, 1 ] || 0,
  104. 'accepts' => [YAML_MIMETYPE],
  105. 'generates' => [YAML_MIMETYPE],
  106. }
  107. end
  108. #########
  109. protected
  110. #########
  111. ### Walk over the contents of +data+, stringifying contents.
  112. def stringify_members( data )
  113. case data
  114. when Hash
  115. newhash = {}
  116. data.each_key {|k| newhash[ k.to_s ] = stringify_members(data[k]) }
  117. return newhash
  118. when Array
  119. data.collect {|obj| stringify_members(obj) }
  120. else
  121. data.to_s
  122. end
  123. end
  124. end # class ThingFish::YAMLFilter
  125. # vim: set nosta noet ts=4 sw=4: