graphql-docs 0.1.0 → 0.1.1

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.
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA1:
3
- metadata.gz: 95ab4bc20c02f63be2acd6f1383fd57184719a7e
4
- data.tar.gz: 79679c4f467ceef22e31677a22b4706011c555c6
3
+ metadata.gz: 540afb95d491f00898705c8bd73fdb8a31d685c9
4
+ data.tar.gz: 23b419944ad81f60fff9264e3691d604ffeda467
5
5
  SHA512:
6
- metadata.gz: d4d9f76f49551625f2509138f44e90bae1e5643ea736b1b949a2efbd66366acb262b8710813a1afe401515c8525b788ca95a77f60163a9ee1c3c0130c148b418
7
- data.tar.gz: 087bfd499ba9196fbd85c6026dd16911b0f270cc81fd162782ad0d1a70822112d55bf745906f03ab21c5d7ea196ddd80fa99c76ee01a776b28819ae0725dc4ec
6
+ metadata.gz: 6ee639a760366f5f240dc117d5770045700e1676eaf9abb06041009513e0a31470a3c6bd77eaf1da4642e542fdc35e2669f9ee22dff23da6ee8c2aa6ee74edf5
7
+ data.tar.gz: 78561641aae9442b65c565b1f198f0dc9da933bfbefc8ad1f6ad50b3d553d4670fcebf3c3d094fb5ecc18779cd2fa7b043a572fe7b1c1d4181302a8fc6e2bddb
data/.gitignore CHANGED
@@ -9,3 +9,5 @@
9
9
  /tmp/
10
10
  /output/
11
11
  /test/graphql-docs/fixtures/output/
12
+ /.sass-cache/
13
+ /sample/
data/README.md CHANGED
@@ -26,12 +26,6 @@ Simple! Call `GraphQLDocs.generate`, taking care to pass in the GraphQL endpoint
26
26
  GraphQLDocs.build(url: "http://graphql.org/swapi-graphql/")
27
27
  ```
28
28
 
29
- If you already have the JSON locally, great! Call the same method with `path` instead:
30
-
31
- ``` ruby
32
- GraphQLDocs.build(path: "location/to/sw-api.json")
33
- ```
34
-
35
29
  If your GraphQL endpoint requires authentication, you can provide a username or password, or an access token:
36
30
 
37
31
  ``` ruby
