Skip to content
This repository has been archived by the owner on Nov 8, 2024. It is now read-only.

Commit

Permalink
Merge pull request #6 from apiaryio/pksunkara/start-expansion
Browse files Browse the repository at this point in the history
Finished expanding MSON
  • Loading branch information
zdne committed Feb 16, 2015
2 parents 25c97d3 + 0f944f0 commit c9387b6
Show file tree
Hide file tree
Showing 9 changed files with 3,281 additions and 2 deletions.
2 changes: 1 addition & 1 deletion package.json
Original file line number Diff line number Diff line change
Expand Up @@ -18,7 +18,7 @@
"dependencies": {
"coffee-script": "~1.7.1",
"yargs": "~1.3.3",
"protagonist-experimental": "0.18.4",
"protagonist-experimental": "0.18.6",
"boutique": "git+ssh://[email protected]:apiaryio/boutique.git"
},
"devDependencies": {
Expand Down
84 changes: 83 additions & 1 deletion src/drafter.coffee
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,9 @@ fs = require 'fs'
#
class Drafter

# List of data structures
@dataStructures: {}

# Default configuration
@defaultConfig:
requireBlueprintName: false # Treat missing API name as error
Expand Down Expand Up @@ -35,7 +38,86 @@ class Drafter
# @param source [String] soruce API Bluerpint code
# @param callback [(Error, ParseResult)]
make: (source, callback) ->
protagonist.parse source, @config, callback
protagonist.parse source, @config, (error, result) =>
callback error if error

ruleList = ['mson-inheritance', 'mson-mixin', 'mson-member-type-name']
rules = (require './rules/' + rule for rule in ruleList)

@dataStructures = {}
delete result.ast.resourceGroups

@expandNode result.ast, rules, 'blueprint'
@reconstructResourceGroups result.ast

callback error, result

# Expand a certain node with the given rules
#
# @param node [Object] A node of API Blueprint
# @param rules [Array] List of rules to apply
# @param elementTye [String] The element type of the node
expandNode: (node, rules, elementType) ->
elementType ?= node.element

# On root node, Gather data structures first before applying rules to any of the children nodes
if elementType is 'blueprint'
for element in node.content

if element.element is 'category'
for subElement in element.content

switch subElement.element
when 'dataStructure'
@dataStructures[subElement.name.literal] = subElement
when 'resource'

for resourceSubElement in subElement.content
@dataStructures[resourceSubElement.name.literal] = resourceSubElement if resourceSubElement.element is 'dataStructure'

# Expand the gathered data structures
for rule in rules
rule.init.call rule, @dataStructures if rule.init

# Apply rules to the current node
for rule in rules
rule[elementType].call rule, node if elementType in Object.keys(rule)

# Recursively do the same for children nodes
switch elementType
when 'resource'
@expandNode action, rules, 'action' for action in node.actions

when 'action'
@expandNode example, rules, 'transactionExample' for example in node.examples

when 'transactionExample'
@expandNode request, rules, 'payload' for request in node.requests
@expandNode response, rules, 'payload' for response in node.responses

if node.content and Array.isArray node.content
@expandNode element, rules for element in node.content

# Reconstruct deprecated resource groups key from elements
#
# @param ast [Object] Blueprint ast
reconstructResourceGroups: (ast) ->
ast.resourceGroups = []

for element in ast.content
if element.element is 'category'
resources = []

for subElement in element.content
resources.push subElement if subElement.element is 'resource'

if resources.length
description = element.content[0].content if element.content[0].element is 'copy'

ast.resourceGroups.push
name: element.attributes?.name || ''
description: description || ''
resources: resources

module.exports = Drafter
module.exports.options = options
88 changes: 88 additions & 0 deletions src/rules/mson-inheritance.coffee
Original file line number Diff line number Diff line change
@@ -0,0 +1,88 @@
rule = require './rule'

module.exports =

# Variables
expanded: {}
dataStructures: {}

# Expand dataStructure element
dataStructure: (element) ->
superType = element.typeDefinition.name
typeName = element.name

if not typeName
typeName =
literal: ''

@expandInheritance typeName.literal, element
delete @expanded['']

# Given a data structure, expand its inheritance recursively
#
# @param name [String] Name of the data structure
# @param dataStructure [Object] Data structure
expandInheritance: (name, dataStructure) ->
return if @expanded[name]

# Check for inheritance
superType = dataStructure.typeDefinition.typeSpecification.name

if superType is null or typeof superType isnt 'object' or not superType?.literal
return @expanded[superType] = true

# Expand the super type first
@expandInheritance superType.literal, @dataStructures[superType.literal]

# If super type is not an object or array or enum
superTypeBaseName = @dataStructures[superType.literal].typeDefinition.typeSpecification.name

if superTypeBaseName not in ['object', 'array', 'value']
dataStructure.typeDefinition.typeSpecification.name = superTypeBaseName
memberTypeSection =
content: []

memberTypeSection['class'] = 'memberType'
rule.copyMembers @dataStructures[superType.literal], memberTypeSection

dataStructure.sections.push memberTypeSection if memberTypeSection.content.length
return @expanded[name] = true

# Find member type section of the current data structure
memberTypeSection = null
push = false

for section in dataStructure.sections
memberTypeSection = section if section['class'] is 'memberType'

# If no member type sections, create one
if not memberTypeSection
memberTypeSection =
content: []

memberTypeSection['class'] = 'memberType'
push = true

# Copy super-type and all the member types to sub type
rule.copyMembers @dataStructures[superType.literal], memberTypeSection
dataStructure.typeDefinition.typeSpecification =
name: superTypeBaseName
nestedTypes: @dataStructures[superType.literal].typeDefinition.typeSpecification.nestedTypes

# Push the created type section
dataStructure.sections.push memberTypeSection if push and memberTypeSection.content.length

# Denote this type as expanded
@expanded[name] = true

init: (dataStructures) ->
@expanded = {}
@dataStructures = dataStructures

# Initiate flags
for name, dataStructure of @dataStructures
@expanded[name] = false

# Actual expansion
for name, dataStructure of @dataStructures
@expandInheritance name, dataStructure
77 changes: 77 additions & 0 deletions src/rules/mson-member-type-name.coffee
Original file line number Diff line number Diff line change
@@ -0,0 +1,77 @@
rule = require './rule'

module.exports =

# Variables
expanded: {}
dataStructures: {}

# Expand dataStructure element
dataStructure: (element) ->
superType = element.typeDefinition.name
typeName = element.name

if not typeName
typeName =
literal: ''

@expandMember typeName.literal, element
delete @expanded['']

# Given a list of elements, recursively expand member type name contained
# in a group of elements inside the initial group of elements
#
# @param elements [Object] List of elements either from type section or a member type
diveIntoElements: (elements) ->
for member in elements
switch member['class']

when 'property', 'value'
superType = member.content.valueDefinition.typeDefinition.typeSpecification.name

# If super type is a valid symbol
if typeof superType is 'object' and superType?.literal
@expandMember superType.literal, @dataStructures[superType.literal]

superTypeBaseName = @dataStructures[superType.literal].typeDefinition.typeSpecification.name
member.content.valueDefinition.typeDefinition.typeSpecification.name = superTypeBaseName

# If super type is not an object or array or enum
if superTypeBaseName in ['object', 'array', 'value']
memberTypeSection =
content: []

memberTypeSection['class'] = 'memberType'
rule.copyMembers @dataStructures[superType.literal], memberTypeSection
member.content.sections.push memberTypeSection if memberTypeSection.content.length

when 'oneOf', 'group'
@diveIntoElements member.content

# Given a data structure, expand its member type recusrively
#
# @param name [String] Name of the data structure
# @param dataStructure [Object] Data structure
expandMember: (name, dataStructure) ->
return if @expanded[name]

# Check for member type name
for section in dataStructure.sections
if section['class'] is 'memberType'

@diveIntoElements section.content

# Denote this type as expanded
@expanded[name] = true

init: (dataStructures) ->
@expanded = {}
@dataStructures = dataStructures

# Initiate flags
for name, dataStructure of @dataStructures
@expanded[name] = false

# Actual expansion
for name, dataStructure of @dataStructures
@expandMember name, dataStructure
84 changes: 84 additions & 0 deletions src/rules/mson-mixin.coffee
Original file line number Diff line number Diff line change
@@ -0,0 +1,84 @@
rule = require './rule'

module.exports =

# Variables
expanded: {}
dataStructures: {}

# Expand dataStructure element
dataStructure: (element) ->
superType = element.typeDefinition.name
typeName = element.name

if not typeName
typeName =
literal: ''

@expandMixin typeName.literal, element
delete @expanded['']

# Given a list of elements, recursively expand mixins contained
# in a group of elements inside the initial group of elements
#
# @param elements [Object] List of elements either from type section or a member type
# @param sectionOrMember [Object] Type section or a member type
diveIntoElements: (elements, sectionOrMember) ->
for member in elements
switch member['class']

when 'mixin'
superType = member.content.typeSpecification.name

# Expand the super type first
@expandMixin superType.literal, @dataStructures[superType.literal]
rule.copyMembers @dataStructures[superType.literal], sectionOrMember

when 'oneOf', 'group'
memberType =
content: []

# Recursively dive into the elements
@diveIntoElements member.content, memberType

# Replace the original member with out new member
member.content = memberType.content
sectionOrMember.content.push member

else
sectionOrMember.content.push member

# Given a data structure, expand its mixins recusrively
#
# @param name [String] Name of the data structure
# @param dataStructure [Object] Data structure
expandMixin: (name, dataStructure) ->
return if @expanded[name]

# Check for mixin
for section in dataStructure.sections
if section['class'] is 'memberType'

# New content for the section
memberTypeSection =
content: []

@diveIntoElements section.content, memberTypeSection

# Replace section content with the new content
section.content = memberTypeSection.content

# Denote this type as expanded
@expanded[name] = true

init: (dataStructures) ->
@expanded = {}
@dataStructures = dataStructures

# Initiate flags
for name, dataStructure of @dataStructures
@expanded[name] = false

# Actual expansion
for name, dataStructure of @dataStructures
@expandMixin name, dataStructure
14 changes: 14 additions & 0 deletions src/rules/rule.coffee
Original file line number Diff line number Diff line change
@@ -0,0 +1,14 @@
module.exports =

# Copy all member types from one data structure to another
#
# @param dataStructure [Object] The super type data structure
# @param memberTypeSection [Object] Member Type Section to be copied into
copyMembers: (dataStructure, memberTypeSection) ->
return if not dataStructure

for section in dataStructure.sections
if section['class'] is 'memberType'

for member in section.content
memberTypeSection.content.push member
Loading

0 comments on commit c9387b6

Please sign in to comment.