calamum 1.0.2 → 1.1.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/bin/calamum CHANGED
@@ -1,8 +1,8 @@
1
1
  #!/usr/bin/env ruby
2
2
  #
3
- # Author:: Mehrez Alachheb (<lachheb.mehrez@gmail.com>)
4
-
5
- $: << File.join(File.dirname(__FILE__), "..", "lib")
3
+ # Calamum is a simple REST API documentation generator.
4
+ # This is a fork, that allows you to work with JSON format.
5
+ $: << File.join(File.dirname(__FILE__), '..', 'lib')
6
6
 
7
7
  require 'rubygems'
8
8
  require 'calamum/runner'
data/lib/calamum.rb CHANGED
@@ -1,9 +1,15 @@
1
+ require 'erb'
2
+ require 'yajl'
1
3
  require 'yaml'
4
+ require 'fileutils'
2
5
  require 'pry'
3
- require 'erb'
4
- require 'active_model'
5
- require 'calamum/calamum_helper'
6
- require "calamum/version"
7
- require "calamum/config"
8
- require "calamum/definition_parser"
9
- require "calamum/request"
6
+
7
+ module Calamum
8
+ require 'calamum/config'
9
+ require 'calamum/helpers'
10
+ require 'calamum/resource'
11
+ require 'calamum/doc_parser'
12
+ require 'calamum/doc_generator'
13
+ require 'calamum/version'
14
+ VALID_TEMPLATES = %{twitter bootstrap}
15
+ end
@@ -1,13 +1,21 @@
1
1
  require 'calamum'
2
2
  require 'mixlib/config'
3
3
 
4
- module Calamum
5
- class Config
6
- extend Mixlib::Config
4
+ # Provides a class-based configuration object.
5
+ # See https://github.com/opscode/mixlib-config
6
+ class Calamum::Config
7
+ extend Mixlib::Config
7
8
 
8
- def self.inspect
9
- configuration.inspect
10
- end
9
+ def self.inspect
10
+ configuration.inspect
11
+ end
12
+
13
+ def self.apply(config)
14
+ merge!(config)
15
+
16
+ self.doc_path = File.join(config[:path], 'docs')
17
+ self.tpl_path = File.join(File.dirname(__FILE__), 'templates', config[:template])
18
+ raise "Unknown template #{config[:template]}" unless Calamum::VALID_TEMPLATES.include?(config[:template])
19
+ end
11
20
 
12
- end
13
21
  end
@@ -1,62 +1,33 @@
1
-
2
1
  class Calamum::DocGenerator
3
- include ERB::Util
4
- attr_accessor :resources, :template, :name, :url, :description
2
+ attr_accessor :template
5
3
 
6
- def initialize(template, resources, name, url, descr)
7
- @template = template
8
- @resources = resources
9
- @name = name
10
- @url = url
11
- @description = descr
12
- end
4
+ def initialize(tpl_name)
5
+ tpl_path = Calamum::Config[:tpl_path]
6
+ filename = "#{tpl_path}/#{tpl_name}.html.erb"
13
7
 
14
- def render()
15
- ERB.new(@template).result(binding)
8
+ @template = ERB.new(File.read(filename))
16
9
  end
17
10
 
18
- def self.load_template
19
- File.read(File.join(File.dirname(__FILE__), "templates", "bootstrap", "index.html.erb"))
20
- end
21
-
22
- def save_result(file)
23
- initialize_doc_dir
24
- File.open(file, "w+") do |f|
25
- f.write(render)
26
- end
27
- end
11
+ def self.init_base_dir
12
+ tpl_path = Calamum::Config[:tpl_path]
13
+ doc_path = Calamum::Config[:doc_path]
14
+ FileUtils.rm_r(doc_path, :force => true)
15
+ Dir.mkdir(doc_path)
28
16
 