@@ -50,37 +44,103 @@ options = {
50
44
  GraphQLDocs.build(options)
51
45
  ```
52
46
 
47
+ If you already have the JSON locally, great! Call the same method with `path` instead:
48
+
49
+ ``` ruby
50
+ GraphQLDocs.build(path: 'location/to/sw-api.json')
51
+ ```
52
+
53
53
  ## Breakdown
54
54
 
55
- There are three phases going on in one little `GraphQLDocs.build` call:
55
+ There are several phaseses going on in one little `GraphQLDocs.build` call:
56
56
 
57
57
  * The GraphQL JSON is _fetched_ (if you passed `url`) through `GraphQL::Client` (or simply read if you passed `path`).
58
58
  * `GraphQL::Parser` manipulates that JSON into a slightly saner format.
59
59
  * `GraphQL::Generator` takes that JSON and converts it into HTML.
60
+ * `GraphQL::Parser` technically runs as part of the generation phase. It passes the contents of each page through a Markdown renderer.
60
61
 
61
- If you wanted to, you could break these calls up individually:
62
+ If you wanted to, you could break these calls up individually. For example:
62
63
 
63
64
  ``` ruby
64
- client = GraphQLDocs::Client.new(options)
65
- response = client.fetch
65
+ options = {}
66
+ options[:path] = "#{File.dirname(__FILE__)}/../data/graphql/docs.json"
67
+ my_renderer = MySuperCoolRenderer(options)
68
+ options[:renderer] = my_renderer
66
69
 
67
- # do something
70
+ options = GraphQLDocs::Configuration::GRAPHQLDOCS_DEFAULTS.merge(options)
71
+
72
+ response = File.read(options[:path])
68
73
 
69
74
  parser = GraphQLDocs::Parser.new(response, options)
70
75
  parsed_schema = parser.parse
71
76
 
72
- # do something else
73
- generator = Generator.new(parsed_schema, options)
77
+ generator = GraphQLDocs::Generator.new(parsed_schema, options)
74
78
 
75
79
  generator.generate
76
80
  ```
77
81
 
78
82
  ## Generating output
79
83
 
80
- The HTML generation process uses [html-pipeline](https://github.com/jch/html-pipeline) and ERB to style the output. There are a bunch of default options provided for you, but feel free to override any of these. The *Configuration* section below has more information on what you can change.
84
+ By default, the HTML generation process uses ERB to layout the content. There are a bunch of default options provided for you, but feel free to override any of these. The *Configuration* section below has more information on what you can change.
85
+
86
+ It also uses [html-pipeline](https://github.com/jch/html-pipeline) to perform the Markdown rendering by default. You can override this by providing a custom rendering class. `initialize` takes two arguments, the configuration options and the parsed schema. You must implement at least one method, `render`, which takes the GraphQL type, the name, and the layout contents. For example:
87
+
88
+ ``` ruby
89
+ class CustomRenderer
90
+ def initialize(options, parsed_schema)
91
+ @options = options
92
+ @parsed_schema = parsed_schema
93
+ end
94
+
95
+ def render(type, name, contents)
96
+ contents.sub(/Repository/i, 'Meow Woof!')
97
+ end
98
+ end
99
+
100
+ options[:path] = 'location/to/sw-api.json'
101
+ options[:renderer] = CustomRenderer
81
102
 
82
- ### Herlper methods
103
+ GraphQLDocs.build(options)
104
+ ```
105
+
106
+ ### Helper methods
107
+
108
+ In your ERB layouts, there are several helper methods you can use. The helper methods are:
109
+
110
+ * `slugify(str)` - This slugifies the given string.
111
+ * `include(filename, opts)` - This embeds a template from your `includes` folder, passing along the local options provided.
112
+ * `markdown(string)` - This converts a string from Markdown to HTML.
113
+
114
+ To call these methods, you must use the dot notation, such as `<%= slugify.(text) %>`.
115
+
116
+ ## Configuration
117
+
118
+ The following options are available:
119
+
120
+
121
+ | Option | Description | Default |
122
+ | :----- | :---------- | :------ |
123
+ | `access_token` | Uses this token while making requests through `GraphQLDocs::Client`. | `nil` |
124
+ | `login` | Uses this login while making requests through `GraphQLDocs::Client`. | `nil` |
125
+ | `password` | Uses this password while making requests through `GraphQLDocs::Client`. | `nil` |
126
+ | `path` | `GraphQLDocs::Client` loads a JSON file found at this location, representing the response from an introspection query. | `nil` |
127
+ | `url` | `GraphQLDocs::Client` makes a `POST` request to this URL, passing along the introspection query. | `nil` |
128
+ | `output_dir` | The location of the output HTML. | `./output/` |
129
+ | `delete_output` | Deletes `output_dir` before generating content. | `false` |
130
+ | `pipeline_config` | Defines two sub-keys, `pipeline` and `context`, which are used by `html-pipeline` when rendering your output. | `pipeline` has `ExtendedMarkdownFilter`, `EmojiFilter`, and `TableOfContentsFilter`. `context` has `gfm: false` and `asset_root` set to GitHub's CDN. |
131
+ | `renderer` | The rendering class to use. | `GraphQLDocs::Renderer`
132
+ | `templates` | The templates to use when generating HTML. You may override any of the following keys: `includes`, `objects`, `mutations`, `interfaces`, `enums`, `unions`, `input_objects`, `scalars`. | The default gem ones are found in _layouts/_.
83
133
 
84
134
  ## Development
85
135
 
86
136
  After checking out the repo, run `script/bootstrap` to install dependencies. Then, run `rake test` to run the tests. You can also run `bin/console` for an interactive prompt that will allow you to experiment.
137
+
138
+ ## Sample site
139
+
140
+ Clone this repository and run:
141
+
142
+ ```
143
+ bundle exec rake sample
144
+ ```
145
+
146
+ to see some sample output.
data/Rakefile CHANGED
@@ -21,3 +21,31 @@ task :console do
21
21
  ARGV.clear
22
22
  Pry.start
23
23
  end
24
+
25
+ task :sample do
26
+ require 'graphql-docs'
27
+ require 'sass'
28
+
29
+ options = {}
30
+ options[:output_dir] = 'sample'
31
+ options[:delete_output] = true
32
+ options[:path] = File.join(File.dirname(__FILE__), 'test', 'graphql-docs', 'fixtures', 'gh-api.json')
33
+
34
+ GraphQLDocs.build(options)
35
+
36
+ assets_dir = File.join('sample', 'assets')
37
+ FileUtils.mkdir_p(assets_dir)
38
+
39
+ sass = File.join('sample_assets', 'css', 'screen.scss')
40
+ system `sass --sourcemap=none #{sass}:style.css`
41
+
42
+ FileUtils.mv('style.css', File.join('sample', 'assets/style.css'))
43
+
44
+ starting_dir = 'sample'
45
+ starting_file = File.join(starting_dir, 'object', 'repository', 'index.html')
46
+
47
+ puts 'Navigate to http://localhost:3000 to see the sample docs'
48
+ puts "Launching #{starting_file}"
49
+ system "open #{starting_file}"
50
+ system "ruby -run -e httpd #{starting_dir} -p 3000"
51
+ end
data/graphql-docs.gemspec CHANGED
@@ -20,15 +20,14 @@ Gem::Specification.new do |spec|
20
20
  spec.executables = spec.files.grep(%r{^bin/}) { |f| File.basename(f) }
21
21
  spec.require_paths = ['lib']
22
22
 
23
- spec.add_dependency 'faraday', '~> 0.11'
23
+ spec.add_dependency 'faraday', '< 0.10'#'~> 0.9'
24
24
  spec.add_dependency 'graphql', '~> 1.4'
25
25
 
26
26
  # rendering
27
- spec.add_dependency 'html-pipeline', '2.5.0'
27
+ spec.add_dependency 'html-pipeline', '~> 2.2'
28
28
  spec.add_dependency 'github-markdown', '0.6.9'
29
29
  spec.add_dependency 'extended-markdown-filter', '~> 0.4'
30
30
  spec.add_dependency 'gemoji', '2.1.0'
31
- spec.add_dependency 'page-toc-filter', '~> 0.0.1'
32
31
 
33
32
  spec.add_development_dependency 'awesome_print'
34
33
  spec.add_development_dependency 'bundler', '~> 1.14'
@@ -37,5 +36,6 @@ Gem::Specification.new do |spec|
37
36
  spec.add_development_dependency 'pry', '~> 0.10.0'
38
37
  spec.add_development_dependency 'rake', '~> 10.0'
39
38
  spec.add_development_dependency 'rubocop-github'
39
+ spec.add_development_dependency 'sass', '~> 3.4'
40
40
  spec.add_development_dependency 'webmock', '~> 2.3'
41
41
  end
data/lib/graphql-docs.rb CHANGED
@@ -1,8 +1,9 @@
1
1
  require 'graphql-docs/client'
2
+ require 'graphql-docs/helpers'
3
+ require 'graphql-docs/renderer'
2
4
  require 'graphql-docs/configuration'
3
5
  require 'graphql-docs/generator'
4
6
  require 'graphql-docs/parser'
5
- require 'graphql-docs/renderer'
6
7
  require 'graphql-docs/version'
7
8
 
8
9
  begin
@@ -32,7 +33,7 @@ module GraphQLDocs
32
33
  parser = GraphQLDocs::Parser.new(response, options)
33
34
  parsed_schema = parser.parse
34
35
 
35
- generator = Generator.new(parsed_schema, options)
36
+ generator = GraphQLDocs::Generator.new(parsed_schema, options)
36
37
 
37
38
  generator.generate
38
39
  end
@@ -9,18 +9,22 @@ module GraphQLDocs
9
9
  url: nil,
10
10
 
11
11
  # Generating
12
+ delete_output: false,
12
13
  output_dir: './output/',
13
14
  pipeline_config: {
14
15
  pipeline:
15
16
  %i(ExtendedMarkdownFilter
16
17
  EmojiFilter
17
- PageTocFilter),
18
+ TableOfContentsFilter),
18
19
  context: {
19
20
  gfm: false,
20
21
  asset_root: 'https://a248.e.akamai.net/assets.github.com/images/icons'
21
22
  }
22
23
  },
