praxis-docs-search 0.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.
@@ -0,0 +1,7 @@
1
+ ---
2
+ SHA1:
3
+ metadata.gz: 3b87c5a598e8934d2adc02815102130441fcca42
4
+ data.tar.gz: 883f4fef35d32169517eb3e5a1c8643681a3ed07
5
+ SHA512:
6
+ metadata.gz: 5d7db10a0cf2fbc703864f59d91434c5d04f1677c1469dffe3555a3de302b65dedad7e316c7a300f72170bf4c57956484f2d9afed1802ac4c91927248801d834
7
+ data.tar.gz: a3c34a641de45578a9e8f1d327105bd84ea8b9ffe62f3dfd46d747b1b7860750c8373d1b528b41c8c4f0127e5a65533da8a9b026d35c47ba37e10ee9dc26d9ff
File without changes
data/Gemfile ADDED
@@ -0,0 +1,3 @@
1
+ source 'https://rubygems.org'
2
+
3
+ gemspec
Binary file
@@ -0,0 +1,5 @@
1
+ {
2
+ "dependencies": {
3
+ "lunr.js": "^0.6.0"
4
+ }
5
+ }
@@ -0,0 +1,23 @@
1
+ .praxis-docs-search {
2
+ input {
3
+ width: 280px;
4
+ }
5
+ .popover-content {
6
+ padding: 0;
7
+ .list-group-item {
8
+ width: 270px;
9
+ margin-right: 20px;
10
+ border-left: none;
11
+ border-right: none;
12
+ &:first-child {
13
+ border-top: none;
14
+ }
15
+ .result-type {
16
+ color: $text-muted;
17
+ position: absolute;
18
+ right: 12px;
19
+ bottom: 11px;
20
+ }
21
+ }
22
+ }
23
+ }
@@ -0,0 +1,218 @@
1
+ app.value('pathIndex', {});
2
+ app.provider('praxisDocsSearch', function() {
3
+ function localSearchFactory(Documentation, $timeout, $q, $state, pathIndex) {
4
+ 'ngInject';
5
+ console.log('Using Local Search Index');
6
+
7
+ // Create the lunr index
8
+ var index = lunr(function() {
9
+ this.ref('path');
10
+ this.field('titleWords', {boost: 50});
11
+ this.field('members', {boost: 40});
12
+ this.field('keywords', {boost: 20});
13
+ this.field('parent', {boost: 30});
14
+ });
15
+
16
+ // Delay building the index by loading the data asynchronously
17
+ var indexReadyPromise = Documentation.versions().then(function(versions) {
18
+ // Delay building the index for 500ms to allow the page to render
19
+ return $timeout(function() {
20
+ $q.all(versions.map(function (version) {
21
+ return Documentation.items(version).then(function(items) {
22
+ function extractMembers(thing) {
23
+ return _.map(_.get(thing, 'type.attributes', {}), function(v, k) {
24
+ var res = [k];
25
+ if (v.description) {
26
+ res.push(v.description);
27
+ }
28
+ if (_.get(v, 'type.attributes')) {
29
+ res = res.concat(extractMembers(v));
30
+ }
31
+ return res.join(' ');
32
+ }).join(' ');
33
+ }
34
+ function extractActionMembers(action) {
35
+ var headers = _.keys(_.get(action, 'headers.type.attributes', {})).join(' '),
36
+ params = extractMembers(_.get(action, 'params', {})),
37
+ payload = extractMembers(_.get(action, 'payload', {})),
38
+ responses = _.map(_.get(action, 'responses'), function(v, k) { return k + ' ' + v.description; }).join(' '),
39
+ urls = _.map(_.get(action, 'urls'), 'path').join(' ');
40
+ return [headers, params, payload, responses, urls].join(' ');
41
+ }
42
+ function extractTypeMembers(type) {
43
+ return extractMembers({type: type});
44
+ }
45
+ _.each(items.resources, function(resource, id) {
46
+ var href = $state.href('root.controller', {version: version, controller: id});
47
+ index.add({
48
+ path: href,
49
+ titleWords: resource.display_name,
50
+ members: _.map(resource.actions, 'name').join(' ') + ' ' + _.get(resource.media_type, 'name') + ' ' + _.get(resource, 'traits', []).join(' '),
51
+ keywords: resource.description + ' ' + version,
52
+ parent: resource.parent ? items.resources[resource.parent].display_name : ''
53
+ });
54
+ pathIndex[href] = {
55
+ type: 'resource',
56
+ name: resource.display_name,
57
+ id: id,
58
+ version: version
59
+ };
60
+
61
+ _.each(resource.actions, function(action) {
62
+ var href = $state.href('root.action', {version: version, controller: id, action: action.name});
63
+ index.add({
64
+ path: $state.href('root.action', {version: version, controller: id, action: action.name}),
65
+ titleWords: action.name,
66
+ members: extractActionMembers(action),
67
+ keywords: action.description + ' ' + version,
68
+ parent: resource.display_name
69
+ });
70
+
71
+ pathIndex[href] = {
72
+ type: 'action',
73
+ name: resource.display_name + ' » ' + action.name,
74
+ id: action.name,
75
+ version: version,
76
+ resource: id
77
+ };
78
+ });
79
+ });
80
+ _.each(items.schemas, function(type) {
81
+ var href = $state.href('root.type', {version: version, type: type.id});
82
+ index.add({
83
+ path: href,
84
+ titleWords: type.display_name,
85
+ members: extractTypeMembers(type),
86
+ keywords: type.description + ' ' + version
87
+ });
88
+ pathIndex[href] = {
89
+ type: 'schema',
90
+ name: type.display_name,
91
+ id: type.id,
92
+ version: version
93
+ };
94
+ });
95
+
96
+ _.each(items.traits, function(trait, id) {
97
+ var href = $state.href('root.trait', {version: version, trait: id});
98
+ index.add({
99
+ path: href,
100
+ titleWords: id,
101
+ members: extractTypeMembers(trait),
102
+ keywords: trait.description + ' ' + version
103
+ });
104
+ pathIndex[href] = {
105
+ type: 'trait',
106
+ name: id,
107
+ id: id,
108
+ version: version
109
+ };
110
+ });
111
+ });
112
+ }));
113
+ }, 500);
114
+ });
115
+
116
+ // The actual service is a function that takes a query string and
117
+ // returns a promise to the search results
118
+ // (In this case we just resolve the promise immediately as it is not
119
+ // inherently an async process)
120
+ return function(q) {
121
+ return indexReadyPromise.then(function() {
122
+ return index.search(q);
123
+ });
124
+ };
125
+ }
126
+
127
+ return {
128
+ $get:localSearchFactory //window.Worker ? webWorkerSearchFactory : localSearchFactory
129
+ };
130
+ });
131
+
132
+ app.controller('DocsSearchCtrl', function($scope, $location, praxisDocsSearch, pathIndex, $timeout) {
133
+
134
+ function clearResults() {
135
+ $scope.results = [];
136
+ $scope.showResults = false;
137
+ $scope.colClassName = null;
138
+ $scope.hasResults = false;
139
+ }
140
+
141
+ clearResults();
142
+ $scope.focus = false;
143
+
144
+ $scope.search = function(q) {
145
+ var MIN_SEARCH_LENGTH = 2;
146
+ if(q.length >= MIN_SEARCH_LENGTH) {
147
+ praxisDocsSearch(q).then(function(hits) {
148
+ $scope.hasResults = hits.length > 0;
149
+ $scope.results = _.map(_.take(hits, 10), function(hit) {
150
+ var result = pathIndex[hit.ref];
151
+ result.path = hit.ref;
152
+ return result;
153
+ });
154
+ });
155
+ } else {
156
+ clearResults();
157
+ }
158
+ if(!$scope.$$phase) $scope.$apply();
159
+ };
160
+
161
+ $scope.submit = function() {
162
+ var result;
163
+ for(var i in $scope.results) {
164
+ result = $scope.results[i];
165
+ if(result) {
166
+ break;
167
+ }
168
+ }
169
+ if(result) {
170
+ $location.path(result.path);
171
+ $scope.hideResults();
172
+ }
173
+ };
174
+
175
+ $scope.hideResults = function() {
176
+ clearResults();
177
+ $scope.q = '';
178
+ };
179
+
180
+ $scope.goToResult = function(result) {
181
+ var str = $scope.q;
182
+ $scope.hideResults();
183
+ $timeout(function() {
184
+ if (window.find) { // Firefox, Google Chrome, Safari
185
+ // if some content is selected, the start position of the search
186
+ // will be the end position of the selection
187
+ window.find(str, false, false, true);
188
+ } else {
189
+ if (document.selection && document.selection.createRange) { // Internet Explorer, Opera before version 10.5
190
+ var textRange = document.selection.createRange ();
191
+ if (textRange.findText) { // Internet Explorer
192
+ // if some content is selected, the start position of the search
193
+ // will be the position after the start position of the selection
194
+ if (textRange.text.length > 0) {
195
+ textRange.collapse(true);
196
+ textRange.mov("character", 1);
197
+ }
198
+ if (textRange.findText(str)) {
199
+ textRange.select();
200
+ }
201
+ }
202
+ }
203
+ }
204
+ }, 200);
205
+ };
206
+
207
+ $scope.$watch('focus && hasResults', function(val) {
208
+ $scope.showResults = val;
209
+ });
210
+ });
211
+
212
+ app.directive('searchForm', function() {
213
+ return {
214
+ restrict: 'E',
215
+ controller: 'DocsSearchCtrl',
216
+ templateUrl: 'views/search_form.html'
217
+ };
218
+ });
@@ -0,0 +1,10 @@
1
+ <div class="header">
2
+ <div class="navbar navbar-default navbar-fixed-top" role="navigation">
3
+ <div class="container">
4
+ <div class="navbar-header">
5
+ <a class="navbar-brand" href="#/">{{:: title}}</a>
6
+ </div>
7
+ <search-form class="navbar-form navbar-right"></search-form>
8
+ </div>
9
+ </div>
10
+ </div>
@@ -0,0 +1,8 @@
1
+ <form ng-controller="DocsSearchCtrl" class="praxis-docs-search" role="search" ng-class="{focus:focus}" ng-submit="submit()">
2
+ <div class="form-group">
3
+ <input type="search" class="form-control" placeholder="Search" ng-focus="focus=true"
4
+ ng-blur="focus=false"
5
+ ng-change="search(q)"
6
+ ng-model="q" autocomplete="off" popover-template="'views/search_results.html'" popover-is-open="showResults" popover-placement="bottom" popover-trigger="none" />
7
+ </div>
8
+ </form>
@@ -0,0 +1,5 @@
1
+ <div class="list-group">
2
+ <a ng-repeat="result in results" href="{{result.path}}" class="list-group-item" ng-click="goToResult(result)">
3
+ {{result.name}} <small class="result-type">{{result.type}}</small>
4
+ </a>
5
+ </div>
@@ -0,0 +1,12 @@
1
+ module Praxis
2
+ class DocsSearch < ::Praxis::Plugin
3
+ include Singleton
4
+ def setup!
5
+ register_doc_browser_plugin File.join(File.dirname(__FILE__), 'api_browser')
6
+ end
7
+
8
+ def config_key
9
+ :docs_search
10
+ end
11
+ end
12
+ end
@@ -0,0 +1,3 @@
1
+ module PraxisDocsSearch
2
+ VERSION = "0.1.0"
3
+ end
@@ -0,0 +1,26 @@
1
+ lib = File.expand_path('../lib', __FILE__)
2
+ $LOAD_PATH.unshift(lib) unless $LOAD_PATH.include?(lib)
3
+
4
+ require 'praxis-docs-search/version'
5
+
6
+ Gem::Specification.new do |spec|
7
+ spec.name = "praxis-docs-search"
8
+ spec.version = PraxisDocsSearch::VERSION
9
+ spec.authors = ["Jakub Hampl"]
10
+ spec.summary = %q{Doc Browser Search plugin for Praxis.}
11
+ spec.email = ["jakub.hampl@rightscale.com"]
12
+
13
+ spec.homepage = "https://github.com/rightscale/praxis-docs-search"
14
+ spec.license = "MIT"
15
+ spec.required_ruby_version = ">=2.1"
16
+
17
+ spec.require_paths = ["lib"]
18
+ spec.files = `git ls-files -z`.split("\x0")
19
+ spec.test_files = spec.files.grep(%r{^(test|spec|features)/})
20
+
21
+ spec.add_runtime_dependency 'praxis', [">= 0.18"]
22
+
23
+ spec.add_development_dependency "bundler", "~> 1.6"
24
+ spec.add_development_dependency "rake", "~> 0"
25
+
26
+ end
@@ -0,0 +1,36 @@
1
+ # Praxis Docs Search
2
+
3
+ This plugin for Praxis adds the capability to search the doc browser.
4
+
5
+ ![Demo](demo.gif)
6
+
7
+ ## Usage
8
+
9
+ Add this to your gemfile:
10
+
11
+ ```ruby
12
+ gem 'praxis-docs-search'
13
+ ```
14
+
15
+ In your `config.ru` add it to the Praxis Bootloader:
16
+
17
+ ```ruby
18
+ application.bootloader.use Praxis::DocsSearch
19
+ ```
20
+
21
+ Finally if you would like to use the default styling, add this to your `docs/styles.scss`:
22
+
23
+ ```scss
24
+ @import "praxis-docs-search.scss";
25
+ ```
26
+
27
+ If you are using your own navbar, you can include the search form anywhere by using
28
+ the `<search-form></search-form>` element in a custom template.
29
+
30
+ ## Authors
31
+
32
+ Jakub Hampl @gampleman
33
+
34
+ MIT LICENSE
35
+
36
+ (c) RightScale, Inc. 2015
metadata ADDED
@@ -0,0 +1,100 @@
1
+ --- !ruby/object:Gem::Specification
2
+ name: praxis-docs-search
3
+ version: !ruby/object:Gem::Version
4
+ version: 0.1.0
5
+ platform: ruby
6
+ authors:
7
+ - Jakub Hampl
8
+ autorequire:
9
+ bindir: bin
10
+ cert_chain: []
11
+ date: 2016-01-18 00:00:00.000000000 Z
12
+ dependencies:
13
+ - !ruby/object:Gem::Dependency
14
+ name: praxis
15
+ requirement: !ruby/object:Gem::Requirement
16
+ requirements:
17
+ - - ">="
18
+ - !ruby/object:Gem::Version
19
+ version: '0.18'
20
+ type: :runtime
21
+ prerelease: false
22
+ version_requirements: !ruby/object:Gem::Requirement
23
+ requirements:
24
+ - - ">="
25
+ - !ruby/object:Gem::Version
26
+ version: '0.18'
27
+ - !ruby/object:Gem::Dependency
28
+ name: bundler
29
+ requirement: !ruby/object:Gem::Requirement
30
+ requirements:
31
+ - - "~>"
32
+ - !ruby/object:Gem::Version
33
+ version: '1.6'
34
+ type: :development
35
+ prerelease: false
36
+ version_requirements: !ruby/object:Gem::Requirement
37
+ requirements:
38
+ - - "~>"
39
+ - !ruby/object:Gem::Version
40
+ version: '1.6'
41
+ - !ruby/object:Gem::Dependency
42
+ name: rake
43
+ requirement: !ruby/object:Gem::Requirement
44
+ requirements:
45
+ - - "~>"
46
+ - !ruby/object:Gem::Version
47
+ version: '0'
48
+ type: :development
49
+ prerelease: false
50
+ version_requirements: !ruby/object:Gem::Requirement
51
+ requirements:
52
+ - - "~>"
53
+ - !ruby/object:Gem::Version
54
+ version: '0'
55
+ description:
56
+ email:
57
+ - jakub.hampl@rightscale.com
58
+ executables: []
59
+ extensions: []
60
+ extra_rdoc_files: []
61
+ files:
62
+ - ".gitignore"
63
+ - Gemfile
64
+ - demo.gif
65
+ - lib/api_browser/bower.json
66
+ - lib/api_browser/praxis-docs-search.scss
67
+ - lib/api_browser/search.js
68
+ - lib/api_browser/views/navbar.html
69
+ - lib/api_browser/views/search_form.html
70
+ - lib/api_browser/views/search_results.html
71
+ - lib/praxis-docs-search.rb
72
+ - lib/praxis-docs-search/version.rb
73
+ - praxis-docs-search.gemspec
74
+ - readme.md
75
+ homepage: https://github.com/rightscale/praxis-docs-search
76
+ licenses:
77
+ - MIT
78
+ metadata: {}
79
+ post_install_message:
80
+ rdoc_options: []
81
+ require_paths:
82
+ - lib
83
+ required_ruby_version: !ruby/object:Gem::Requirement
84
+ requirements:
85
+ - - ">="
86
+ - !ruby/object:Gem::Version
87
+ version: '2.1'
88
+ required_rubygems_version: !ruby/object:Gem::Requirement
89
+ requirements:
90
+ - - ">="
91
+ - !ruby/object:Gem::Version
92
+ version: '0'
93
+ requirements: []
94
+ rubyforge_project:
95
+ rubygems_version: 2.2.2
96
+ signing_key:
97
+ specification_version: 4
98
+ summary: Doc Browser Search plugin for Praxis.
99
+ test_files: []
100
+ has_rdoc: