Skip to content
This repository

HTTPS clone URL

Subversion checkout URL

You can clone with HTTPS or Subversion.

Download ZIP
  • 4 commits
  • 12 files changed
  • 0 comments
  • 1 contributor
6  happypdf_json_schema.gemspec
... ...
@@ -1,6 +1,6 @@
1 1
 # -*- encoding: utf-8 -*-
2 2
 $:.push File.expand_path('../lib', __FILE__)
3  
-require 'happypdf_schema/version'
  3
+require 'happypdf/version'
4 4
 
5 5
 Gem::Specification.new do |s|
6 6
   s.version = HappyPdf::Schema::VERSION
@@ -19,12 +19,10 @@ Besides ruby users can use a small lib with utility methods to load and test the
19 19
 
20 20
   s.required_rubygems_version = Gem::Requirement.new(">= 0") if s.respond_to? :required_rubygems_version=
21 21
   s.require_paths = ['lib']
22  
-  s.rubygems_version = '1.8.24'
23 22
 
24  
-  s.add_runtime_dependency 'activesupport'
25 23
   s.add_development_dependency 'rdoc'
26 24
   s.add_development_dependency 'rspec'
27  
-  s.add_development_dependency 'simplecov'
  25
+  s.add_development_dependency 'json_schema_tools'
28 26
   s.add_development_dependency 'rake', '>= 0.9.2'
29 27
 
30 28
 end
184  lib/happypdf/schema.rb
... ...
@@ -1,183 +1,11 @@
1 1
 # encoding: utf-8
2  
-require 'active_support'
3  
-require 'active_support/core_ext/hash/indifferent_access'
4  
-require 'active_support/core_ext/string/inflections'
5  
-
6 2
 module HappyPdf
7 3
   class Schema
8  
-
9  
-    class << self
10  
-
11  
-      # class var remembering already read-in schema's
12  
-      # {
13  
-      #   'v1.0'=>{
14  
-      #     :invoice =>{schema}
15  
-      #     :credit_note =>{schema}
16  
-      #   }
17  
-      # }
18  
-      # === Return
19  
-      #<Hash{String=>Hash{Symbol=>HashWithIndifferentAccess}}>::
20  
-      def registry
21  
-        @registry ||={}
22  
-      end
23  
-
24  
-      def registry_reset
25  
-        @registry = nil
26  
-      end
27  
-      # Read a schema with a given version and return it as hash
28  
-      # See ../json folder for available schema's and versions
29  
-      # === Parameter
30  
-      # schema<String|Symbol>::name of the schema, available ones are in json directory
31  
-      # version<String>:: version to read, the folder name where the schema is in.
32  
-      # Can be used without v-prefix
33  
-      # === Return
34  
-      # <HashWithIndifferentAccess>:: schema as hash
35  
-      def read(schema, version)
36  
-        schema = schema.to_sym
37  
-        return registry[version][schema] if registry[version] && registry[version][schema]
38  
-        # prefix version with v1.0 of v is not present
39  
-        v = (version =~ /^v/) ? version : "v#{version}"
40  
-        registry[v] = {} unless registry[v]
41  
-        # read schema from file
42  
-        file_path = File.join(File.dirname(__FILE__), '../json', v, "#{schema}.json")
43  
-        plain_data = File.open(file_path, 'r'){|f| f.read}
44  
-        # remember & return
45  
-        registry[v][schema] = ActiveSupport::JSON.decode(plain_data).with_indifferent_access
46  
-      end
47  
-
48  
-      # Read all available schemas from a given version(folder) and return
49  
-      # them as array
50  
-      # See ../json folder for available schema's and versions
51  
-      # === Parameter
52  
-      # schema<String|Symbol>:: version to read, equals json/foldername v1.0 or
53  
-      # use without prefix 1.0
54  
-      # === Return
55  
-      # Array[<HashWithIndifferentAccess>]:: array of schemas as hash
56  
-      def read_all(version)
57  
-        schemas = []
58  
-        v = (version =~ /^v/) ? version : "v#{version}"
59  
-        registry[v] = {} unless registry[v]
60  
-        file_path = File.join(File.dirname(__FILE__), '../json', v, '*.json')
61  
-        Dir.glob( file_path ).each do |file|
62  
-          schema_name = File.basename(file, ".json").to_sym
63  
-          schemas << read(schema_name, v)
64  
-        end
65  
-        schemas
66  
-      end
67  
-
68  
-      # Create a Hash with the available (api)object attributes defined in the
69  
-      # according schema properties. This is the meat of the
70  
-      # object-to-api-markup workflow
71  
-      #
72  
-      # === Example
73  
-      #  obj = Invoice.new(:title =>'hello world', :number=>'4711')
74  
-      #
75  
-      #  obj_hash = HappyPdf::Schema.to_hash_from_schema(obj, 'v1.0')
76  
-      #   => { 'invoice' =>{'title'=>'hello world', 'number'=>'4711' } }
77  
-      #
78  
-      #  obj_hash = HappyPdf::Schema.to_hash_from_schema(obj, 'v1.0', :fields=>['title'])
79  
-      #   => { 'invoice' =>{'title'=>'hello world' } }
80  
-      #
81  
-      #  obj_hash = HappyPdf::Schema.to_hash_from_schema(obj, 'v1.0', :class_name=>:document)
82  
-      #   => { 'document' =>{'title'=>'hello world' } }
83  
-      #
84  
-      # === Parameter
85  
-      # obj<Object>:: An ruby object which is returned as hash
86  
-      # version<String>:: the schema version, must be a valid folder name see
87  
-      # #self.read
88  
-      # opts<Hash{Symbol=>Mixed} >:: additional options
89  
-      #
90  
-      # ==== opts Parameter
91  
-      # class_name<String|Symbol>:: Name of the class to use as hash key. Should be
92  
-      # a lowered, underscored name and it MUST have an existing schema file.
93  
-      # Use it to override the default, which is obj.class.name
94  
-      # fields<Array[String]>:: Fields/properties to return. If not set all
95  
-      # schema's properties are used.
96  
-      #
97  
-      # === Return
98  
-      # <Hash{String=>{String=>Mixed}}>:: The object as hash:
99  
-      # { invoice =>{'title'=>'hello world', 'number'=>'4711' } }
100  
-      def to_hash_from_schema(obj, version, opts={})
101  
-        fields = opts[:fields]
102  
-        # get objects class name without inheritance
103  
-        real_class_name = obj.class.name.split('::').last.underscore
104  
-        class_name =  opts[:class_name] || real_class_name
105  
-        data = {}
106  
-        # get schema
107  
-        schema = read(class_name, version)
108  
-        # iterate over the defined schema fields
109  
-        schema['properties'].each do |field, prop|
110  
-          next if fields && !fields.include?(field)
111  
-          if prop['type'] == 'array'
112  
-            data[field] = [] # always set an empty array
113  
-            if rel_objects = obj.send( field )
114  
-              rel_objects.each do |rel_obj|
115  
-                # call related objects to_hash_from_schema method ex:
116  
-                # data[:client][:addresses] << SKApi::Models::Address.to_hash_from_schema(object)
117  
-                data[field] << to_hash_from_schema(rel_obj, version)
118  
-              end
119  
-            end
120  
-          elsif prop['type'] == 'object' # a singular related object
121  
-            data[field] = nil # always set empty val
122  
-            if rel_obj = obj.send( field )
123  
-              #dont nest field to prevent => client=>{client=>{data} }
124  
-              data[field] = to_hash_from_schema(rel_obj, version)
125  
-            end
126  
-          else # a simple field is only added if the object knows it
127  
-            data[field] = obj.send(field) if obj.respond_to?(field)
128  
-          end
129  
-        end
130  
-        hsh = { "#{class_name}" => data }
131  
-        #add links if present
132  
-        real_class_schema = read(real_class_name, version)
133  
-        links = parse_links(obj, real_class_schema)
134  
-        links && hsh['links'] = links
135  
-        # return hash
136  
-        hsh
137  
-      end
138  
-
139  
-      # Parse the link section of the schema by replacing {id} in urls
140  
-      # === Returns
141  
-      # <Array[Hash{String=>String}]>::
142  
-      # <nil>:: no links present
143  
-      def parse_links(obj, schema)
144  
-        links = []
145  
-        schema['links'] && schema['links'].each do |link|
146  
-          links << { 'rel' => link['rel'], 'href' => link['href'].gsub(/\{id\}/, "#{obj.id}") }
147  
-        end
148  
-        links.uniq
149  
-        # return links only if not empty
150  
-        links.empty? ? nil : links
151  
-      end
152  
-
153  
-      # Clean a hash before a new object is created from it. Can be used in
154  
-      # your ruby controllers where new objects are created from a params-hash
155  
-      # Directly operates and CHANGES incoming properties-hash!
156  
-      #
157  
-      # === Parameter
158  
-      # obj_name<String>:: name of the object/schema
159  
-      # props<Hash{String|Symbol=>Mixed}>:: properties of the object as hash
160  
-      # version<String>:: api version
161  
-      # opts<Hash{Symbol=>Mixed}>:: additional options
162  
-      # === Param opts
163  
-      # :keep<Array[String]>:: properties not being kicked even if defined as
164  
-      # readonly
165  
-      def clean_hash!(obj_name, props, version, opts={})
166  
-        # gather allowed properties
167  
-        schema = read(obj_name, version)
168  
-        setters = []
169  
-        schema['properties'].each{ |k,v| setters << k if !v['readonly'] }
170  
-        setters += opts[:keep] if opts[:keep] && opts[:keep].is_a?(Array)
171  
-        # kick readonly
172  
-        props.delete_if { |k,v| !setters.include?("#{k}")  }
173  
-        #convert to type in schema
174  
-        props.each do |k,v|
175  
-          if schema['properties']["#{k}"]['type'] == 'string' && !v.is_a?(String)
176  
-           props[k] = "#{v}"
177  
-          end
178  
-        end
179  
-      end
180  
-
181  
-    end # class methods
  4
