autoparse 0.2.3 → 0.3.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
data/CHANGELOG CHANGED
@@ -1,3 +1,9 @@
1
+ == 0.3.0
2
+
3
+ * fixed handling of additional properties w/ a set schema
4
+ * modified index methods to allow either raw or parsed access
5
+ * modified index methods to default to parsed access
6
+
1
7
  == 0.2.3
2
8
 
3
9
  * fixed stupid bug in inspect method
@@ -46,6 +46,11 @@ module AutoParse
46
46
  def self.additional_properties_schema
47
47
  # Override the superclass implementation so we're not always returning
48
48
  # the empty schema.
49
+ if @additional_properties_schema.data['$ref']
50
+ # Dereference the schema if necessary.
51
+ @additional_properties_schema =
52
+ @additional_properties_schema.dereference
53
+ end
49
54
  return @additional_properties_schema
50
55
  end
51
56
 
@@ -81,89 +86,50 @@ module AutoParse
81
86
  schema_data['additionalProperties'] == nil
82
87
  # Schema-less unknown properties are allowed.
83
88
  @additional_properties_schema = EMPTY_SCHEMA
84
- define_method('method_missing') do |method, *params, &block|
85
- # We need to convert from Ruby calling style to JavaScript calling
86
- # style. If this fails, attempt to use JavaScript calling style
87
- # directly.
88
-
89
- # We can't modify the method in-place because this affects the call
90
- # to super.
91
- stripped_method = method.to_s
92
- assignment = false
93
- if stripped_method[-1..-1] == '='
94
- assignment = true
95
- stripped_method[-1..-1] = ''
96
- end
97
- key = INFLECTOR.camelize(stripped_method)
98
- key[0..0] = key[0..0].downcase
99
- if self[key] != nil
100
- value = self[key]
101
- elsif self[stripped_method] != nil
102
- key = stripped_method
103
- value = self[stripped_method]
104
- else
105
- # Method not found.
106
- super(method, *params, &block)
107
- end
108
- # If additionalProperties is simply set to true, no parsing takes
109
- # place and all values are treated as 'any'.
110
- if assignment
111
- new_value = params[0]
112
- self[key] = new_value
113
- else
114
- value
115
- end
116
- end
117
-
118
89
  elsif schema_data['additionalProperties']
119
90
  # Unknown properties follow the supplied schema.
120
91
  ap_schema = AutoParse.generate(
121
92
  schema_data['additionalProperties'], :parent => self
122
93
  )
123
94
  @additional_properties_schema = ap_schema
124
- define_method('method_missing') do |method, *params, &block|
125
- # We need to convert from Ruby calling style to JavaScript calling
126
- # style. If this fails, attempt to use JavaScript calling style
127
- # directly.
128
-
129
- # We can't modify the method in-place because this affects the call
130
- # to super.
131
- stripped_method = method.to_s
132
- assignment = false
133
- if stripped_method[-1..-1] == '='
134
- assignment = true
135
- stripped_method[-1..-1] = ''
136
- end
137
- key = INFLECTOR.camelize(stripped_method)
138
- key[0..0] = key[0..0].downcase
139
- if self[key] != nil
140
- value = self[key]
141
- elsif self[stripped_method] != nil
142
- key = stripped_method
143
- value = self[stripped_method]
144
- else
145
- # Method not found.
146
- super
147
- end
148
- if assignment
149
- # In the case of assignment, it's very likely the developer is
150
- # passing in an unparsed Hash value. This value must be parsed.
151
- # Unfortunately, we may accidentally reparse something that's
152
- # already in a parsed state because Schema.new(Schema.new(data))
153
- # is completely valid. This will cause performance issues if
154
- # developers are careless, but since there's no good reason to
155
- # do assignment on parsed objects, hopefully this should not
156
- # cause problems often.
157
- new_value = params[0]
158
- self[key] = ap_schema.new(new_value)
159
- else
160
- ap_schema.new(value)
161
- end
162
- end
163
95
  else
164
96
  @additional_properties_schema = nil
165
97
  end
166
98
 
