crack-without-safe_yaml 0.4.3

Sign up to get free protection for your applications and to get access to all the features.
@@ -0,0 +1,7 @@
1
+ ---
2
+ SHA1:
3
+ metadata.gz: f0e2f8a3bdaf0cf4d6370d17515f8337602f9dd1
4
+ data.tar.gz: 0ed3cc855986617a10b12e6e260de348c2c01a9f
5
+ SHA512:
6
+ metadata.gz: 21196e82748f57c78554dd7d6b2495ab96cb118bc3f68ee08a4f4b01f01d7cc40169a334931c3be40b9dc3bb4ac95fdb9500d928abafaac51a9ca9fe429c412f
7
+ data.tar.gz: 2846f21043ddb611dd121bb2ba7db73a74bcfa2c4cf63eebb9dbe36f17099898114e1c3202d66afdd409ce36629fcf846b309fab47b9627bacbf46fabc8736fd
@@ -0,0 +1,8 @@
1
+ *.sw?
2
+ .DS_Store
3
+ coverage
4
+ rdoc
5
+ pkg
6
+ *.gem
7
+ /.bundle
8
+ /Gemfile.lock
@@ -0,0 +1,14 @@
1
+ language: ruby
2
+ rvm:
3
+ - 1.8
4
+ - 1.9
5
+ - 2.0
6
+ - 2.1
7
+ - 2.2
8
+ - jruby
9
+ - rbx
10
+ matrix:
11
+ allow_failures:
12
+ - rvm: 1.8
13
+ script: ./script/test
14
+ sudo: false
data/Gemfile ADDED
@@ -0,0 +1,5 @@
1
+ source 'https://rubygems.org'
2
+ gemspec
3
+
4
+ gem "rake"
5
+ gem "minitest"
data/History ADDED
@@ -0,0 +1,25 @@
1
+ == 0.1.7 2010-02-19
2
+ * 1 minor patch
3
+ * Added patch from @purp for ISO 8601 date/time format
4
+ == 0.1.6 2010-01-31
5
+ * 1 minor patch
6
+ * Added Crack::VERSION constant - http://weblog.rubyonrails.org/2009/9/1/gem-packaging-best-practices
7
+ == 0.1.5 2010-01-27
8
+ * 1 minor patch
9
+ * Strings that begin with dates shouldn't be parsed as such (sandro)
10
+
11
+ == 0.1.3 2009-06-22
12
+ * 1 minor patch
13
+ * Parsing a text node with attributes stores them in the attributes method (tamalw)
14
+
15
+ == 0.1.2 2009-04-21
16
+ * 2 minor patches
17
+ * Correct unnormalization of attribute values (der-flo)
18
+ * Fix error in parsing YAML in the case where a hash value ends with backslashes, and there are subsequent values in the hash (deadprogrammer)
19
+
20
+ == 0.1.1 2009-03-31
21
+ * 1 minor patch
22
+ * Parsing empty or blank xml now returns empty hash instead of raising error.
23
+
24
+ == 0.1.0 2009-03-28
25
+ * Initial release.
data/LICENSE ADDED
@@ -0,0 +1,20 @@
1
+ Copyright (c) 2009 John Nunemaker
2
+
3
+ Permission is hereby granted, free of charge, to any person obtaining
4
+ a copy of this software and associated documentation files (the
5
+ "Software"), to deal in the Software without restriction, including
6
+ without limitation the rights to use, copy, modify, merge, publish,
7
+ distribute, sublicense, and/or sell copies of the Software, and to
8
+ permit persons to whom the Software is furnished to do so, subject to
9
+ the following conditions:
10
+
11
+ The above copyright notice and this permission notice shall be
12
+ included in all copies or substantial portions of the Software.
13
+
14
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
15
+ EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
16
+ MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
17
+ NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
18
+ LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
19
+ OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
20
+ WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
@@ -0,0 +1,46 @@
1
+ # crack
2
+
3
+ [![Build Status](https://travis-ci.org/jnunemaker/crack.svg?branch=master)](https://travis-ci.org/jnunemaker/crack)
4
+
5
+ Really simple JSON and XML parsing, ripped from Merb and Rails. The XML parser is ripped from Merb and the JSON parser is ripped from Rails. I take no credit, just packaged them for all to enjoy and easily use.
6
+
7
+ ## compatibility
8
+
9
+ * ruby 1.8.7
10
+ * ruby 1.9+ (3 failures related to time parsing, would love it if someone could figure them out)
11
+
12
+ ## note on patches/pull requests
13
+
14
+ * Fork the project.
15
+ * Make your feature addition or bug fix.
16
+ * Add tests for it. This is important so I don't break it in a future version unintentionally.
17
+ * `script/test` - this will bootstrap and run the tests
18
+ * Commit, do not mess with rakefile, version, or history. (if you want to have your own version, that is fine but bump version in a commit by itself in another branch so I can ignore when I pull)
19
+ * Send me a pull request. Bonus points for topic branches.
20
+
21
+ ## usage
22
+
23
+ ```ruby
24
+ gem 'crack' # in Gemfile
25
+ require 'crack' # for xml and json
26
+ require 'crack/json' # for just json
27
+ require 'crack/xml' # for just xml
28
+ ```
29
+
30
+ ## examples
31
+
32
+ ```ruby
33
+ Crack::XML.parse("<tag>This is the contents</tag>")
34
+ # => {'tag' => 'This is the contents'}
35
+
36
+ Crack::JSON.parse('{"tag":"This is the contents"}')
37
+ # => {'tag' => 'This is the contents'}
38
+ ```
39
+
40
+ ## Copyright
41
+
42
+ Copyright (c) 2009 John Nunemaker. See LICENSE for details.
43
+
44
+ ## Docs
45
+
46
+ http://rdoc.info/projects/jnunemaker/crack
@@ -0,0 +1,18 @@
1
+ # -*- encoding: utf-8 -*-
2
+ require File.expand_path('../lib/crack/version', __FILE__)
3
+
4
+ Gem::Specification.new do |gem|
5
+ gem.authors = ["John Nunemaker"]
6
+ gem.email = ["nunemaker@gmail.com"]
7
+ gem.description = %q{Really simple JSON and XML parsing, ripped from Merb and Rails.}
8
+ gem.summary = %q{Really simple JSON and XML parsing, ripped from Merb and Rails.}
9
+ gem.homepage = "http://github.com/jnunemaker/crack"
10
+
11
+ gem.executables = `git ls-files -- bin/*`.split("\n").map{ |f| File.basename(f) }
12
+ gem.files = `git ls-files`.split("\n")
13
+ gem.test_files = `git ls-files -- {test,spec,features}/*`.split("\n")
14
+ gem.name = "crack-without-safe_yaml"
15
+ gem.require_paths = ["lib"]
16
+ gem.version = Crack::VERSION
17
+ gem.license = "MIT"
18
+ end
@@ -0,0 +1,7 @@
1
+ module Crack
2
+ class ParseError < StandardError; end
3
+ end
4
+
5
+ require 'crack/util'
6
+ require 'crack/json'
7
+ require 'crack/xml'
@@ -0,0 +1,98 @@
1
+ # Copyright (c) 2004-2008 David Heinemeier Hansson
2
+ # Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions:
3
+ # The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software.
4
+ # THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
5
+
6
+ require 'strscan'
7
+
8
+ module Crack
9
+ class JSON
10
+ def self.parser_exceptions
11
+ @parser_exceptions ||= [ArgumentError, Psych::SyntaxError]
12
+ end
13
+
14
+ def self.parse(json)
15
+ yaml = unescape(convert_json_to_yaml(json))
16
+ YAML.safe_load(yaml, [Regexp, Date, Time])
17
+ rescue *parser_exceptions
18
+ raise ParseError, "Invalid JSON string"
19
+ rescue Psych::DisallowedClass
20
+ yaml
21
+ end
22
+
23
+ protected
24
+ def self.unescape(str)
25
+ # Force the encoding to be UTF-8 so we can perform regular expressions
26
+ # on 1.9.2 without blowing up.
27
+ # see http://stackoverflow.com/questions/1224204/ruby-mechanize-getting-force-encoding-exception for a similar issue
28
+ str.force_encoding('UTF-8') if defined?(Encoding) && str.respond_to?(:force_encoding)
29
+ str.gsub(/\\u0000/, "").gsub(/\\[u|U]([0-9a-fA-F]{4})/) { [$1.hex].pack("U") }
30
+ end
31
+
32
+ # matches YAML-formatted dates
33
+ DATE_REGEX = /^\d{4}-\d{2}-\d{2}$|^\d{4}-\d{1,2}-\d{1,2}[T \t]+\d{1,2}:\d{2}:\d{2}(\.[0-9]*)?(([ \t]*)Z|[-+]\d{2}?(:\d{2})?)$/
34
+
35
+ # Ensure that ":" and "," are always followed by a space
36
+ def self.convert_json_to_yaml(json) #:nodoc:
37
+ json = String.new(json) #can't modify a frozen string
38
+ scanner, quoting, marks, pos, date_starts, date_ends = StringScanner.new(json), false, [], nil, [], []
39
+ while scanner.scan_until(/(\\['"]|['":,\/\\]|\\.)/)
40
+ case char = scanner[1]
41
+ when '"', "'"
42
+ if !quoting
43
+ quoting = char
44
+ pos = scanner.pos
45
+ elsif quoting == char
46
+ if json[pos..scanner.pos-2] =~ DATE_REGEX
47
+ # found a date, track the exact positions of the quotes so we can remove them later.
48
+ # oh, and increment them for each current mark, each one is an extra padded space that bumps
49
+ # the position in the final YAML output
50
+ total_marks = marks.size
51
+ date_starts << pos+total_marks
52
+ date_ends << scanner.pos+total_marks
53
+ end
54
+ quoting = false
55
+ end
56
+ when "/"
57
+ if !quoting
58
+ json[scanner.pos - 1] = "!ruby/regexp /"
59
+ scanner.pos += 13
60
+ scanner.scan_until(/\/[mix]*/)
61
+ end
62
+ when ":",","
63
+ marks << scanner.pos - 1 unless quoting
64
+ when "\\"
65
+ scanner.skip(/\\/)
66
+ end
67
+ end
68
+
69
+ if marks.empty?
70
+ json.gsub(/\\\//, '/')
71
+ else
72
+ left_pos = marks.clone.unshift(-1)
73
+ right_pos = marks << json.length
74
+ output = []
75
+ left_pos.each_with_index do |left, i|
76
+ output << json[left.succ..right_pos[i]]
77
+ end
78
+ output = output * " "
79
+
80
+ format_dates(output, date_starts, date_ends)
81
+ output.gsub!(/\\\//, '/')
82
+ output
83
+ end
84
+ end
85
+
86
+ def self.format_dates(output, date_starts, date_ends)
87
+ if YAML.constants.include?('Syck')
88
+ (date_starts + date_ends).each { |i| output[i-1] = ' ' }
89
+ else
90
+ extra_chars_to_be_added = 0
91
+ date_starts.each do |i|
92
+ output[i-2+extra_chars_to_be_added] = '!!timestamp '
93
+ extra_chars_to_be_added += 10
94
+ end
95
+ end
96
+ end
97
+ end
98
+ end
@@ -0,0 +1,17 @@
1
+ module Crack
2
+ module Util
3
+ def snake_case(str)
4
+ return str.downcase if str =~ /^[A-Z]+$/
5
+ str.gsub(/([A-Z]+)(?=[A-Z][a-z]?)|\B[A-Z]/, '_\&') =~ /_*(.*)/
6
+ return $+.downcase
7
+ end
8
+
9
+ def to_xml_attributes(hash)
10
+ hash.map do |k,v|
11
+ %{#{Crack::Util.snake_case(k.to_s).sub(/^(.{1,1})/) { |m| m.downcase }}="#{v.to_s.gsub('"', '&quot;')}"}
12
+ end.join(' ')
13
+ end
14
+
15
+ extend self
16
+ end
17
+ end
@@ -0,0 +1,3 @@
1
+ module Crack
2
+ VERSION = "0.4.3"
3
+ end
@@ -0,0 +1,238 @@
1
+ require 'rexml/parsers/streamparser'
2
+ require 'rexml/parsers/baseparser'
3
+ require 'rexml/light/node'
4
+ require 'rexml/text'
5
+ require "rexml/document"
6
+ require 'date'
7
+ require 'time'
8
+ require 'yaml'
9
+ require 'bigdecimal'
10
+
11
+ # The Reason behind redefining the String Class for this specific plugin is to
12
+ # avoid the dynamic insertion of stuff on it (see version previous to this commit).
13
+ # Doing that disables the possibility of efectuating a dump on the structure. This way it goes.
14
+ class REXMLUtiliyNodeString < String
15
+ attr_accessor :attributes
16
+ end
17
+
18
+ # This is a slighly modified version of the XMLUtilityNode from
19
+ # http://merb.devjavu.com/projects/merb/ticket/95 (has.sox@gmail.com)
20
+ # It's mainly just adding vowels, as I ht cd wth n vwls :)
21
+ # This represents the hard part of the work, all I did was change the
22
+ # underlying parser.
23
+ class REXMLUtilityNode #:nodoc:
24
+ attr_accessor :name, :attributes, :children, :type
25
+
26
+ def self.typecasts
27
+ @@typecasts
28
+ end
29
+
30
+ def self.typecasts=(obj)
31
+ @@typecasts = obj
32
+ end
33
+
34
+ def self.available_typecasts
35
+ @@available_typecasts
36
+ end
37
+
38
+ def self.available_typecasts=(obj)
39
+ @@available_typecasts = obj
40
+ end
41
+
42
+ self.typecasts = {}
43
+ self.typecasts["integer"] = lambda{|v| v.nil? ? nil : v.to_i}
44
+ self.typecasts["boolean"] = lambda{|v| v.nil? ? nil : (v.strip != "false")}
45
+ self.typecasts["datetime"] = lambda{|v| v.nil? ? nil : Time.parse(v).utc}
46
+ self.typecasts["date"] = lambda{|v| v.nil? ? nil : Date.parse(v)}
47
+ self.typecasts["dateTime"] = lambda{|v| v.nil? ? nil : Time.parse(v).utc}
48
+ self.typecasts["decimal"] = lambda{|v| v.nil? ? nil : BigDecimal(v.to_s)}
49
+ self.typecasts["double"] = lambda{|v| v.nil? ? nil : v.to_f}
50
+ self.typecasts["float"] = lambda{|v| v.nil? ? nil : v.to_f}
51
+ self.typecasts["string"] = lambda{|v| v.to_s}
52
+ self.typecasts["base64Binary"] = lambda{|v| v.unpack('m').first }
53
+
54
+ self.available_typecasts = self.typecasts.keys
55
+
56
+ def initialize(name, normalized_attributes = {})
57
+
58
+ # unnormalize attribute values
59
+ attributes = Hash[* normalized_attributes.map { |key, value|
60
+ [ key, unnormalize_xml_entities(value) ]
61
+ }.flatten]
62
+
63
+ @name = name.tr("-", "_")
64
+ # leave the type alone if we don't know what it is
65
+ @type = self.class.available_typecasts.include?(attributes["type"]) ? attributes.delete("type") : attributes["type"]
66
+
67
+ @nil_element = attributes.delete("nil") == "true"
68
+ @attributes = undasherize_keys(attributes)
69
+ @children = []
70
+ @text = false
71
+ end
72
+
73
+ def add_node(node)
74
+ @text = true if node.is_a? String
75
+ @children << node
76
+ end
77
+
78
+ def to_hash
79
+ # ACG: Added a check here to prevent an exception a type == "file" tag has nodes within it
80
+ if @type == "file" and (@children.first.nil? or @children.first.is_a?(String))
81
+ f = StringIO.new((@children.first || '').unpack('m').first)
82
+ class << f
83
+ attr_accessor :original_filename, :content_type
84
+ end
85
+ f.original_filename = attributes['name'] || 'untitled'
86
+ f.content_type = attributes['content_type'] || 'application/octet-stream'
87
+ return {name => f}
88
+ end
89
+
90
+ if @text
91
+ t = typecast_value( unnormalize_xml_entities( inner_html ) )
92
+ if t.is_a?(String)
93
+ t = REXMLUtiliyNodeString.new(t)
94
+ t.attributes = attributes
95
+ end
96
+ return { name => t }
97
+ else
98
+ #change repeating groups into an array
99
+ groups = @children.inject({}) { |s,e| (s[e.name] ||= []) << e; s }
100
+
101
+ out = nil
102
+ if @type == "array"
103
+ out = []
104
+ groups.each do |k, v|
105
+ if v.size == 1
106
+ out << v.first.to_hash.entries.first.last
107
+ else
108
+ out << v.map{|e| e.to_hash[k]}
109
+ end
110
+ end
111
+ out = out.flatten
112
+
113
+ else # If Hash
114
+ out = {}
115
+ groups.each do |k,v|
116
+ if v.size == 1
117
+ out.merge!(v.first)
118
+ else
119
+ out.merge!( k => v.map{|e| e.to_hash[k]})
120
+ end
121
+ end
122
+ out.merge! attributes unless attributes.empty?
123
+ out = out.empty? ? nil : out
124
+ end
125
+
126
+ if @type && out.nil?
127
+ { name => typecast_value(out) }
128
+ else
129
+ { name => out }
130
+ end
131
+ end
132
+ end
133
+
134
+ # Typecasts a value based upon its type. For instance, if
135
+ # +node+ has #type == "integer",
136
+ # {{[node.typecast_value("12") #=> 12]}}
137
+ #
138
+ # @param value<String> The value that is being typecast.
139
+ #
140
+ # @details [:type options]
141
+ # "integer"::
142
+ # converts +value+ to an integer with #to_i
143
+ # "boolean"::
144
+ # checks whether +value+, after removing spaces, is the literal
145
+ # "true"
146
+ # "datetime"::
147
+ # Parses +value+ using Time.parse, and returns a UTC Time
148
+ # "date"::
149
+ # Parses +value+ using Date.parse
150
+ #
151
+ # @return <Integer, TrueClass, FalseClass, Time, Date, Object>
152
+ # The result of typecasting +value+.
153
+ #
154
+ # @note
155
+ # If +self+ does not have a "type" key, or if it's not one of the
156
+ # options specified above, the raw +value+ will be returned.
157
+ def typecast_value(value)
158
+ return value unless @type
159
+ proc = self.class.typecasts[@type]
160
+ proc.nil? ? value : proc.call(value)
161
+ end
162
+
163
+ # Take keys of the form foo-bar and convert them to foo_bar
164
+ def undasherize_keys(params)
165
+ params.keys.each do |key, value|
166
+ params[key.tr("-", "_")] = params.delete(key)
167
+ end
168
+ params
169
+ end
170
+
171
+ # Get the inner_html of the REXML node.
172
+ def inner_html
173
+ @children.join
174
+ end
175
+
176
+ # Converts the node into a readable HTML node.
177
+ #
178
+ # @return <String> The HTML node in text form.
179
+ def to_html
180
+ attributes.merge!(:type => @type ) if @type
181
+ "<#{name}#{Crack::Util.to_xml_attributes(attributes)}>#{@nil_element ? '' : inner_html}</#{name}>"
182
+ end
183
+
184
+ # @alias #to_html #to_s
185
+ def to_s
186
+ to_html
187
+ end
188
+
189
+ private
190
+
191
+ def unnormalize_xml_entities value
192
+ REXML::Text.unnormalize(value)
193
+ end
194
+ end
195
+
196
+ module Crack
197
+ class REXMLParser
198
+ def self.parse(xml)
199
+ stack = []
200
+ parser = REXML::Parsers::BaseParser.new(xml)
201
+
202
+ while true
203
+ event = parser.pull
204
+ case event[0]
205
+ when :end_document
206
+ break
207
+ when :end_doctype, :start_doctype
208
+ # do nothing
209
+ when :start_element
210
+ stack.push REXMLUtilityNode.new(event[1], event[2])
211
+ when :end_element
212
+ if stack.size > 1
213
+ temp = stack.pop
214
+ stack.last.add_node(temp)
215
+ end
216
+ when :text, :cdata
217
+ stack.last.add_node(event[1]) unless event[1].strip.length == 0 || stack.empty?
218
+ end
219
+ end
220
+
221
+ stack.length > 0 ? stack.pop.to_hash : {}
222
+ end
223
+ end
224
+
225
+ class XML
226
+ def self.parser
227
+ @@parser ||= REXMLParser
228
+ end
229
+
230
+ def self.parser=(parser)
231
+ @@parser = parser
232
+ end
233
+
234
+ def self.parse(xml)
235
+ parser.parse(xml)
236
+ end
237
+ end
238
+ end