24
+ renderer: GraphQLDocs::Renderer,
25
+
23
26
  templates: {
27
+ default: "#{File.dirname(__FILE__)}/layouts/default.html",
24
28
  includes: "#{File.dirname(__FILE__)}/layouts/includes",
25
29
  objects: "#{File.dirname(__FILE__)}/layouts/graphql_objects.html",
26
30
  mutations: "#{File.dirname(__FILE__)}/layouts/graphql_mutations.html",
@@ -30,6 +34,6 @@ module GraphQLDocs
30
34
  input_objects: "#{File.dirname(__FILE__)}/layouts/graphql_input_objects.html",
31
35
  scalars: "#{File.dirname(__FILE__)}/layouts/graphql_scalars.html"
32
36
  }
33
- }
37
+ }.freeze
34
38
  end
35
39
  end
@@ -1,15 +1,16 @@
1
1
  require 'erb'
2
- require 'graphql-docs/generator/helpers'
3
2
 
4
3
  module GraphQLDocs
5
4
  class Generator
6
5
  include Helpers
7
6
 
7
+ attr_accessor :parsed_schema
8
+
8
9
  def initialize(parsed_schema, options)
9
10
  @parsed_schema = parsed_schema
10
11
  @options = options
11
12
 
12
- @renderer = Renderer.new(@options)
13
+ @renderer = @options[:renderer].new(@options, @parsed_schema)
13
14
 