99
+ define_method('method_missing') do |method, *params, &block|
100
+ # We need to convert from Ruby calling style to JavaScript calling
101
+ # style. If this fails, attempt to use JavaScript calling style
102
+ # directly.
103
+
104
+ # We can't modify the method in-place because this affects the call
105
+ # to super.
106
+ stripped_method = method.to_s
107
+ assignment = false
108
+ if stripped_method[-1..-1] == '='
109
+ assignment = true
110
+ stripped_method[-1..-1] = ''
111
+ end
112
+ key = INFLECTOR.camelize(stripped_method)
113
+ key[0..0] = key[0..0].downcase
114
+ if @data[key] != nil
115
+ # Data found
116
+ elsif @data[stripped_method] != nil
117
+ # Data found
118
+ key = stripped_method
119
+ else
120
+ # Method not found.
121
+ super(method, *params, &block)
122
+ end
123
+ # If additionalProperties is simply set to true, no parsing takes
124
+ # place and all values are treated as 'any'.
125
+ if assignment
126
+ new_value = params[0]
127
+ self.__set__(key, new_value)
128
+ else
129
+ self.__get__(key)
130
+ end
131
+ end
132
+
167
133
  if schema_data['dependencies']
168
134
  for dependency_key, dependency_data in schema_data['dependencies']
169
135
  self.property_dependencies[dependency_key] = dependency_data
@@ -84,11 +84,11 @@ module AutoParse
84
84
  end
85
85
 
86
86
  def self.data
87
- return @schema_data
87
+ return @schema_data ||= {}
88
88
  end
89
89
 
90
90
  def self.description
91
- return @schema_data['description']
91
+ return self.data['description']
92
92
  end
93
93
 
94
94
  def self.validate_string_property(property_value, schema_data)
@@ -323,47 +323,66 @@ module AutoParse
323
323
  end
324
324
 
325
325
  def __get__(property_name)
326
- property_key = self.class.keys[property_name]
326
+ property_key = self.class.keys[property_name] || property_name
327
327
 
328
328
  schema_class = self.class.properties[property_key]
329
+ schema_class = self.class.additional_properties_schema if !schema_class
329
330
  if !schema_class
330
- raise TypeError,
331
- "Missing property schema for '#{property_key}'."
332
- end
333
- if schema_class.data['$ref']
334
- # Dereference the schema if necessary.
335
- schema_class = schema_class.dereference
336
- # Avoid this dereference in the future.
337
- self.class.properties[property_key] = schema_class
338
- end
331
+ @data[property_key]
332
+ else
333
+ if schema_class.data['$ref']
334
+ # Dereference the schema if necessary.
335
+ schema_class = schema_class.dereference
336
+ # Avoid this dereference in the future.
337
+ self.class.properties[property_key] = schema_class
338
+ end
339
339
 
340
- value = self[property_key] || schema_class.data['default']
340
+ value = @data[property_key] || schema_class.data['default']
341
341
 
342
- AutoParse.import(value, schema_class)
342
+ AutoParse.import(value, schema_class)
343
+ end
343
344
  end
344
345
  protected :__get__
345
346
 
346
347
  def __set__(property_name, value)
347
- property_key = self.class.keys[property_name]
348
+ property_key = self.class.keys[property_name] || property_name
348
349
 
349
350
  schema_class = self.class.properties[property_key]
350
- if schema_class.data['$ref']
351
- # Dereference the schema if necessary.
352
- schema_class = schema_class.dereference
353
- # Avoid this dereference in the future.
354
- self.class.properties[property_key] = schema_class
355
- end
351
+ schema_class = self.class.additional_properties_schema if !schema_class
352
+ if !schema_class
353
+ @data[property_key] = value
354
+ else
355
+ if schema_class.data['$ref']
356
+ # Dereference the schema if necessary.
357
+ schema_class = schema_class.dereference
358
+ # Avoid this dereference in the future.
359
+ self.class.properties[property_key] = schema_class
360
+ end
356
361
 
357
- self[property_key] = AutoParse.export(value, schema_class)
362
+ @data[property_key] = AutoParse.export(value, schema_class)
363
+ end
358
364
  end
359
365
  protected :__set__
360
366
 
361
- def [](key)
362
- return @data[key]
367
+ def [](key, raw=false)
368
+ if raw == true
369
+ return @data[key]
370
+ else
371
+ return self.__get__(key)
372
+ end
363
373
  end
364
374
 
365
- def []=(key, value)
366
- return @data[key] = value
375
+ def []=(key, raw=false, value=:undefined)
376
+ if value == :undefined
377
+ # Due to the way Ruby handles default values in assignment methods,
378
+ # we have to swap some values around here.
379
+ raw, value = false, raw
380
+ end
381
+ if raw == true
382
+ return @data[key] = value
383
+ else
384
+ return self.__set__(key, value)
385
+ end
367
386
  end
