json_schema_tools 0.1.2 → 0.2.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.md CHANGED
@@ -1,6 +1,10 @@
1
1
  # Changelog JSON Schema Tools
2
2
 
3
3
 
4
+ 2013-10
5
+
6
+ * add option to exclude_root in to_schema hash method
7
+
4
8
  2013-06
5
9
 
6
10
  * *has_schema_attr :schema=>{schema as ruby hash}* allow pass a schema as hash
@@ -8,10 +12,10 @@
8
12
  * rails 4 (ActiveModel) compatibility
9
13
  * Testing with different ActiveModel Versions
10
14
 
11
-
12
15
  2013-02
13
16
 
14
17
  * add validations for classes generated by KlassFactory
15
18
 
16
19
  2012-12
20
+
17
21
  * initial version with reader, hasher, params cleaner, attributes module
data/README.md CHANGED
@@ -17,9 +17,12 @@ Hook the gem into your app
17
17
 
18
18
  ## Read Schema
19
19
 
20
- Before the fun begins, with any of the tools, one or multiple JSON schema files
21
- must be available(read into a hash). So first provide a base path where the
22
- schema.json files are located.
20
+ Before the fun begins, with any of the tools, one or multiple JSON schema(files)
21
+ must be available. A schema is converted into a ruby hash and for convenience is
22
+ cached into a registry (global or local). So you only need to initialize them
23
+ once e.g on program start.
24
+
25
+ Provide a base path where the schema json files are located.
23
26
 
24
27
  ```ruby
25
28
  SchemaTools.schema_path = '/path/to/schema-json-files'
@@ -64,29 +67,58 @@ reader.read :client, 'from/path'
64
67
  reader.registry
65
68
  ```
66
69
 
67
- ## Object to Schema JSON
70
+ ## Object to JSON - from Schema
71
+
72
+ As you probably know such is done e.g in rails via object.to_json. While using
73
+ this might be simple, it has a damn big drawback: There is no transparent
74
+ contract about the data-structure, as rails simply uses all fields defined in the
75
+ database(ActiveRecord model). One side-effect: With each migration you are f***ed
68
76
 
69
- A schema provides a (public) contract about an object definition. Therefore an
70
- internal object is converted to it's schema version on delivery(API access).
77
+ A schema provides a public contract about an object definition. Therefore an
78
+ internal object is converted to it's public(schema) version on delivery(API access).
71
79
  First the object is converted to a hash containing only the properties(keys)
72
80
  from its schema definition. Afterwards it is a breeze to convert this hash into
73
81
  JSON, with your favorite generator.
74
82
 
75
- Following uses client.json schema(same as peter.class name) inside the global
76
- schema_path and adds properties to the clients_hash simply calling
83
+ Following uses client.json schema, detected from peter.class name.underscore => "client",
84
+ inside the global schema_path and adds properties to the clients_hash by simply calling
77
85
  client.send('property-name'):
78
86
 
79
87
  ```ruby
88
+ class Client < ActiveRecord::Base
89
+ include SchemaTools::Modules::AsSchema
90
+ end
91
+
80
92
  peter = Client.new name: 'Peter'
81
- client_hash = SchemaTools::Hash.from_schema(peter)
82
- #=> "client"=>{"id"=>12, "name"=> "Peter", "email"=>"",..} # whatever else you have as properties
93
+ peter.as_schema_json
94
+ #=> "client":{"id":12, "name": "Peter", "email":"",..}
95
+
96
+ peter.as_schema_hash
97
+ #=> "client"=>{"id"=>12, "name"=> "Peter", "email"=>"",..}
98
+ ```
99
+
100
+ The AsSchema module is a tiny wrapper for following low level method:
101
+
102
+ ```ruby
103
+ paul = Contact.new name: 'Peter'
104
+ contact_hash = SchemaTools::Hash.from_schema(paul)
105
+ #=> "contact"=>{"id"=>12, "name"=> "Peter", "email"=>"",..}
83
106
  # to_json is up to you .. or your rails controller
84
107
  ```
85
108
 
86
- ### Customise Schema Hash
109
+ ### Customise Output JSON / Hash
110
+
111
+ Following examples show options to customize the resulting json or hash. Of
112
+ course they can be combined.
87
113
 
88
114
  Only use some fields e.g. to save bandwidth
89
115
 
116
+ ```ruby
117
+ peter.as_schema_json(fields:['id', 'name'])
118
+ #=> "client":{"id":12, "name": "Peter"}
119
+ ```
120
+ Of course the low level hash method also supports all of these options:
121
+
90
122
  ```ruby
91
123
  client_hash = SchemaTools::Hash.from_schema(peter, fields:['id', 'name'])
92
124
  #=> "client"=>{"id"=>12, "name"=> "Peter"}
@@ -96,15 +128,29 @@ Use a custom schema name e.g. to represent a client as contact. Assumes you also
96
128
  have a schema named contact.json
97
129
 
98
130
  ```ruby
99
- client_hash = SchemaTools::Hash.from_schema(peter, class_name: 'contact')
100
- #=> "contact"=>{"id"=>12, "name"=> "Peter"}
131
+ peter.as_schema_json(class_name: 'contact')
101
132
  ```
102
133
 
103
- Use a custom schema path
134
+ Set a custom schema path
104
135
 
105
136
  ```ruby
106
- client_hash = SchemaTools::Hash.from_schema(peter, path: 'path-to/json-files/')
107
- #=> "client"=>{"id"=>12, "name"=> "Peter"}
137
+ peter.as_schema_json( path: 'path-to/json-files/')
138
+ ```
139
+
140
+ By default the object hash has the class name (client) and the link-section on
141
+ root level. This divides the data from the available methods and makes a clear
142
+ statement about the object type(it's class).
143
+ If you don't want to traverse that one extra level you can exclude the root
144
+ and move the data one level up. See how class name and links are available
145
+ inline:
146
+
147
+ ```ruby
148
+
149
+ peter.as_schema_json( exclude_root: true )
150
+
151
+ client_hash = SchemaTools::Hash.from_schema(peter, exclude_root: true)
152
+ #=> {"id"=>12, "name"=> "Peter",
153
+ # "_class_name":"client", "_links":[ .. are inlined .. ]}
108
154
  ```
109
155
 
110
156
  ## Parameter cleaning
@@ -125,7 +171,7 @@ end
125
171
  ## Object attributes from Schema
126
172
 
127
173
  Add methods, defined in schema properties, to an existing class.
128
- Very usefull if you are building a API client and don't want to manually add
174
+ Very useful if you are building a API client and don't want to manually add
129
175
  methods to you local classes .. like people NOT using JSON schema
130
176
 
131
177
  ```ruby
@@ -138,6 +184,8 @@ contact = Client.new
138
184
  contact.last_name = 'Rambo'
139
185
  # raw access
140
186
  contact.schema_attrs
187
+ # to json
188
+ contact.as_schema_json
141
189
  ```
142
190
 
143
191
  ## Classes from Schema - KlassFactory
@@ -194,8 +242,7 @@ Testing with different ActiveModel / ActiveSupport Versions:
194
242
  RAILS_VERSION=4 rake spec
195
243
 
196
244
  The RAILS_VERSION switch sets the version of the gems in the Gemfile and is only
197
- usefull in test env.
198
-
245
+ useful in test env.
199
246
 
200
247
  # Credits
201
248
 
data/lib/schema_tools.rb CHANGED
@@ -2,6 +2,7 @@ require 'json'
2
2
  require 'schema_tools/version'
3
3
  require 'schema_tools/modules/read'
4
4
  require 'schema_tools/modules/hash'
5
+ require 'schema_tools/modules/as_schema'
5
6
  require 'schema_tools/modules/attributes'
6
7
  require 'schema_tools/modules/validations'
7
8
  require 'schema_tools/reader'
@@ -5,25 +5,29 @@ module SchemaTools
5
5
 
6
6
  class << self
7
7
 
8
- # Build classes from schema inside the given namespace. Uses all classes
9
- # found in schema path:
8
+ # Build ruby classes from a schema with getter/setter methods for all
9
+ # properties and validation. Uses all classes(json files) found in global
10
+ # schema path or you can add a custom path.
11
+ # A namespace can be given under which the classes will be created.
10
12
  # @example
11
13
  #
12
- # First set a global schema path:
14
+ # First set a(global) schema path:
13
15
  # SchemaTools.schema_path = File.expand_path('../fixtures', __FILE__)
14
16
  #
15
- # Bake classes from all schema.json found
17
+ # Build classes from all json files in schema path
16
18
  # SchemaTools::KlassFactory.build
17
19
  #
18
- # Go use it
20
+ # Go use them
19
21
  # client = Client.new organisation: 'SalesKing'
20
22
  # client.valid?
21
23
  # client.errors.should be_blank
22
- #
24
+ # With custom options:
25
+ # SchemaTools::KlassFactory.build namespace: MyModule,
26
+ # path: 'custom/path/to_json_schema'
23
27
  # @param [Hash] opts
24
28
  # @options opts [SchemaTools::Reader] :reader to use instead of global one
25
- # @options opts [SchemaTools::Reader] :path to schema files instead of global one
26
- # @options opts [SchemaTools::Reader] :namespace of the new classes e.g. MyCustomNamespace::MySchemaClass
29
+ # @options opts [String] :path to schema files instead of global one
30
+ # @options opts [String|Symbol|Module] :namespace of the new classes e.g. MyCustomNamespace::MySchemaClass
27
31
  def build(opts={})
28
32
  reader = opts[:reader] || SchemaTools::Reader
29
33
  schemata = reader.read_all( opts[:path] || SchemaTools.schema_path )
@@ -0,0 +1,40 @@
1
+ require 'active_support/concern'
2
+ module SchemaTools
3
+ module Modules
4
+ # Extend a class so it can be rendered as json from a named schema
5
+ module AsSchema
6
+ extend ActiveSupport::Concern
7
+
8
+ # convert this class to a schema markup.
9
+ # @param [Hash{Symbol=>Mixed}] opts passed on to #SchemaTools::Hash.from_schema
10
+ # @return [String] json
11
+ def as_schema_json(opts={})
12
+ ActiveSupport::JSON.encode(as_schema_hash(opts))
13
+ end
14
+
15
+ # Get the object as hash with fields detected from its schema.
16
+ # The schema is derived from
17
+ # * from options[:class_name]
18
+ # * has_schema_attrs definition(if used)
19
+ # * the underscored class name
20
+ # @param [Hash{Symbol=>Mixed}] opts passed on to #SchemaTools::Hash.from_schema
21
+ # @return [Hash]
22
+ def as_schema_hash(opts={})
23
+ # detect schema name from class method, else class name or opts is used.
24
+ if self.class.schema_name
25
+ opts[:class_name] ||= self.class.schema_name
26
+ end
27
+ SchemaTools::Hash.from_schema(self, opts)
28
+ end
29
+
30
+ module ClassMethods
31
+ # Get or set the schema name used
32
+ def schema_name(name=nil)
33
+ @schema_name = name if name
34
+ @schema_name
35
+ end
36
+ end
37
+
38
+ end
39
+ end
40
+ end
@@ -1,10 +1,11 @@
1
1
  require 'active_support/concern'
2
2
  module SchemaTools
3
3
  module Modules
4
- # Add schema properties to a class by including this module and defining from
4
+ # Add schema properties to a class by using has_schema_attrs to define from
5
5
  # which schema to inherit attributes.
6
6
  module Attributes
7
7
  extend ActiveSupport::Concern
8
+ include SchemaTools::Modules::AsSchema
8
9
 
9
10
  def schema_attrs
10
11
  @schema_attrs ||= {}
@@ -16,10 +17,12 @@ module SchemaTools
16
17
  # @param [Hash<Symbol|String>] opts
17
18
  # @options opts [String] :path schema path
18
19
  # @options opts [SchemaTools::Reader] :reader instance, instead of global reader/registry
19
- def has_schema_attrs(schema, opts={})
20
+ def has_schema_attrs(schema_name, opts={})
20
21
  reader = opts[:reader] || SchemaTools::Reader
21
22
  schema_location = opts[:path] || opts[:schema]
22
- schema = reader.read(schema, schema_location)
23
+ schema = reader.read(schema_name, schema_location)
24
+ # remember name on class level
25
+ self.schema_name(schema_name)
23
26
  # make getter / setter
24
27
  schema[:properties].each do |key, val|
25
28
  # getter
@@ -36,7 +39,7 @@ module SchemaTools
36
39
  end
37
40
  end
38
41
 
39
- end # ClassMethods
42
+ end
40
43
 
41
44
  end
42
45
  end
@@ -24,7 +24,7 @@ module SchemaTools
24
24
  # obj_hash = Schema.to_hash_from_schema(obj, class_name: :document)
25
25
  # => { 'document' =>{'title'=>'hello world' } }
26
26
  #
27
- # @param [Object] obj which is returned as hash
27
+ # @param [Object] obj returned as hash
28
28
  # @param [Hash{Symbol=>Mixed}] opts additional options
29
29
  # @options opts [String|Symbol] :class_name used as hash key. Should be
30
30
  # a lowercase underscored name and it MUST have an existing schema file.
@@ -32,6 +32,8 @@ module SchemaTools
32
32
  # @options opts [Array<String>] :fields to return. If not set all schema
33
33
  # properties are used.
34
34
  # @options opts [String] :path of the schema files overriding global one
35
+ # @options opts [Boolean] :exclude_root if set objects are not nested under
36
+ # their class name and the object hash gets _links and _class_name inline.
35
37
  #
36
38
  # @return [Hash{String=>{String=>Mixed}}] The object as hash:
37
39
  # { 'invoice' => {'title'=>'hello world', 'number'=>'4711' } }
@@ -56,10 +58,17 @@ module SchemaTools
56
58
  data[field] = obj.send(field) if obj.respond_to?(field)
57
59
  end
58
60
  end
59
- hsh = { "#{class_name}" => data }
60
- #add links if present
61
+ #get links if present
61
62
  links = parse_links(obj, schema)
62
- links && hsh['links'] = links
63
+
64
+ if opts[:exclude_root]
65
+ hsh = data
66
+ hsh['_class_name'] = "#{class_name}"
67
+ links && hsh['_links'] = links
68
+ else
69
+ hsh = { "#{class_name}" => data }
70
+ links && hsh['links'] = links
71
+ end
63
72
  hsh
64
73
  end
65
74
 
@@ -88,11 +97,11 @@ module SchemaTools
88
97
  if obj.respond_to?( field ) && rel_objects = obj.send( field )
89
98
  rel_objects.each do |rel_obj|
90
99
  res << if prop['properties'] && prop['properties']['$ref']
91
- #got schema describing the objects
92
- from_schema(rel_obj, opts)
93
- else
94
- rel_obj
95
- end
100
+ #got schema describing the objects
101
+ from_schema(rel_obj, opts)
102
+ else
103
+ rel_obj
104
+ end
96
105
  end
97
106
  end
98
107
  res
@@ -25,8 +25,8 @@ module SchemaTools
25
25
 
26
26
  # Read a schema and return it as hash. You can supply a path or the
27
27
  # global path defined in #SchemaTools.schema_path is used.
28
- # Schemata returned from cache in #registry to prevent filesystem
29
- # round-trips. The cache can be resented with #registry_reset
28
+ # Schemata are returned from cache(#registry) if present to prevent
29
+ # filesystem round-trips. The cache can be reset with #registry_reset
30
30
  #
31
31
  # @param [String|Symbol] schema name to be read from schema path directory
32
32
  # @param [String|Hash] either the path to retrieve schema_name from,
@@ -36,14 +36,14 @@ module SchemaTools
36
36
  schema_name = schema_name.to_sym
37
37
  return registry[schema_name] if registry[schema_name]
38
38
 
39
- if path_or_schema.is_a? ::Hash
39
+ if path_or_schema.is_a?(::Hash)
40
40
  path = nil
41
41
  plain_data = path_or_schema.to_json
42
42
  elsif path_or_schema.is_a?(::String) || path_or_schema.nil?
43
43
  path = path_or_schema
44
44
  file_path = File.join(path || SchemaTools.schema_path, "#{schema_name}.json")
45
45
  else
46
- raise ArgumentError, "Second parameter must be a path or a schema!"
46
+ raise ArgumentError, 'Second parameter must be a path or a schema!'
47
47
  end
48
48
 
49
49
  plain_data ||= File.open(file_path, 'r'){|f| f.read}
@@ -1,3 +1,3 @@
1
1
  module SchemaTools
2
- VERSION = '0.1.2'
2
+ VERSION = '0.2.0'
3
3
  end
@@ -1,7 +1,7 @@
1
1
  {"type":"object",
2
2
  "title": "client",
3
3
  "name": "client",
4
- "description": "A client in SalesKing",
4
+ "description": "A example client.",
5
5
  "properties":{
6
6
  "id":{
7
7
  "description":"Unique identifier - UUID",
@@ -12,7 +12,7 @@
12
12
  "minLength":22
13
13
  },
14
14
  "number":{
15
- "description": "Unique number, auto-created by SK for new client without number.",
15
+ "description": "Unique number, auto-created for new client without number.",
16
16
  "type":"string",
17
17
  "maxLength": 50
18
18
  },
@@ -32,54 +32,11 @@
32
32
  "type":"string",
33
33
  "maxLength": 50
34
34
  },
35
- "gender":{
36
- "description": "Can be empty for a company. Is used in salutation",
37
- "enum":["male", "female"],
38
- "type":"string"
39
- },
40
- "notes":{
41
- "description": "Notes for a contact. For day to day information you should use comments instead.",
42
- "type":"string",
43
- "format": "text"
44
- },
45
- "position":{
46
- "description": "Position of a person in a company.",
47
- "type":"string",
48
- "maxLength": 50
49
- },
50
- "title":{
51
- "description": "Academical title of a person e.g. Dr., Prof",
52
- "type":"string",
53
- "maxLength": 50
54
- },
55
- "tax_number":{
56
- "description": "Tax number, normally applies to a private person",
57
- "type":"string",
58
- "maxLength": 30
59
- },
60
- "vat_number":{
61
- "description": "VAT number, for a company or person paying value added taxes.",
62
- "type":"string",
63
- "maxLength": 30
64
- },
65
35
  "email":{
66
36
  "description": "Email address of the contact.",
67
37
  "type":"string",
68
38
  "maxLength": 100
69
39
  },
70
- "url":{
71
- "description": "An url associated with the person, e.g its company website.",
72
- "type":"string",
73
- "maxLength": 255
74
- },
75
- "birthday":{
76
- "format":"date",
77
- "type":"string"
78
- },
79
- "tag_list":{
80
- "description": "Space separated list of tags. Are split and saved as Tag objects on create, update.",
81
- "type":"string"
82
- },
83
40
  "created_at":{
84
41
  "description": "Date the record was created in SK. Never changes afterwards.",
85
42
  "format":"date-time",
@@ -92,101 +49,26 @@
92
49
  "readonly":true,
93
50
  "type":"string"
94
51
  },
95
- "language":{
96
- "description": "Should be a valid language short-code: de-DE, fr, en-GB; like defined in your account language menu. When the client is emailed, a localized version of a multi-language template(email, pdf) will be used if available. The language will also be set for new documents.",
97
- "type":"string",
98
- "maxLength": 10
99
- },
100
- "currency":{
101
- "description": "Currency code as defined by the ISO 4217 standard(3-letter UPCASE: EUR, USD). If set the currency is taken for new documents.",
102
- "type":"string",
103
- "maxLength": 3,
104
- "minLength": 3
105
- },
106
- "payment_method":{
107
- "description": "Default payment method for used for new documemts",
108
- "enum":["cash","bank_transfer","credit_card","paypal","direct_debit","cheque", "moneybookers", "premium_sms"],
109
- "type":"string"
110
- },
111
- "bank_name":{
112
- "description": "Bank name",
113
- "type":"string",
114
- "maxLength": 70
115
- },
116
- "bank_number":{
117
- "description": "Bank number",
118
- "type":"string",
119
- "maxLength": 35
120
- },
121
- "bank_account_number":{
122
- "description": "Bank account number.",
123
- "type":"string",
124
- "maxLength": 35
125
- },
126
- "bank_iban":{
127
- "description": "IBAN Number of the bank account. Is validated",
128
- "type":"string",
129
- "maxLength": 35
130
- },
131
- "bank_swift":{
132
- "description": "SWIFT BIC- Bank Identifier Code",
133
- "type":"string",
134
- "maxLength": 11
135
- },
136
- "bank_owner":{
137
- "description": "Bank account owner",
138
- "type":"string",
139
- "maxLength": 70
140
- },
141
- "phone_fax":{
142
- "description": "Fax number",
143
- "type":"string",
144
- "maxLength": 30
145
- },
146
- "phone_office":{
147
- "description": "Office phone number",
148
- "type":"string",
149
- "maxLength": 30
150
- },
151
- "phone_home":{
152
- "description": "Private phone number",
153
- "type":"string",
154
- "maxLength": 30
155
- },
156
52
  "phone_mobile":{
157
53
  "description": "Mobile phone number",
158
54
  "type":"string",
159
55
  "maxLength": 30
160
56
  },
161
- "lock_version":{
162
- "description": "Increased on every edit, so SK can detect/prevent a concurrent edit by another user. First save wins.",
163
- "type":"integer"
164
- },
165
57
  "cash_discount":{
166
58
  "description": "Default cash discount for new invoices.",
167
59
  "maximum": 100,
168
60
  "minimum": 0,
169
61
  "type":"number"
170
62
  },
171
- "due_days":{
172
- "description": "Default due days for new invoices.",
173
- "type":"integer"
174
- },
175
- "address_field":{
176
- "description": "Returns the address field used on new docs. Consist of Organisation name and default(first) address",
177
- "readonly":true,
178
- "type":"string"
179
- },
180
63
  "addresses":{
181
64
  "description": "A client can have many addresses, sorted by date descending(new first). Default address is the most recent one.",
182
65
  "type":"array",
183
66
  "properties" : {"$ref":"./address.json#properties"}
184
67
  },
185
- "team_id":{
186
- "description": "A team uuid. If set only the team and its parent teams can see the record.",
187
- "type":"string",
188
- "maxLength": 22,
189
- "minLength":22
68
+ "work_address":{
69
+ "description": "The work address as an example for a single nested/related object",
70
+ "type":"object",
71
+ "properties" : {"$ref":"./address.json#properties"}
190
72
  }
191
73
  },
192
74
  "links":[
@@ -211,55 +93,6 @@
211
93
  "description": "Wildcard search in first, last_name, organisation, email, number",
212
94
  "type":"string"
213
95
  },
214
- "filter[tags]":{
215
- "title" : "Tags",
216
- "description": "Filter by a space delimited list of tags",
217
- "type":"string"
218
- },
219
- "filter[ids]":{
220
- "title" : "Clients",
221
- "description": "A single or a list of client uuids, comma separated",
222
- "type" : "string"
223
- },
224
- "filter[created_at_from]":{
225
- "title" : "From date",
226
- "description": "Objects with a creation date after the date, including given datetime. ISO 8601 format YYY-MM-DDThh:mm:ss+z",
227
- "format" : "date-time",
228
- "type" : "string"
229
- },
230
- "filter[created_at_to]":{
231
- "title" : "To date",
232
- "description": "Objects with a creation date before the date, including given datetime. ISO 8601 format YYY-MM-DDThh:mm:ss+z",
233
- "format" : "date-time",
234
- "type" : "string"
235
- },
236
- "filter[birthday_from]":{
237
- "title" : "From birthday date",
238
- "description": "Contacts with a birthday after and on the date. Leave the birthday-to date blank to only search on this day.",
239
- "format" : "date",
240
- "type" : "string"
241
- },
242
- "filter[birthday_to]":{
243
- "title" : "To birthday date",
244
- "description": "Contacts with a birthday date before and on the date.",
245
- "format" : "date",
246
- "type" : "string"
247
- },
248
- "filter[creator_ids]":{
249
- "title" : "Creator",
250
- "description": "Objects created by the given users uuids, comma separated",
251
- "type" : "string"
252
- },
253
- "filter[number]":{
254
- "title" : "By number",
255
- "description": "Search by number where the number is matched from the start: number%",
256
- "type" : "string"
257
- },
258
- "filter[languages]":{
259
- "title" : "Languages",
260
- "description": "A single or a list of language codes, comma separated",
261
- "type" : "string"
262
- },
263
96
  "sort_by":{
264
97
  "title" : "Sort by",
265
98
  "description": "Sort the results by the given field => number",
@@ -293,27 +126,6 @@
293
126
  },
294
127
  { "rel": "invoices",
295
128
  "href": "clients/{id}/invoices"
296
- },
297
- { "rel": "estimates",
298
- "href": "clients/{id}/estimates"
299
- },
300
- { "rel": "orders",
301
- "href": "clients/{id}/orders"
302
- },
303
- { "rel": "credit_notes",
304
- "href": "clients/{id}/credit_notes"
305
- },
306
- { "rel": "recurrings",
307
- "href": "clients/{id}/recurrings"
308
- },
309
- { "rel": "payment_reminders",
310
- "href": "clients/{id}/payment_reminders"
311
- },
312
- { "rel": "comments",
313
- "href": "clients/{id}/comments"
314
- },
315
- { "rel": "emails",
316
- "href": "clients/{id}/emails"
317
129
  }
318
130
  ]
319
131
  }
@@ -6,7 +6,8 @@ describe SchemaTools::Cleaner do
6
6
  let(:params){
7
7
  { id: 'some id',
8
8
  last_name: 'Clean',
9
- first_name: 'Paul'
9
+ first_name: 'Paul',
10
+ phone_mobile: 110
10
11
  }
11
12
  }
12
13
 
@@ -19,6 +20,11 @@ describe SchemaTools::Cleaner do
19
20
  params[:last_name].should == 'Clean'
20
21
  params[:id].should be_nil
21
22
  end
23
+
24
+ it 'should convert values for string fields' do
25
+ SchemaTools::Cleaner.clean_params!(:client, params)
26
+ params[:phone_mobile].should == '110'
27
+ end
22
28
  end
23
29
  end
24
30
 
@@ -1,12 +1,34 @@
1
1
  require 'spec_helper'
2
2
 
3
+ ################################################################################
4
+ # classes used in tests their naming is important because the respecting
5
+ # json schema is derived from it
6
+ ################################################################################
7
+ class Client
8
+ attr_accessor :first_name, :id, :addresses, :work_address
9
+ end
10
+ class Address
11
+ attr_accessor :city, :zip
12
+ end
13
+
3
14
  class Contact
4
15
  attr_accessor :first_name, :last_name, :addresses, :id
5
16
  end
6
17
 
18
+ # see fixtures/lead.json
19
+ class Lead < Contact
20
+ attr_accessor :links_clicked, :conversion
21
+ end
22
+
23
+ class Conversion
24
+ attr_accessor :from, :to
25
+ end
26
+
27
+
7
28
  describe SchemaTools::Hash do
8
29
 
9
- context 'from_schema' do
30
+ context 'from_schema to hash conversion' do
31
+
10
32
  let(:contact){Contact.new}
11
33
  before :each do
12
34
  contact.first_name = 'Peter'
@@ -40,23 +62,38 @@ describe SchemaTools::Hash do
40
62
  hash['contact']['id'].should == contact.id
41
63
  hash['contact']['first_name'].should be_nil
42
64
  end
43
- end
44
65
 
45
- context 'with nested object values' do
46
- class Client
47
- attr_accessor :first_name, :last_name, :addresses, :id
66
+ it 'should exclude root' do
67
+ hash = SchemaTools::Hash.from_schema(contact, exclude_root: true)
68
+ hash['last_name'].should == 'Paul'
69
+ hash['_class_name'].should == 'contact'
48
70
  end
49
- class Address
50
- attr_accessor :city, :zip
71
+
72
+ it 'should have _links on object if exclude root' do
73
+ hash = SchemaTools::Hash.from_schema(contact, exclude_root: true, class_name: :client)
74
+ hash['_links'].length.should == 8
75
+ end
76
+
77
+ it 'should have _class_name on object if exclude root' do
78
+ hash = SchemaTools::Hash.from_schema(contact, exclude_root: true, class_name: :client)
79
+ hash['_class_name'].should == 'client'
51
80
  end
81
+ end
82
+
83
+ context 'with nested values referencing a schema' do
52
84
 
53
85
  let(:client){Client.new}
54
86
 
55
- it 'should have empty nested array values' do
87
+ it 'should have an empty array if values are missing' do
56
88
  hash = SchemaTools::Hash.from_schema(client)
57
89
  hash['client']['addresses'].should == []
58
90
  end
59
91
 
92
+ it 'should have nil if nested object is missing' do
93
+ hash = SchemaTools::Hash.from_schema(client)
94
+ hash['client']['work_address'].should be_nil
95
+ end
96
+
60
97
  it 'should have nested array values' do
61
98
  a1 = Address.new
62
99
  a1.city = 'Cologne'
@@ -66,19 +103,37 @@ describe SchemaTools::Hash do
66
103
  hash['client']['addresses'].should == [{"address"=>{"city"=>"Cologne", "zip"=>50733}}]
67
104
  end
68
105
 
69
- end
70
-
71
-
72
- context 'with plain nested values' do
106
+ it 'should have nested array values without root' do
107
+ a1 = Address.new
108
+ a1.city = 'Cologne'
109
+ a1.zip = 50733
110
+ client.addresses = [a1]
111
+ hash = SchemaTools::Hash.from_schema(client, exclude_root: true)
112
+ hash['addresses'].should == [{"city"=>"Cologne", "zip"=>50733, "_class_name"=>"address"}]
113
+ end
73
114
 
74
- class Lead < Contact
75
- attr_accessor :links_clicked, :conversion
115
+ it 'should have nested object value' do
116
+ a1 = Address.new
117
+ a1.city = 'Cologne'
118
+ a1.zip = 50733
119
+ client.work_address = a1
120
+ hash = SchemaTools::Hash.from_schema(client)
121
+ hash['client']['work_address'].should == {"address"=>{"city"=>"Cologne", "zip"=>50733}}
76
122
  end
77
123
 
78
- class Conversion
79
- attr_accessor :from, :to
124
+ it 'should have nested object value without root' do
125
+ a1 = Address.new
126
+ a1.city = 'Cologne'
127
+ a1.zip = 50733
128
+ client.work_address = a1
129
+ hash = SchemaTools::Hash.from_schema(client, exclude_root: true)
130
+ hash['work_address'].should == {"city"=>"Cologne", "zip"=>50733, "_class_name"=>"address"}
80
131
  end
81
132
 
133
+ end
134
+
135
+ context 'with plain nested values' do
136
+
82
137
  let(:lead){Lead.new}
83
138
  before :each do
84
139
  lead.links_clicked = ['2012-12-12', '2012-12-15', '2012-12-16']
@@ -1,12 +1,10 @@
1
1
  require 'spec_helper'
2
2
 
3
3
  class TestNamespaceKlass
4
- # namepsace for schema classes
4
+ # namespace for schema classes
5
5
  end
6
6
 
7
- module TestNamespaceModule
8
-
9
- end
7
+ module TestNamespaceModule; end
10
8
 
11
9
  describe SchemaTools::KlassFactory do
12
10
 
@@ -0,0 +1,85 @@
1
+ require 'spec_helper'
2
+
3
+ class ClassWithSchemaAttrs
4
+ include SchemaTools::Modules::Attributes
5
+ has_schema_attrs :client
6
+ end
7
+
8
+ class ClassWithSchemaName
9
+ include SchemaTools::Modules::AsSchema
10
+ schema_name :lead
11
+ end
12
+
13
+ # namespaced to not interfere with classes used in other tests
14
+ module Test
15
+ class Address
16
+ include SchemaTools::Modules::AsSchema
17
+ end
18
+ end
19
+
20
+
21
+ describe SchemaTools::Modules::AsSchema do
22
+
23
+ describe 'included' do
24
+ subject { ClassWithSchemaAttrs.new }
25
+
26
+ it 'should add as_schema_hash method' do
27
+ subject.should respond_to(:as_schema_hash)
28
+ end
29
+
30
+ it 'should add as_schema_json method' do
31
+ subject.should respond_to(:as_schema_json)
32
+ end
33
+
34
+ it 'should return hash' do
35
+ subject.last_name = 'Hogan'
36
+ hsh = subject.as_schema_hash
37
+ hsh['client']['last_name'].should == 'Hogan'
38
+ end
39
+
40
+ it 'should return json' do
41
+ subject.last_name = 'Hogan'
42
+ json_str = subject.as_schema_json
43
+ hsh = ActiveSupport::JSON.decode(json_str)
44
+ hsh['client']['last_name'].should == 'Hogan'
45
+ end
46
+
47
+ end
48
+
49
+ describe 'schema name detection' do
50
+
51
+ it 'should use name from has_schema_attrs' do
52
+ ClassWithSchemaAttrs.new.as_schema_hash.keys.should include('client', 'links')
53
+ end
54
+
55
+ it 'should use schema_name defined in class' do
56
+ ClassWithSchemaName.new.as_schema_hash.keys.should include('lead')
57
+ end
58
+
59
+ it 'should use class name ' do
60
+ Test::Address.new.as_schema_hash.keys.should include('address')
61
+ end
62
+ end
63
+
64
+ describe 'schema options' do
65
+ subject { ClassWithSchemaAttrs.new }
66
+
67
+ it 'should override schema name from' do
68
+ subject.as_schema_hash(class_name:'contact').keys.should include('contact')
69
+ end
70
+
71
+ it 'should use fields' do
72
+ subject.as_schema_hash(fields:['id'])['client'].keys.should == ['id']
73
+ end
74
+
75
+ it 'should exclude root' do
76
+ subject.as_schema_hash(exclude_root: true).keys.should include('id')
77
+ end
78
+
79
+ it 'should use class name ' do
80
+ Test::Address.new.as_schema_hash.keys.should include('address')
81
+ end
82
+ end
83
+
84
+ end
85
+
@@ -27,6 +27,10 @@ describe SchemaTools::Modules::Attributes do
27
27
  subject.should_not respond_to('id=')
28
28
  subject.should_not respond_to('created_at=')
29
29
  end
30
+
31
+ it 'should add schema_name to class' do
32
+ subject.class.schema_name.should == :client
33
+ end
30
34
  end
31
35
 
32
36
  context 'attributes from dynamic schema' do
metadata CHANGED
@@ -1,7 +1,7 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: json_schema_tools
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.1.2
4
+ version: 0.2.0
5
5
  prerelease:
6
6
  platform: ruby
7
7
  authors:
@@ -9,7 +9,7 @@ authors:
9
9
  autorequire:
10
10
  bindir: bin
11
11
  cert_chain: []
12
- date: 2013-10-11 00:00:00.000000000 Z
12
+ date: 2013-10-12 00:00:00.000000000 Z
13
13
  dependencies:
14
14
  - !ruby/object:Gem::Dependency
15
15
  name: json
@@ -96,6 +96,7 @@ files:
96
96
  - lib/schema_tools/cleaner.rb
97
97
  - lib/schema_tools/hash.rb
98
98
  - lib/schema_tools/klass_factory.rb
99
+ - lib/schema_tools/modules/as_schema.rb
99
100
  - lib/schema_tools/modules/attributes.rb
100
101
  - lib/schema_tools/modules/hash.rb
101
102
  - lib/schema_tools/modules/read.rb
@@ -110,6 +111,7 @@ files:
110
111
  - spec/schema_tools/cleaner_spec.rb
111
112
  - spec/schema_tools/hash_spec.rb
112
113
  - spec/schema_tools/klass_factory_spec.rb
114
+ - spec/schema_tools/modules/as_schema_spec.rb
113
115
  - spec/schema_tools/modules/attributes_spec.rb
114
116
  - spec/schema_tools/reader_spec.rb
115
117
  - spec/spec_helper.rb
@@ -128,7 +130,7 @@ required_ruby_version: !ruby/object:Gem::Requirement
128
130
  version: '0'
129
131
  segments:
130
132
  - 0
131
- hash: 2491825896483527088
133
+ hash: -1056530391654598731
132
134
  required_rubygems_version: !ruby/object:Gem::Requirement
133
135
  none: false
134
136
  requirements:
@@ -137,7 +139,7 @@ required_rubygems_version: !ruby/object:Gem::Requirement
137
139
  version: '0'
138
140
  segments:
139
141
  - 0
140
- hash: 2491825896483527088
142
+ hash: -1056530391654598731
141
143
  requirements: []
142
144
  rubyforge_project:
143
145
  rubygems_version: 1.8.24