14
15
  @graphql_object_template = ERB.new(File.read(@options[:templates][:objects]))
15
16
  @graphql_mutations_template = ERB.new(File.read(@options[:templates][:mutations]))
@@ -21,7 +22,7 @@ module GraphQLDocs
21
22
  end
22
23
 
23
24
  def generate
24
- FileUtils.rm_rf(@options[:output_dir])
25
+ FileUtils.rm_rf(@options[:output_dir]) if @options[:delete_output]
25
26
 
26
27
  create_graphql_object_pages
27
28
  create_graphql_mutation_pages
@@ -45,8 +46,10 @@ module GraphQLDocs
45
46
 
46
47
  def create_graphql_mutation_pages
47
48
  graphql_mutation_types.each do |mutation|
48
- input = graphql_input_object_types.find { |t| t['name'] = mutation['args'].first['type']['ofType']['name'] }
49
- payload = graphql_object_types.find { |t| t['name'] == mutation['type']['name'] }
49
+ input_name = mutation['args'].first['type']['ofType']['name']
50
+ return_name = mutation['type']['name']
51
+ input = graphql_input_object_types.find { |t| t['name'] == input_name }
52
+ payload = graphql_object_types.find { |t| t['name'] == return_name }
50
53
 
51
54
  opts = { type: mutation, input_fields: input, return_fields: payload }.merge(helper_methods)
52
55
 
@@ -102,39 +105,11 @@ module GraphQLDocs
102
105
 
103
106
  private
104
107
 
105
- def graphql_mutation_types
106
- graphql_object_types.find { |t| t['name'] == 'Mutation' }['fields']
107
- end
108
-
109
- def graphql_object_types
110
- @parsed_schema['object_types']
111
- end
112
-
113
- def graphql_interface_types
114
- @parsed_schema['interface_types']
115
- end
116
-
117
- def graphql_enum_types
118
- @parsed_schema['enum_types']
119
- end
120
-
121
- def graphql_union_types
122
- @parsed_schema['union_types']
123
- end
124
-
125
- def graphql_input_object_types
126
- @parsed_schema['input_object_types']
127
- end
128
-
129
- def graphql_scalar_types
130
- @parsed_schema['scalar_types']
131
- end
132
-
133
108
  def write_file(type, name, contents)
134
109
  path = File.join(@options[:output_dir], type, name.downcase)
135
110
  FileUtils.mkdir_p(path)