368
387
 
369
388
  ##
@@ -371,7 +390,7 @@ module AutoParse
371
390
  def valid?
372
391
  unvalidated_fields = @data.keys.dup
373
392
  for property_key, schema_class in self.class.properties
374
- property_value = self[property_key]
393
+ property_value = @data[property_key]
375
394
  if !self.class.validate_property_value(
376
395
  property_value, schema_class.data)
377
396
  return false
@@ -388,7 +407,7 @@ module AutoParse
388
407
  when String, Array
389
408
  property_dependencies = [property_dependencies].flatten
390
409
  for dependency_key in property_dependencies
391
- dependency_value = self[dependency_key]
410
+ dependency_value = @data[dependency_key]
392
411
  return false if dependency_value == nil
393
412
  end
394
413
  when Class
@@ -406,9 +425,13 @@ module AutoParse
406
425
  return false unless unvalidated_fields.empty?
407
426
  elsif self.class.additional_properties_schema != EMPTY_SCHEMA
408
427
  # Validate all remaining fields against this schema
409
-
410
- # Make sure tests don't pass prematurely
411
- return false
428
+ for property_key in unvalidated_fields
429
+ property_value = @data[property_key]
430
+ if !self.class.additional_properties_schema.validate_property_value(
431
+ property_value, self.class.additional_properties_schema.data)
432
+ return false
433
+ end
434
+ end
412
435
  end
413
436
  if self.class.superclass && self.class.superclass != Instance &&
414
437
  self.class.ancestors.first != Instance
@@ -17,8 +17,8 @@ unless defined? AutoParse::VERSION
17
17
  module AutoParse
18
18
  module VERSION
19
19
  MAJOR = 0
20
- MINOR = 2
21
- TINY = 3
20
+ MINOR = 3
21
+ TINY = 0
22
22
 
23
23
  STRING = [MAJOR, MINOR, TINY].join('.')
24
24
  end
@@ -482,6 +482,184 @@ describe AutoParse::Instance, 'with the adult schema' do
482
482
  end
483
483
  end
484
484
 