29
- def initialize_doc_dir
30
- create_doc_dir
31
- Dir.mkdir File.join(Calamum::Config[:path], 'doc', 'assets')
32
- copy_assets
17
+ # copy assets from template directory
18
+ source = File.join(tpl_path, 'assets')
19
+ target = File.join(doc_path, 'assets')
20
+ FileUtils.copy_entry(source, target)
33
21
  end
34
22
 
35
- def copy_assets
36
- src = File.join(File.dirname(__FILE__), "templates", Calamum::Config[:template], "assets")
37
- dst = File.join(Calamum::Config[:path], 'doc', 'assets')
38
- begin
39
- FileUtils.copy_entry(src, dst)
40
- rescue => e
41
- puts_error e.message
23
+ def save_template(filename, values)
24
+ values.each do |k, v|
25
+ instance_variable_set("@#{k}", v)
42
26
  end
43
- end
44
27
 
45
- def create_doc_dir
46
- doc_dir = File.join(Calamum::Config[:path], 'doc')
47
- if File.exist?(doc_dir)
48
- while true
49
- print "The filename directoy #{doc_dir} already exists. Do you want to overwrite it? [Y/N]: "
50
- case $stdin.gets.chomp!.downcase
51
- when 'y'
52
- FileUtils.rm_r(doc_dir, :force => true)
53
- break
54
- when 'n'
55
- exit(1)
56
- end
57
- end
58
- end
59
- Dir.mkdir doc_dir
28
+ html_data = @template.result(binding)
29
+ filename = File.join(Calamum::Config[:doc_path], filename)
30
+ File.open(filename, 'w+') { |file| file.write(html_data) }
60
31
  end
61
32
 
62
33
  end
@@ -0,0 +1,49 @@
1
+ class Calamum::DocParser
2
+ attr_accessor :resources, :definition
3
+
4
+ def initialize(definition)
5
+ @resources = Hash.new
6
+ @definition = definition
7
+ end
8
+
9
+ def get_url
10
+ @definition['url']
11
+ end
12
+
13
+ def get_name
14
+ @definition['name']
15
+ end
16
+
17
+ def get_version
18
+ @definition['version']
19
+ end
20
+
21
+ def get_description
22
+ @definition['description']
23
+ end
24
+
25
+ def get_resources
26
+ Calamum::Config[:sort]? @definition['resources'].sort : @definition['resources']
27
+ end
28
+
29
+ def load_resources
30
+ get_resources.each do |name, methods|
31
+ list = []
32
+ methods.each do |resource|
33
+ if validate_resource?(resource)
34
+ list << Calamum::Resource.new(resource)
35
+ puts_info "#{resource['action']}: #{resource['uri']}"
36
+ else
37
+ puts_warning "Resource #{resource['action']}: #{resource['uri']} has incorrect definition"
38
+ end
39
+ end
40
+ @resources[name] = list if list.any?
41
+ end
42
+ @resources
43
+ end
44
+
45
+ def validate_resource?(resource)
46
+ resource['uri'] && resource['action'] && resource['description'] && %{GET POST PUT DELETE}.include?(resource['action'].upcase)
47
+ end
48
+
49
+ end
@@ -0,0 +1,31 @@
1
+ # Returns a formatted JSON string.
2
+ #
3
+ # @param json [String] JSON string to format
4
+ # @param indent [String] indent that used in formatting
5
+ # @return [String] prettier form of the given JSON string
6
+ def pj(json, indent = '&nbsp;')
7
+ require 'json'
8
+ JSON.pretty_generate(json, :indent => indent * 4).gsub!(/\n/, '<br/>')
9
+ end
10
+
11
+ # Output info message to console.
12
+ #
13
+ # @param msg [String] message to output
14
+ def puts_info(msg)
15
+ $stdout.puts "\e[32m[INFO] #{msg}\e[0m" if Calamum::Config[:debug]
16
+ end
17
+
18
+ # Output warning message to console.
19
+ #
20
+ # @param msg [String] message to output
21
+ def puts_warning(msg)
22
+ $stdout.puts "\e[33m[WARNING] #{msg}\e[0m" if Calamum::Config[:debug]
23
+ end
24
+
25
+ # Output error message to console.
26
+ #
27
+ # @param msg [String] message to output
28
+ def puts_error(msg)
29
+ $stderr.puts "\e[31m[ERROR] #{msg}\e[0m" if Calamum::Config[:debug]
30
+ exit(1)
31
+ end
@@ -0,0 +1,47 @@
1
+ # This class represents a single resource.
2
+ # It contains attributes from parsed definition.
3
+ # So anywhere in view template we can use this object.
4
+ class Calamum::Resource
5
+ attr_accessor :uri, :action, :headers,
6
+ :auth, :params, :errors, :description, :response
7
+
8
+ # Initialize object from attributes.
9
+ #
10
+ # @param attrs [Hash] attributes to set
11
+ def initialize(attrs)
12
+ @uri = attrs['uri']
13
+ @action = attrs['action'].upcase
14
+ @headers = attrs['headers'] || {}
15
+ @auth = !!attrs['authentication']
16
+ @params = attrs['params'] || {}
17
+ @errors = attrs['errors'] || {}
18
+ @description = attrs['description']
19
+ @response = attrs['response']
20
+ end
21
+
22
+ # @override
23
+ # Returns a string representing a label css class.
24
+ #
25
+ # @return [String] css class
26
+ def action_label
27
+ case @action
28
+ when 'GET'
29
+ 'label-info'
30
+ when 'POST'
31
+ 'label-success'
32
+ when 'PUT'
33
+ 'label-warning'
34
+ when 'DELETE'
35
+ 'label-important'
36
+ end
37
+ end
38
+
39
+ # @override
40
+ # Returns a string representing a resource.
41
+ #
42
+ # @return [String] resource in a form (action: uri)
43
+ def to_s
44
+ "#{action}: #{uri}"
45
+ end
46
+
47
+ end
@@ -3,89 +3,114 @@ require 'calamum/config'
3
3
  require 'calamum/doc_generator'
