Module: Prmd

Defined in:
lib/prmd.rb,
lib/prmd/schema.rb,
lib/prmd/version.rb,
lib/prmd/template.rb,
lib/prmd/commands/init.rb,
lib/prmd/url_generator.rb,
lib/prmd/commands/render.rb,
lib/prmd/commands/verify.rb,
lib/prmd/commands/combine.rb

Defined Under Namespace

Classes: Schema, Template, UrlGenerator

Constant Summary collapse

VERSION =
"0.6.2"
SCHEMAS =

These schemas are listed manually and in order because they reference each other.

[
  "schema.json",
  "hyper-schema.json",
  "interagent-hyper-schema.json"
]

Class Method Summary collapse

Class Method Details

.combine(path, options = {}) ⇒ Object



2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
# File 'lib/prmd/commands/combine.rb', line 2

def self.combine(path, options={})
  files = if File.directory?(path)
    Dir.glob(File.join(path, '**', '*.json')) +
      Dir.glob(File.join(path, '**', '*.yaml')) -
      [options[:meta]]
  else
    [path]
  end
  # sort for stable loading on any platform
  schemata = []
  files.sort.each do |file|
    begin
      schemata << [file, YAML.load(File.read(file))]
    rescue
      $stderr.puts "unable to parse #{file}"
    end
  end
  unless schemata.length == files.length
    exit(1) # one or more files failed to parse
  end

  data = {
    '$schema'     => 'http://json-schema.org/draft-04/hyper-schema',
    'definitions' => {},
    'properties'  => {},
    'type'        => ['object']
  }

  # tracks which entities where defined in which file
  schemata_map = {}

  if options[:meta] && File.exists?(options[:meta])
    data.merge!(YAML.load(File.read(options[:meta])))
  end

  schemata.each do |schema_file, schema_data|
    id = schema_data['id'].split('/').last

    if file = schemata_map[schema_data['id']]
      $stderr.puts "`#{schema_data['id']}` (from #{schema_file}) was already defined in `#{file}` and will overwrite the first definition"
    end
    schemata_map[schema_data['id']] = schema_file

    # schemas are now in a single scope by combine
    schema_data.delete('id')

    data['definitions']
    data['definitions'][id] = schema_data
    reference_localizer = lambda do |datum|
      case datum
      when Array
        datum.map {|element| reference_localizer.call(element)}
      when Hash
        if datum.has_key?('$ref')
          datum['$ref'] = '#/definitions' + datum['$ref'].gsub('#', '').gsub('/schemata', '')
        end
        if datum.has_key?('href') && datum['href'].is_a?(String)
          datum['href'] = datum['href'].gsub('%23', '').gsub(%r{%2Fschemata(%2F[^%]*%2F)}, '%23%2Fdefinitions\1')
        end
        datum.each { |k,v| datum[k] = reference_localizer.call(v) }
      else
        datum
      end
    end
    reference_localizer.call(data['definitions'][id])

    data['properties'][id] = { '$ref' => "#/definitions/#{id}" }
  end

  Prmd::Schema.new(data)
end

.init(resource, options = {}) ⇒ Object



2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
# File 'lib/prmd/commands/init.rb', line 2

def self.init(resource, options={})
  data = {
    '$schema'     => 'http://json-schema.org/draft-04/hyper-schema',
    'title'       => 'FIXME',
    'definitions' => {},
    'description' => 'FIXME',
    'links'       => [],
    'properties'  => {},
    'type'        => ['object']
  }

  schema = Prmd::Schema.new(data)

  if resource
    if resource.include?('/')
      parent, resource = resource.split('/')
    end
    schema['id']    = "schemata/#{resource}"
    schema['title'] = "FIXME - #{resource[0...1].upcase}#{resource[1..-1]}"
    schema['definitions'] = {
      "created_at" => {
        "description" => "when #{resource} was created",
        "example"     => "2012-01-01T12:00:00Z",
        "format"      => "date-time",
        "type"        => ["string"]
      },
      "id" => {
        "description" => "unique identifier of #{resource}",
        "example"     => "01234567-89ab-cdef-0123-456789abcdef",
        "format"      => "uuid",
        "type"        => ["string"]
      },
      "identity" => {
        "$ref" => "/schemata/#{resource}#/definitions/id"
      },
      "updated_at" => {
        "description" => "when #{resource} was updated",
        "example"     => "2012-01-01T12:00:00Z",
        "format"      => "date-time",
        "type"        => ["string"]
      }
    }
    schema['links'] = [
      {
        "description"   => "Create a new #{resource}.",
        "href"          => "/#{resource}s",
        "method"        => "POST",
        "rel"           => "create",
        "schema"        => {
          "properties"  => {},
          "type"        => ["object"]
        },
        "title"         => "Create"
      },
      {
        "description"   => "Delete an existing #{resource}.",
        "href"          => "/#{resource}s/{(%2Fschemata%2F#{resource}%23%2Fdefinitions%2Fidentity)}",
        "method"        => "DELETE",
        "rel"           => "destroy",
        "title"         => "Delete"
      },
      {
        "description"   => "Info for existing #{resource}.",
        "href"          => "/#{resource}s/{(%2Fschemata%2F#{resource}%23%2Fdefinitions%2Fidentity)}",
        "method"        => "GET",
        "rel"           => "self",
        "title"         => "Info"
      },
      {
        "description"   => "List existing #{resource}s.",
        "href"          => "/#{resource}s",
        "method"        => "GET",
        "rel"           => "instances",
        "title"         => "List"
      },
      {
        "description"   => "Update an existing #{resource}.",
        "href"          => "/#{resource}s/{(%2Fschemata%2F#{resource}%23%2Fdefinitions%2Fidentity)}",
        "method"        => "PATCH",
        "rel"           => "update",
        "schema"        => {
          "properties"  => {},
          "type"        => ["object"]
        },
        "title"         => "Update"
      }
    ]
    if parent
      schema['links'] << {
        "description"  => "List existing #{resource}s for existing #{parent}.",
        "href"         => "/#{parent}s/{(%2Fschemata%2F#{parent}%23%2Fdefinitions%2Fidentity)}/#{resource}s",
        "method"       => "GET",
        "rel"          => "instances",
        "title"        => "List"
      }
    end
    schema['properties'] = {
      "created_at"  => { "$ref" => "/schemata/#{resource}#/definitions/created_at" },
      "id"          => { "$ref" => "/schemata/#{resource}#/definitions/id" },
      "updated_at"  => { "$ref" => "/schemata/#{resource}#/definitions/updated_at" }
    }
  end

  if options[:yaml]
    schema.to_yaml
  else
    schema.to_json
  end
end

.render(schema, options = {}) ⇒ Object



2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
# File 'lib/prmd/commands/render.rb', line 2

def self.render(schema, options={})
  doc = ''

  options[:content_type] ||= 'application/json'
  options[:style] ||= 'default'

  if options[:prepend]
    doc << options[:prepend].map {|path| File.read(path)}.join("\n") << "\n"
  end

  template_dir = File::expand_path(options[:template])
  if not File.directory?(template_dir)  # to keep backward compatibility
    template_dir = File.dirname(options[:template])
  end

  doc << Prmd::Template::render('schema.erb', template_dir, {
    options:         options,
    schema:          schema
  })

  doc
end

.verify(schema_data) ⇒ Object



13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
# File 'lib/prmd/commands/verify.rb', line 13

def self.verify(schema_data)
  store = init_document_store

  if !(schema_uri = schema_data["$schema"])
    return ["Missing $schema."]
  end

  # for good measure, make sure that the schema parses and that its
  # references can be expanded
  schema, errors = JsonSchema.parse!(schema_data)
  return JsonSchema::SchemaError.aggregate(errors) if !schema

  valid, errors = schema.expand_references(store: store)
  return JsonSchema::SchemaError.aggregate(errors) if !valid

  if !(meta_schema = store.lookup_schema(schema_uri))
    return ["Unknown $schema: #{schema_uri}."]
  end

  valid, errors = meta_schema.validate(schema_data)
  return JsonSchema::SchemaError.aggregate(errors) if !valid

  []
end