485
+ describe AutoParse::Instance, 'with the user list schema' do
486
+ before do
487
+ @person_uri = Addressable::URI.new(
488
+ :scheme => 'file',
489
+ :host => '',
490
+ :path => File.expand_path(File.join(spec_dir, './data/person.json'))
491
+ )
492
+ @person_schema_data =
493
+ JSON.parse(File.open(@person_uri.path, 'r') { |f| f.read })
494
+ @person_parser = AutoParse.generate(
495
+ @person_schema_data, :uri => @person_uri
496
+ )
497
+
498
+ @user_list_uri = Addressable::URI.new(
499
+ :scheme => 'file',
500
+ :host => '',
501
+ :path => File.expand_path(File.join(spec_dir, './data/user-list.json'))
502
+ )
503
+ @user_list_schema_data =
504
+ JSON.parse(File.open(@user_list_uri.path, 'r') { |f| f.read })
505
+ @user_list_parser = AutoParse.generate(
506
+ @user_list_schema_data, :uri => @user_list_uri
507
+ )
508
+ end
509
+
510
+ it 'should have the correct URI' do
511
+ @user_list_parser.uri.should === @user_list_uri
512
+ end
513
+
514
+ it 'should accept a valid person input' do
515
+ instance = @user_list_parser.new({
516
+ "users" => {
517
+ "bobaman@google.com" => {
518
+ "name" => "Bob Aman",
519
+ "age" => 29
520
+ }
521
+ }
522
+ })
523
+ instance.should be_valid
524
+ end
525
+
526
+ it 'should accept extra fields' do
527
+ instance = @user_list_parser.new({
528
+ "users" => {
529
+ "bobaman@google.com" => {
530
+ "name" => "Bob Aman",
531
+ "age" => 29,
532
+ "extra" => "bonus!"
533
+ }
534
+ },
535
+ "extra" => "bonus!"
536
+ })
537
+ instance.should be_valid
538
+ end
539
+
540
+ it 'should not accept additional properties that do not validate' do
541
+ instance = @user_list_parser.new({
542
+ "users" => {
543
+ "bobaman@google.com" => {
544
+ "name" => "Bob Aman",
545
+ "age" => 29
546
+ },
547
+ "oldguy@example.com" => {
548
+ "name" => "Methuselah",
549
+ "age" => 969
550
+ }
551
+ }
552
+ })
553
+ instance.should_not be_valid
554
+ end
555
+
556
+ it 'should not accept additional properties that do not validate' do
557
+ instance = @user_list_parser.new({
558
+ "users" => {
559
+ "bobaman@google.com" => {
560
+ "name" => "Bob Aman",
561
+ "age" => 29.7
562
+ }
563
+ }
564
+ })
565
+ instance.should_not be_valid
566
+ end
567
+
568
+ it 'should expose values via generated accessors' do
569
+ instance = @user_list_parser.new({
570
+ "users" => {
571
+ "bobaman@google.com" => {
572
+ "name" => "Bob Aman",
573
+ "age" => 29
574
+ }
575
+ }
576
+ })
577
+ instance.users['bobaman@google.com'].name.should == "Bob Aman"
578
+ instance.users['bobaman@google.com'].age.should == 29
579
+ end
580
+
581
+ it 'should permit access to raw values' do
582
+ instance = @user_list_parser.new({
583
+ "users" => {
584
+ "bobaman@google.com" => {
585
+ "name" => "Bob Aman",
586
+ "age" => 29
587
+ }
588
+ }
589
+ })
590
+ instance.users['bobaman@google.com', true].should == {
591
+ "name" => "Bob Aman",
592
+ "age" => 29
593
+ }
594
+ end
595
+
596
+ it 'should alter output structure via generated mutators' do
597
+ instance = @user_list_parser.new
598
+ instance.users = {}
599
+ instance.users["bobaman@google.com"] = @person_parser.new
600
+ instance.users["bobaman@google.com"].name = "Bob Aman"
601
+ instance.users["bobaman@google.com"].age = 29
602
+ instance.to_hash.should == {
603
+ "users" => {
604
+ "bobaman@google.com" => {
605
+ "name" => "Bob Aman",
606
+ "age" => 29
607
+ }
608
+ }
609
+ }
610
+ end
611
+
612
+ it 'should allow raw assignment' do
613
+ instance = @user_list_parser.new
614
+ instance.users = {}
615
+ instance.users["bobaman@google.com", true] = {
616
+ "name" => "Bob Aman",
617
+ "age" => 29
618
+ }
619
+ instance.to_hash.should == {
620
+ "users" => {
621
+ "bobaman@google.com" => {
622
+ "name" => "Bob Aman",
623
+ "age" => 29
624
+ }
625
+ }
626
+ }
627
+ end
628
+
629
+ it 'should be coerceable to a Hash value' do
630
+ instance = @user_list_parser.new({
631
+ "users" => {
632
+ "bobaman@google.com" => {
633
+ "name" => "Bob Aman",
634
+ "age" => 29
635
+ }
636
+ }
637
+ })
638
+ instance.to_hash.should == {
639
+ "users" => {
640
+ "bobaman@google.com" => {
641
+ "name" => "Bob Aman",
642
+ "age" => 29
643
+ }
644
+ }
645
+ }
646
+ end
647
+
648
+ it 'should convert to a JSON string' do
649
+ instance = @user_list_parser.new({
650
+ "users" => {
651
+ "bobaman@google.com" => {
652
+ "name" => "Bob Aman",
653
+ "age" => 29
654
+ }
655
+ }
656
+ })
657
+ instance.to_json.should == (
658
+ '{"users":{"bobaman@google.com":{"name":"Bob Aman","age":29}}}'
659
+ )
660
+ end
661
+ end
662
+
485
663
  describe AutoParse::Instance, 'with the positive schema' do
486
664
  before do
