autoparse 0.2.3 → 0.3.0

Sign up to get free protection for your applications and to get access to all the features.
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.