+    # Get the path to schema files. So it can be used f.ex. with json_schema_tools
  5
+    # gem
  6
+    # @param [String] version folder name to use
  7
+    def self.path(version='v1.0')
  8
+      File.expand_path( File.join('../../schema', version), File.dirname(__FILE__))
  9
+    end
182 10
   end
183 11
 end
2  lib/happypdf/version.rb
... ...
@@ -1,5 +1,5 @@
1 1
 module HappyPdf
2 2
   class Schema
3  
-    VERSION='0.0.1'
  3
+    VERSION='0.0.2'
4 4
   end
5 5
 end
19  schema/v1.0/block.json
... ...
@@ -1,5 +1,6 @@
1 1
 {
2  
-  "name": "Block",
  2
+  "title": "Block",
  3
+  "name": "block",
3 4
   "description":"A block acts as a placeholder in PDF templates. It can hold text, multiline text, images or pdf's. In this file you find the general properties for all block types. See details in the derived block-type schemas.",
4 5
   "type":"object",
5 6
   "properties":{
@@ -9,16 +10,10 @@
9 10
       "maxLength": 125,
10 11
       "required": true
11 12
     },
12  
-    "subtype": {
13  
-      "description": "The block type, one of Text, Image, or PDF",
14  
-      "type": "string",
15  
-      "enum": ["Text", "Image", "PDF"],
16  
-      "required": true
17  
-    },
18 13
     "rect":{
19 14
       "description": "The block coordinates in DTP points. [ LowerLeftX llY UpperRightX urY] x-increases to the right, y counts upwards, The coordinate system in PDF starts in the lower left corner of a page. Calculation with 72 DPI: 1pt = 1/72inch = 25.4/72 mm = 0.3528 mm",
20 15
       "type": "array",
21  
-      "items":{
  16
+      "properties":{
22 17
         "type": "number"
23 18
       },
24 19
       "minItems": 4,
@@ -47,14 +42,14 @@
47 42
       "default": "north"
48 43
     },
49 44
     "position": {
50  
-      "description": "Reference point for content placement. Defaults to lower-left corner[0,0]. Values are [x,y] percentages from the start point. E.G. top-right: [100,100] center: [50,50] bottom-center [50,0]. NOT available for text-flow blocks",
  45
+      "description": "Reference point for content placement. Defaults to lower-left corner[0,0]. Values are [x,y] percentages from the start point. E.G. top-right: [100,100] center: [50,50] bottom-center [50,0]. NOT available for textflow blocks",
51 46
       "type": "array",
52 47
       "items":{
53  
-        "type": "number"
  48
+        "type": ["number", "string"]
54 49
       },
55  
-      "minItems": 1,
  50
+      "minItems": 2,
56 51
       "maxItems": 2
57  
-      },
  52
+    },
58 53
      "rotate": {
59 54
        "description": "Rotation angle(degrees) by which the block will be rotated counter-clockwise. The center of the rotation is the position-value, which defaults to lower-left corner.",
60 55
        "type": "number",
6  schema/v1.0/page.json
... ...
@@ -1,7 +1,7 @@
1 1
 {
2 2
   "type": "object",
3 3
   "title": "Page",
4  
-  "name": "Page",
  4
+  "name": "page",
5 5
   "description": "A page of a PDF which can either belong to a PDF template or archived PDF.",
6 6
   "properties": {
7 7
     "id": {
@@ -48,7 +48,7 @@
48 48
       "type": "integer"
49 49
     },
50 50
     "screenshot": {
51  
-      "description": "Filename of the screenshot.",
  51
+      "description": "Screenshot file url png",
52 52
       "type": "string",
53 53
       "maxlength": 128
54 54
     },
@@ -57,7 +57,7 @@
57 57
       "type": "integer"
58 58
     },
59 59
     "blocks": {
60  
-      "description": "Placeholder blocks on this page",
  60
+      "description": "Placeholder blocks on this page,, if it belongs to a PDF Template",
61 61
       "type": "array",
62 62
       "properties": {"$ref":"./block.json#properties"}
63 63
     }
11  schema/v1.0/pdt.json
... ...
@@ -1,7 +1,7 @@
1 1
 {
2 2
   "type": "object",
3 3
   "title": "PDF Template",
4  
-  "name": "Pdt",
  4
+  "name": "pdt",
5 5
   "description": "A PDF Template is a plain PDF with data-placeholders(blocks) in it. During the PDF creation process each block is substituted with supplied plain-text or image data.",
6 6
   "properties": {
7 7
     "id": {
@@ -20,11 +20,6 @@
20 20
       "type": "string",
21 21
       "maxlength": 60
22 22
     },
23  
-    "disk_filename": {
24  
-      "description": "Cleaned and unique filename on disk.",
25  
-      "type": "string",
26  
-      "maxlength": 255
27  
-    },
28 23
     "description": {
29 24
       "description": "A description of the template",
30 25
       "type": "string"
@@ -71,8 +66,8 @@
71 66
     },
72 67
     "pages": {
73 68
       "description": "Pages of the doc.",
74  
-      "type": "Array",
75  
-      "properties": {"$ref":"./page.json#properties"}
  69
+      "type": "array",
  70
+      "properties":{"$ref":"./page.json#properties"}
76 71
     }
77 72
   },
78 73
   "links": [
3  schema/v1.0/table.json
... ...
@@ -1,5 +1,6 @@
1 1
 {
2  
-  "name": "Table",
  2
+  "title": "Table",
  3
+  "name": "table",
3 4
   "description":"A table to be rendered into a pdf. The table is more or less just a nested hash. Formatting options and table sections(footer, header, body) are on the top level. Rows with cells and options are nested in array-hash structures beneath each section-key.",
4 5
   "type":"object",
5 6
   "properties":{
3  schema/v1.0/table_cell.json
... ...
@@ -1,5 +1,6 @@
1 1
 {
2  
-  "name": "TableCell",
  2
+  "title": "TableCell",
  3
+  "name": "table_cell",
3 4
   "description":"Table formatting can be done by passing a json string in the data-attribute table-opts in a html table definition.",
4 5
   "type":"object",
5 6
   "properties":{
10  schema/v1.0/text_block.json
... ...
@@ -1,8 +1,9 @@
1 1
 {
2  
-  "name": "TextBlock",
  2
+  "title": "Text Block",
  3
+  "extends": "block",
3 4
   "description":"A single line text block. Multiline text-blocks inherit from this one, see textflow for ref.",
  5
+  "name": "text_block",
4 6
   "type":"object",
5  
-  "extends": "Block",
6 7
   "properties":{
7 8
     "defaulttext": {
8 9
       "description": "Content if no text is supplied by the client.",
@@ -32,11 +33,6 @@
32 33
       "format": "color",
33 34
       "default": "gray 0"
34 35
     },
35  
-    "textflow":{
36  
-      "description": "This setting must be false, as it make the block a text-line box.",
37  
-      "type": "boolean",
38  
-      "default": "false"
39  
-    },
40 36
     "fitmethod":{
41 37
       "type": "string",
42 38
       "enum": ["auto","nofit","clip","meet", "slice", "entire"],
11  schema/v1.0/textflow_block.json
... ...
@@ -1,15 +1,10 @@
1 1
 {
2  
-  "name": "TextFlowBlock",
  2
+  "title": "TextFlowBlock",
  3
+  "name": "textflow_block",
3 4
   "description":"Multi line text block, inheriting all properties from Block and TextBlock.",
4 5
   "type":"object",
5  
-  "extends": "TextBlock",
  6
+  "extends": "text_block",
6 7
   "properties":{
7  
-    "textflow ":{
8  
-      "description": "This setting must be true, as it makes the block a textflow box.",
9  
-      "type": "boolean",
10  
-      "default": true,
11  
-      "required": true
12  
-    },
13 8
     "alignment":{
14 9
       "description": "Alignment for lines in a paragraph.",
15 10
       "type": "string",
22  spec/happypdf_schema_spec.rb
... ...
@@ -0,0 +1,22 @@
  1
+require 'spec_helper'
  2
+
  3
+describe HappyPdf::Schema do
  4
+
  5
+  context 'path' do
  6
+    it 'should provide path to schema files' do
  7
+      HappyPdf::Schema.path.should == File.expand_path( File.join('../schema/v1.0'), File.dirname(__FILE__))
  8
+    end
  9
+  end
  10
+
  11
+  context 'read schemata' do
  12
+
  13
+    it 'should read all json files' do
  14
+      SchemaTools.schema_path = HappyPdf::Schema.path
  15
+      SchemaTools::Reader.read_all
  16
+      SchemaTools::Reader.registry.should_not be_empty
  17
+    end
  18
+
  19
+  end
  20
+
  21
+
  22
+end
10  spec/spec_helper.rb
... ...
@@ -0,0 +1,10 @@
  1
+# encoding: utf-8
  2
+$:.unshift(File.dirname(__FILE__))
  3
+$:.unshift(File.join(File.dirname(__FILE__), '..', 'lib'))
  4
+
  5
+require 'rspec'
  6
+require 'json_schema_tools'
  7
+require 'happypdf_json_schema'
  8
+
  9
+RSpec.configure do |config|
  10
+end

No commit comments for this range

Something went wrong with that request. Please try again.