4
4
  require 'mixlib/cli'
5
5
 
6
+ # Provides a class-based command line opts.
7
+ # See https://github.com/opscode/mixlib-cli
6
8
  class Calamum::Runner
7
9
  include Mixlib::CLI
8
- CMD_NAME = "calamum"
9
-
10
- def initialize
11
- super
12
- end
13
10
 
14
11
  option :help,
15
- :short => "-h",
16
- :long => "--help",
17
- :description => "Show this message",
12
+ :short => '-h',
13
+ :long => '--help',
14
+ :description => 'Show this help',
18
15
  :on => :tail,
19
16
  :boolean => true,
20
17
  :show_options => true,
21
18
  :exit => 0
22
19
 
23
- option :version,
24
- :short => "-v",
25
- :long => "--version",
26
- :description => "Show #{CMD_NAME} version",
27
- :boolean => true,
28
- :proc => lambda {|v| puts "Calamum: #{Calamum::VERSION}"},
29
- :exit => 0
30
-
31
- option :debug,
32
- :short => "-d",
33
- :long => "--debug",
34
- :description => "Show actions to do (default)",
35
- :boolean => true,
36
- :default => true,
37
- :proc => lambda { |p| true }
38
-
39
- option :definition,
40
- :short => "-f DEFINITION",
41
- :long => "--file DEFINITION",
42
- :description => "Definition YAML file",
43
- :required => true
20
+ option :source,
21
+ :short => '-f DEFINITION',
22
+ :long => '--file DEFINITION',
23
+ :description => 'Path to the file with JSON API definition',
24
+ :required => true
44
25
 
45
26
  option :template,
46
- :short => "-t TEMPLATE",
47
- :long => "--template TEMPLATE",
48
- :description => "Documentation HTML template",
49
- :default => "bootstrap"
27
+ :short => '-t TEMPLATE',
28
+ :long => '--template TEMPLATE',
29
+ :description => 'Name of HTML template [twitter, bootstrap](twitter by default)',
30
+ :default => 'twitter'
50
31
 