136
- contents = @renderer.render(contents)
137
- File.write(File.join(path, 'index.html'), contents)
111
+ contents = @renderer.render(type, name, contents)
112
+ File.write(File.join(path, 'index.html'), contents) unless contents.nil?
138
113
  end
139
114
  end
140
115
  end
@@ -0,0 +1,74 @@
1
+ module GraphQLDocs
2
+ module Helpers
3
+ SLUGIFY_PRETTY_REGEXP = Regexp.new("[^[:alnum:]._~!$&'()+,;=@]+").freeze
4
+
5
+ attr_accessor :templates
6
+
7
+ def slugify(str)
8
+ slug = str.gsub(SLUGIFY_PRETTY_REGEXP, '-')
9
+ slug.gsub!(%r!^\-|\-$!i, '')
10
+ slug.downcase
11
+ end
12
+
13
+ def include(filename, opts = {})
14
+ template = fetch_include(filename)
15
+ template.result(OpenStruct.new(opts.merge(helper_methods)).instance_eval { binding })
16
+ end
17
+
18
+ def markdown(string)
19
+ GitHub::Markdown.render(string || 'n/a')
20
+ end
21
+
22
+ def graphql_mutation_types
23
+ @parsed_schema['mutation_types']
24
+ end
25
+
26
+ def graphql_object_types
27
+ @parsed_schema['object_types']
28
+ end
29
+
30
+ def graphql_interface_types
31
+ @parsed_schema['interface_types']
32
+ end
33
+
34
+ def graphql_enum_types
35
+ @parsed_schema['enum_types']
36
+ end
37
+
38
+ def graphql_union_types
39
+ @parsed_schema['union_types']
40
+ end
41
+
42
+ def graphql_input_object_types
43
+ @parsed_schema['input_object_types']
44
+ end
45
+
46
+ def graphql_scalar_types
47
+ @parsed_schema['scalar_types']
48
+ end
49
+
50
+ private
51
+
52
+ def fetch_include(filename)
53
+ @templates ||= {}
54
+
55
+ return @templates[filename] unless @templates[filename].nil?
56
+
57
+ @templates[filename] = ERB.new(File.read(File.join(@options[:templates][:includes], filename)))
58
+ @templates[filename]
59
+ end
60
+
61
+ def helper_methods
62
+ return @helper_methods if defined?(@helper_methods)
63
+
64
+ @helper_methods = {}
65
+
66
+ Helpers.instance_methods.each do |name|
67
+ next if name == :helper_methods
68
+ @helper_methods[name] = method(name)
69
+ end
70
+
71
+ @helper_methods
72
+ end
73
+ end
74
+ end
@@ -0,0 +1,39 @@
1
+ <!DOCTYPE html>
2
+ <html lang="en">
3
+ <head>
4
+ <meta charset="utf-8">
5
+ <meta name="description" content="<%= name %> GraphQL documentation">
6
+ <meta name="viewport" content="width=device-width, initial-scale=1, maximum-scale=1, user-scalable=no">
7
+ <title><%= name %></title>
8
+ <link href='https://fonts.googleapis.com/css?family=Source+Sans+Pro:400,300,200,300italic,400italic' rel='stylesheet' type='text/css'>
9
+ <link rel="stylesheet" href="../../assets/style.css">
10
+ <script src="https://code.jquery.com/jquery-3.0.0.min.js" integrity="sha256-JmvOoLtYsmqlsWxa7mDSLMwa6dZ9rrIdtrrVYRnDRH0=" crossorigin="anonymous"></script>
11
+ </head>
12
+ <body>
13
+
14
+ <div id="wrap">
15
+ <div id="header">
16
+ </div>
17
+ <div id="sidebar">
18
+ <%= include.('sidebar.html') %>
19
+ </div>
20
+ <div id="content">
21
+
22
+ <h1><%= name %></h1>
23
+
24
+ <%= contents %>
25
+
26
+ </div>
27
+
28
+ <!-- mobile only -->
29
+ <div id="mobile-header">
30
+ <a class="menu-button"></a>
31
+ <a class="logo" href="/">
32
+
33
+ </a>
34
+ </div>
35
+ <div id="mobile-shade"></div>
36
+
37
+ </div>
38
+ </body>
39
+ </html>