487
665
  @positive_uri = Addressable::URI.new(
@@ -661,6 +839,21 @@ describe AutoParse::Instance, 'with the card schema' do
661
839
  @card_parser.uri.should === @card_uri
662
840
  end
663
841
 
842
+ it 'should allow nested objects to be accessed in raw' do
843
+ instance = @card_parser.new({
844
+ "givenName" => "Robert",
845
+ "familyName" => "Aman",
846
+ "org" => {
847
+ "organizationName" => "Google, Inc.",
848
+ "organizationUnit" => "Developer Relations"
849
+ }
850
+ })
851
+ instance['org', true].should == {
852
+ "organizationName" => "Google, Inc.",
853
+ "organizationUnit" => "Developer Relations"
854
+ }
855
+ end
856
+
664
857
  it 'should have the correct URI for anonymous nested objects' do
665
858
  instance = @card_parser.new({
666
859
  "givenName" => "Robert",
@@ -0,0 +1,12 @@
1
+ {
2
+ "description": "A mapping of users.",
3
+ "type": "object",
4
+ "properties": {
5
+ "users": {
6
+ "type": "object",
7
+ "additionalProperties": {
8
+ "$ref": "person.json"
9
+ }
10
+ }
11
+ }
12
+ }
metadata CHANGED
@@ -1,12 +1,13 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: autoparse
3
3
  version: !ruby/object:Gem::Version
4
- prerelease: false
4
+ hash: 19
5
+ prerelease:
5
6
  segments:
6
7
  - 0
7
- - 2
8
8
  - 3
9
- version: 0.2.3
9
+ - 0
10
+ version: 0.3.0
10
11
  platform: ruby
11
12
  authors:
12
13
  - Bob Aman
@@ -14,8 +15,7 @@ autorequire:
14
15
  bindir: bin
15
16
  cert_chain: []
16
17
 
17
- date: 2011-10-21 00:00:00 +03:00
18
- default_executable:
18
+ date: 2011-11-10 00:00:00 Z
19
19
  dependencies:
20
20
  - !ruby/object:Gem::Dependency
21
21
  name: addressable
@@ -25,6 +25,7 @@ dependencies:
25
25
  requirements:
26
26
  - - ~>
27
27
  - !ruby/object:Gem::Version
28
+ hash: 3
28
29
  segments:
29
30
  - 2
30
31
  - 2
@@ -40,6 +41,7 @@ dependencies:
40
41
  requirements:
41
42
  - - ">="
42
43
  - !ruby/object:Gem::Version
44
+ hash: 11
43
45
  segments:
44
46
  - 1
45
47
  - 4
@@ -55,6 +57,7 @@ dependencies:
55
57
  requirements:
56
58
  - - ">="
57
59
  - !ruby/object:Gem::Version
60
+ hash: 37
58
61
  segments:
59
62
  - 0
60
63
  - 9
@@ -70,6 +73,7 @@ dependencies:
70
73
  requirements:
71
74
  - - ~>
72
75
  - !ruby/object:Gem::Version
76
+ hash: 57
73
77
  segments:
74
78
  - 0
75
79
  - 8
@@ -85,6 +89,7 @@ dependencies:
85
89
  requirements:
86
90
  - - ~>
87
91
  - !ruby/object:Gem::Version
92
+ hash: 23
88
93
  segments:
89
94
  - 2
90
95
  - 6
@@ -100,6 +105,7 @@ dependencies:
100
105
  requirements:
101
106
  - - ~>
102
107
  - !ruby/object:Gem::Version
108
+ hash: 23
103
109
  segments:
104
110
  - 0
105
111
  - 3
@@ -115,6 +121,7 @@ dependencies:
115
121
  requirements:
116
122
  - - ~>
117
123
  - !ruby/object:Gem::Version
124
+ hash: 23
118
125
  segments:
119
126
  - 1
120
127
  - 1
@@ -154,6 +161,7 @@ files:
154
161
  - spec/data/person.json
155
162
  - spec/data/positive.json
156
163
  - spec/data/schema.json
164
+ - spec/data/user-list.json
157
165
  - spec/spec.opts
158
166
  - spec/spec_helper.rb
159
167
  - tasks/clobber.rake
@@ -169,7 +177,6 @@ files:
169
177
  - LICENSE
170
178
  - Rakefile
171
179
  - README.md
172
- has_rdoc: true
173
180
  homepage: http://autoparse.rubyforge.org/
174
181
  licenses: []
175
182
 
@@ -184,6 +191,7 @@ required_ruby_version: !ruby/object:Gem::Requirement
184
191
  requirements:
185
192
  - - ">="
186
193
  - !ruby/object:Gem::Version
194
+ hash: 3
187
195
  segments:
188
196
  - 0
189
197
  version: "0"
@@ -192,13 +200,14 @@ required_rubygems_version: !ruby/object:Gem::Requirement
192
200
  requirements:
193
201
  - - ">="
194
202
  - !ruby/object:Gem::Version
203
+ hash: 3
195
204
  segments:
196
205
  - 0
197
206
  version: "0"
198
207
  requirements: []
199
208
 
200
209
  rubyforge_project: autoparse
201
- rubygems_version: 1.3.7
210
+ rubygems_version: 1.8.6
202
211
  signing_key:
203
212
  specification_version: 3
204
213
  summary: A parsing system based on JSON Schema.