51
32
  option :path,
52
- :short => "-p PATH",
53
- :long => "--path PATH",
54
- :description => "The distination path for the generated doc directory",
55
- :default => ENV['HOME']
33
+ :short => '-p PATH',
34
+ :long => '--path PATH',
35
+ :description => 'Path to the directory where docs will be generated',
36
+ :default => ENV['HOME']
56
37
 
38
+ option :debug,
39
+ :short => '-d',
40
+ :long => '--debug',
41
+ :description => 'Show actions to do (true by default)',
42
+ :default => true,
43
+ :boolean => true,
44
+ :proc => lambda { |x| true }
45
+
46
+ option :version,
47
+ :short => '-v',
48
+ :long => '--version',
49
+ :description => 'Show version number',
50
+ :proc => lambda { |x| puts Calamum::VERSION },
51
+ :exit => 0
52
+
57
53
  option :sort,
58
54
  :short => "-s",
59
55
  :long => "--sort",
60
56
  :description => "Sort the resources alphabetically",
61
57
  :boolean => true,
62
58
  :default => false
63
-
59
+
60
+ # Parses command line options and generates API documentation.
61
+ # See samples for details how to define meta-data for your API.
64
62
  def run
65
- load_options
66
- Calamum::Config.merge!(config)
67
- api_definition = load_definition_file
68
- @definition = Calamum::DefinitionParser.new(api_definition)
69
- @definition.load_requests
70
- template = Calamum::DocGenerator.load_template
71
- html_output = Calamum::DocGenerator.new(template,
72
- @definition.resources, @definition.get_name, @definition.get_url, @definition.get_description)
73
- html_output.save_result(File.join(Calamum::Config[:path], 'doc', 'index.html'))
63
+ parse_options
64
+ Calamum::Config.apply(config)
65
+ @definition = Calamum::DocParser.new(load_source)
66
+ @definition.load_resources
67
+ Calamum::DocGenerator.init_base_dir
68
+ process_index
69
+ process_pages if config[:template] == 'twitter'
70
+ rescue => ex
71
+ puts_error ex.message
74
72
  end
75
73
 
76
- def load_definition_file
77
- begin
78
- YAML.load(File.open(config[:definition]))
79
- rescue => e
80
- puts_error e.message
74
+ # Open and load the source file of api definition
75
+ def load_source
76
+ case File.extname(config[:source])
77
+ when '.json'
78
+ Yajl.load(File.open(config[:source]))
79
+ when '.yml'
80
+ YAML.load(File.open(config[:source]))
81
+ else
82
+ raise 'unknown source file extension'
81
83
  end
82
84
  end
85
+
86
+ # Bind values to index page and save it.
87
+ def process_index
88
+ bindings = {
89
+ :url => @definition.get_url,
90
+ :name => @definition.get_name,
91
+ :resources => @definition.resources,
92
+ :description => @definition.get_description,
93
+ :version => @definition.get_version
94
+ }
95
+
96
+ page = Calamum::DocGenerator.new(:index)
97
+ page.save_template('index.html', bindings)
98
+ end
99
+
100
+ # Bind values to view pages and save them.
101
+ def process_pages
102
+ bindings = {
103
+ :name => @definition.get_name,
104
+ :version => @definition.get_version
105
+ }
83
106
 
84
- def load_options
85
- begin
86
- parse_options
87
- rescue => e
88
- puts_error e.message
107
+ page = Calamum::DocGenerator.new(:view)
108
+ @definition.resources.each do |methods|
109
+ methods[1].each do |resource|
110
+ bindings.merge!(:resource => resource)
111
+ filename = "#{resource.object_id}.html"
112
+ page.save_template(filename, bindings)
113
+ end
89
114
  end
90
115
  end
91
116