doc_contract 0.1.6 → 0.2.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.
- checksums.yaml +4 -4
- data/README.md +3 -2
- data/Rakefile +6 -5
- data/app/assets/javascripts/doc_contract/application.coffee +2 -0
- data/app/controllers/concerns/doc_contract/shared_template_and_instance_methods.rb +24 -7
- data/app/controllers/doc_contract/application_controller.rb +4 -1
- data/app/controllers/doc_contract/contract_instances_controller.rb +14 -12
- data/app/controllers/doc_contract/contract_templates_controller.rb +14 -25
- data/app/helpers/doc_contract/application_helper.rb +38 -17
- data/app/models/concerns/yaml_validator.rb +9 -3
- data/app/models/doc_contract/application_record.rb +27 -0
- data/app/models/doc_contract/contract_instance.rb +9 -2
- data/app/models/doc_contract/contract_template.rb +14 -3
- data/app/views/doc_contract/application/_button-preview-pdf.html.slim +1 -1
- data/app/views/doc_contract/application/_navigation.html.slim +7 -3
- data/app/views/doc_contract/application/main.html.slim +1 -1
- data/app/views/doc_contract/contract_instances/_form.html.slim +4 -1
- data/config/environment.rb +4 -0
- data/config/initializers/human_plural.rb +8 -5
- data/config/routes.rb +3 -2
- data/db/migrate/20220118150923_create_doc_contract_contract_templates.rb +1 -0
- data/db/migrate/20220121160034_add_markdown_to_doc_contract_contract_templates.rb +1 -0
- data/db/migrate/20220121165825_add_config_yml_to_doc_contract_contract_templates.rb +1 -0
- data/db/migrate/20220122151402_create_doc_contract_contract_instances.rb +1 -0
- data/db/migrate/20220125183437_add_titlepage_background_to_contract_templates.rb +1 -0
- data/lib/doc_contract/engine.rb +25 -10
- data/lib/doc_contract/handlebars.rb +45 -30
- data/lib/doc_contract/version.rb +3 -1
- data/lib/doc_contract.rb +4 -3
- data/lib/tasks/doc_contract_tasks.rake +1 -0
- metadata +17 -15
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA256:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: a3a6c918f368226c070e81509c178e61d83f45cd1f239a126ae66a355f50c512
|
4
|
+
data.tar.gz: 7bb72324e156c3fc3451e7379b9ab642ce395808ca0d3c2117819fd0231a86c8
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: 23ab0131768cc5d6579447c141de09915656d3d1249d8f966f1e55daa8b455189e3d4793ef995169ddf56aecceea914826bbf2156277f301974762229a1821b1
|
7
|
+
data.tar.gz: 76096e9d5a43835fd96dd94ca9a5ddc75b85ab12c3ceb60c3e66655a55404e05d5fcdc6d9cd97105b631c4d88acbda606b8c06710d266c44af56b4b3d3596ea5
|
data/README.md
CHANGED
@@ -173,16 +173,17 @@ To put your own version of this page create a view having this path in the main
|
|
173
173
|
put the following in your `config/application.rb` file:
|
174
174
|
|
175
175
|
```ruby
|
176
|
-
config.doc_contract.show_readme_on_main_page = false
|
176
|
+
config.x.doc_contract.show_readme_on_main_page = false
|
177
177
|
```
|
178
178
|
|
179
179
|
### The link home content
|
180
180
|
The default value for `config/application.rb` is:
|
181
181
|
|
182
182
|
```ruby
|
183
|
-
config.doc_contract.link_home_content = '<i class="arrow left icon"></i> Back'
|
183
|
+
config.x.doc_contract.link_home_content = -> { '<i class="arrow left icon"></i> Back' }
|
184
184
|
```
|
185
185
|
To change for example the icon, see the options at the [fomantic-ui](https://fomantic-ui.com/elements/icon.html) site.
|
186
|
+
Note that the value is a `lambda` to allow the use of for example `I18n`.
|
186
187
|
|
187
188
|
### Screenshot
|
188
189
|

|
data/Rakefile
CHANGED
@@ -1,8 +1,9 @@
|
|
1
|
-
|
1
|
+
# frozen_string_literal: true
|
2
|
+
require 'bundler/setup'
|
2
3
|
|
3
|
-
APP_RAKEFILE = File.expand_path(
|
4
|
-
load
|
4
|
+
APP_RAKEFILE = File.expand_path('spec/dummy/Rakefile', __dir__)
|
5
|
+
load 'rails/tasks/engine.rake'
|
5
6
|
|
6
|
-
load
|
7
|
+
load 'rails/tasks/statistics.rake'
|
7
8
|
|
8
|
-
require
|
9
|
+
require 'bundler/gem_tasks'
|
@@ -11,6 +11,8 @@ latex_template_infos =
|
|
11
11
|
</a>'
|
12
12
|
$ ->
|
13
13
|
$('.ui.dropdown').dropdown() # assume fomantic that has the clearable class option, a lot better than the below custom code
|
14
|
+
$('.message .close').on 'click', ->
|
15
|
+
$(@).closest('.message').transition('fade')
|
14
16
|
|
15
17
|
$('.preview-markdown').click ->
|
16
18
|
markdown = $('#contract_template_markdown').val()
|
@@ -1,3 +1,4 @@
|
|
1
|
+
# frozen_string_literal: true
|
1
2
|
module DocContract
|
2
3
|
module SharedTemplateAndInstanceMethods
|
3
4
|
# POST /doc-contract/contract_templates/:id/processed_markdown?markdown=...
|
@@ -19,12 +20,17 @@ module DocContract
|
|
19
20
|
render json: {html: @record.processed_html}
|
20
21
|
end
|
21
22
|
|
22
|
-
|
23
|
+
# rubocop:disable Metrics/MethodLength
|
24
|
+
# for now keep as a whole story
|
25
|
+
def pdf
|
23
26
|
@record = record_class.find params[:id]
|
24
27
|
authorize! :show, @record
|
28
|
+
|
25
29
|
# Allow preview using the markdown parameter. Fallback to actual saved
|
26
30
|
# version when param is not given
|
27
31
|
@record.markdown = params[:markdown] if params[:markdown].present?
|
32
|
+
return redirect_to @record unless @record.markdown.present?
|
33
|
+
|
28
34
|
@tempfile_markdown = Tempfile.new(['pandoc-markdown', '.md'])
|
29
35
|
config_addittions = {}
|
30
36
|
if nbackground = @record.try(:titlepage_background) || @record.contract_template.titlepage_background.presence
|
@@ -36,31 +42,42 @@ module DocContract
|
|
36
42
|
@tempfile_markdown.close
|
37
43
|
@tempfile_pdf = Tempfile.create(['pandoc-pdf-', '.pdf'])
|
38
44
|
@tempfile_pdf.close
|
39
|
-
command = %(
|
40
|
-
|
45
|
+
command = %(
|
46
|
+
pandoc --template=vendor/assets/doc-contract/pandoc-templates/eisvogel.tex
|
47
|
+
--css=app/assets/stylesheets/doc_contract/pandoc.css #{@tempfile_markdown.path}
|
48
|
+
-o #{@tempfile_pdf.path}
|
49
|
+
).strip.gsub(/\n\s*/, ' ')
|
50
|
+
command << ' --variable top-level-division=chapter' if @record.config['book']
|
41
51
|
#command << " --variable titlepage=true" if false # done through YAML config
|
42
|
-
command <<
|
43
|
-
command <<
|
52
|
+
command << ' --listings' # if some conditions TODO: make this a database boolean
|
53
|
+
command << ' --filter pandoc-latex-environment' if template_markdown['pandoc-latex-environment:']
|
44
54
|
|
45
55
|
stdout, stderr, status = nil
|
46
56
|
Dir.chdir DocContract::Engine.root do
|
47
57
|
stdout, stderr, status = Open3.capture3(command)
|
48
58
|
end
|
59
|
+
|
49
60
|
if status.success?
|
50
61
|
send_file @tempfile_pdf.path
|
51
62
|
else
|
52
63
|
redirect_back fallback_location: record_class, flash: {error: stderr}
|
53
64
|
end
|
54
65
|
ensure
|
55
|
-
|
56
|
-
|
66
|
+
begin
|
67
|
+
@tempfile_markdown.unlink
|
68
|
+
@tempfile_pdf.unlink
|
69
|
+
rescue
|
70
|
+
nil
|
71
|
+
end
|
57
72
|
end
|
73
|
+
# rubocop:enable Metrics/MethodLength
|
58
74
|
|
59
75
|
private
|
60
76
|
|
61
77
|
def record_class
|
62
78
|
result = controller_path.classify.safe_constantize
|
63
79
|
raise "Cannot determine record_class from controller_path: #{controller_path}" unless result
|
80
|
+
|
64
81
|
result
|
65
82
|
end
|
66
83
|
|
@@ -1,3 +1,4 @@
|
|
1
|
+
# frozen_string_literal: true
|
1
2
|
module DocContract
|
2
3
|
class ContractInstancesController < ApplicationController
|
3
4
|
include SharedTemplateAndInstanceMethods
|
@@ -14,16 +15,12 @@ module DocContract
|
|
14
15
|
|
15
16
|
def new
|
16
17
|
@record = record_class.new
|
17
|
-
|
18
|
-
|
19
|
-
def edit
|
20
|
-
@record = record_class.find(params[:id])
|
21
|
-
authorize! :edit, @record
|
18
|
+
authorize! :create, @record
|
22
19
|
end
|
23
20
|
|
24
21
|
def create
|
25
|
-
authorize! :create, record_class
|
26
22
|
@record = record_class.new record_params
|
23
|
+
authorize! :create, @record
|
27
24
|
if @record.save
|
28
25
|
redirect_to @record
|
29
26
|
else
|
@@ -31,9 +28,19 @@ module DocContract
|
|
31
28
|
end
|
32
29
|
end
|
33
30
|
|
31
|
+
def show
|
32
|
+
@record = record_class.find params[:id]
|
33
|
+
authorize! :show, @record
|
34
|
+
end
|
35
|
+
|
36
|
+
def edit
|
37
|
+
@record = record_class.find(params[:id])
|
38
|
+
authorize! :edit, @record
|
39
|
+
end
|
40
|
+
|
34
41
|
def update
|
35
42
|
@record = record_class.find params[:id]
|
36
|
-
authorize! :
|
43
|
+
authorize! :edit, @record
|
37
44
|
if @record.update record_params
|
38
45
|
redirect_to @record
|
39
46
|
else
|
@@ -41,11 +48,6 @@ module DocContract
|
|
41
48
|
end
|
42
49
|
end
|
43
50
|
|
44
|
-
def show
|
45
|
-
@record = record_class.find params[:id]
|
46
|
-
authorize! :show, @record
|
47
|
-
end
|
48
|
-
|
49
51
|
def destroy
|
50
52
|
@record = record_class.find params[:id]
|
51
53
|
authorize! :destroy, @record
|
@@ -1,3 +1,4 @@
|
|
1
|
+
# frozen_string_literal: true
|
1
2
|
module DocContract
|
2
3
|
class ContractTemplatesController < ApplicationController
|
3
4
|
include SharedTemplateAndInstanceMethods
|
@@ -14,16 +15,12 @@ module DocContract
|
|
14
15
|
|
15
16
|
def new
|
16
17
|
@record = record_class.new
|
17
|
-
|
18
|
-
|
19
|
-
def edit
|
20
|
-
@record = record_class.find(params[:id])
|
21
|
-
authorize! :edit, @record
|
18
|
+
authorize! :create, @record
|
22
19
|
end
|
23
20
|
|
24
21
|
def create
|
25
|
-
authorize! :create, record_class
|
26
22
|
@record = record_class.new record_params
|
23
|
+
authorize! :create, @record
|
27
24
|
if @record.save
|
28
25
|
redirect_to @record, status: :see_other
|
29
26
|
else
|
@@ -31,9 +28,19 @@ module DocContract
|
|
31
28
|
end
|
32
29
|
end
|
33
30
|
|
31
|
+
def show
|
32
|
+
@record = record_class.find params[:id]
|
33
|
+
authorize! :show, @record
|
34
|
+
end
|
35
|
+
|
36
|
+
def edit
|
37
|
+
@record = record_class.find(params[:id])
|
38
|
+
authorize! :edit, @record
|
39
|
+
end
|
40
|
+
|
34
41
|
def update
|
35
42
|
@record = record_class.find params[:id]
|
36
|
-
authorize! :
|
43
|
+
authorize! :edit, @record
|
37
44
|
if @record.update record_params
|
38
45
|
redirect_to @record, status: :see_other
|
39
46
|
else
|
@@ -41,29 +48,11 @@ module DocContract
|
|
41
48
|
end
|
42
49
|
end
|
43
50
|
|
44
|
-
def show
|
45
|
-
@record = record_class.find params[:id]
|
46
|
-
authorize! :show, @record
|
47
|
-
end
|
48
|
-
|
49
51
|
def destroy
|
50
52
|
@record = record_class.find params[:id]
|
51
53
|
authorize! :destroy, @record
|
52
54
|
@record.destroy
|
53
55
|
redirect_to record_class, status: :see_other
|
54
56
|
end
|
55
|
-
|
56
|
-
private
|
57
|
-
|
58
|
-
def record_class
|
59
|
-
result = controller_path.classify.safe_constantize
|
60
|
-
raise "Cannot determine record_class from controller_path: #{controller_path}" unless result
|
61
|
-
|
62
|
-
result
|
63
|
-
end
|
64
|
-
|
65
|
-
def record_params
|
66
|
-
params.require(record_class.name.demodulize.underscore).permit!
|
67
|
-
end
|
68
57
|
end
|
69
58
|
end
|
@@ -1,3 +1,5 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
1
3
|
module DocContract
|
2
4
|
module ApplicationHelper
|
3
5
|
# Return active or nil based on the given route spec
|
@@ -6,8 +8,9 @@ module DocContract
|
|
6
8
|
#NOTE: Taken from the dunlop-core gem. That is the best maintained version
|
7
9
|
def active_class(*route_specs)
|
8
10
|
options = route_specs.extract_options!
|
9
|
-
return nil if Array.wrap(options[:except]).any?{|exception| current_route_spec?(exception) }
|
10
|
-
return 'active' if route_specs.any?{|rs| current_route_spec?(rs, options) }
|
11
|
+
return nil if Array.wrap(options[:except]).any?{ |exception| current_route_spec?(exception) }
|
12
|
+
return 'active' if route_specs.any?{ |rs| current_route_spec?(rs, options) }
|
13
|
+
|
11
14
|
nil
|
12
15
|
end
|
13
16
|
|
@@ -19,17 +22,21 @@ module DocContract
|
|
19
22
|
# current_route_spec?('#show') #=> true if action_name is show, false otherwise
|
20
23
|
#NOTE: this helper is tested through the active_class helper
|
21
24
|
#NOTE: Taken from the dunlop-core gem. That is the best maintained version
|
22
|
-
def current_route_spec?(route_spec,
|
25
|
+
def current_route_spec?(route_spec, _options = {})
|
23
26
|
return route_spec.match([controller_path, action_name].join('#')) if route_spec.is_a?(Regexp)
|
27
|
+
|
24
28
|
controller, action = route_spec.split('#')
|
25
29
|
return action == params[:id] if controller_path == 'high_voltage/pages'
|
30
|
+
|
26
31
|
actual_controller_parts = controller_path.split('/')
|
27
32
|
if controller #and controller_path == controller
|
28
33
|
tested_controller_parts = controller.split('/')
|
29
34
|
return if tested_controller_parts.size > actual_controller_parts.size
|
35
|
+
|
30
36
|
if actual_controller_parts[0...tested_controller_parts.size] == tested_controller_parts
|
31
37
|
# controller spec matches
|
32
38
|
return true unless action
|
39
|
+
|
33
40
|
action_name == action
|
34
41
|
end
|
35
42
|
else
|
@@ -78,11 +85,11 @@ module DocContract
|
|
78
85
|
def page_title_for_resource(args)
|
79
86
|
options = args.extract_options!
|
80
87
|
model = args[1].respond_to?(:model_name) ? args[1] : args[1].class
|
81
|
-
if args.first == :index
|
82
|
-
|
83
|
-
|
84
|
-
|
85
|
-
|
88
|
+
title = if args.first == :index
|
89
|
+
t('action.index.label', models: model.model_name.human_plural).html_safe
|
90
|
+
else
|
91
|
+
t("action.#{args.first}.label", model: model.model_name.human).html_safe
|
92
|
+
end
|
86
93
|
if back_options = options[:back]
|
87
94
|
url =
|
88
95
|
case back_options
|
@@ -105,7 +112,7 @@ module DocContract
|
|
105
112
|
"#{from_item} - #{to_item} / #{records.total_count}"
|
106
113
|
end
|
107
114
|
|
108
|
-
def at(attribute_name, scope_model=nil)
|
115
|
+
def at(attribute_name, scope_model = nil)
|
109
116
|
scope_model ||= @scope_model
|
110
117
|
scope_model.human_attribute_name(attribute_name)
|
111
118
|
end
|
@@ -116,7 +123,7 @@ module DocContract
|
|
116
123
|
classes |= ['search']
|
117
124
|
classes << 'conditions-present' if @q.conditions.present?
|
118
125
|
content = capture(&blk)
|
119
|
-
content_tag(:tr, content,
|
126
|
+
content_tag(:tr, content, class: classes)
|
120
127
|
end
|
121
128
|
|
122
129
|
# This helper returns the link for showing a record inside a table
|
@@ -126,7 +133,11 @@ module DocContract
|
|
126
133
|
else
|
127
134
|
return unless can? :show, record
|
128
135
|
end
|
129
|
-
link_to(
|
136
|
+
link_to(
|
137
|
+
content_tag(:i, nil, class: 'folder open icon'),
|
138
|
+
path || record,
|
139
|
+
class: 'table-link show ui mini basic primary icon button'
|
140
|
+
)
|
130
141
|
end
|
131
142
|
|
132
143
|
# This helper returns the link for showing a record inside a table
|
@@ -136,7 +147,11 @@ module DocContract
|
|
136
147
|
else
|
137
148
|
return unless can? :download, record
|
138
149
|
end
|
139
|
-
link_to(
|
150
|
+
link_to(
|
151
|
+
content_tag(:i, nil, class: 'download icon'),
|
152
|
+
path || [:download, record],
|
153
|
+
class: 'table-link download ui mini violet icon button'
|
154
|
+
)
|
140
155
|
end
|
141
156
|
|
142
157
|
# This helper returns the link for editing a record inside a table
|
@@ -161,16 +176,22 @@ module DocContract
|
|
161
176
|
record_name ||= record.name if record.respond_to?(:name)
|
162
177
|
record_name ||= record.title if record.respond_to?(:title)
|
163
178
|
confirm_text << " #{record_name}" if record_name.present?
|
164
|
-
confirm_text <<
|
165
|
-
link_to(
|
179
|
+
confirm_text << '?'
|
180
|
+
link_to(
|
181
|
+
content_tag(:i, nil, class: 'trash icon'),
|
182
|
+
path || record,
|
183
|
+
method: :delete,
|
184
|
+
data: { confirm: confirm_text },
|
185
|
+
class: 'table-link destroy ui mini negative icon button'
|
186
|
+
)
|
166
187
|
end
|
167
188
|
|
168
189
|
# https://coderwall.com/p/7gqmog/display-flash-messages-with-semantic-ui-in-rails
|
169
190
|
def flash_class(level)
|
170
191
|
case level.to_sym
|
171
|
-
when :success then
|
172
|
-
when :error, :alert then
|
173
|
-
when :notice then
|
192
|
+
when :success then 'ui positive message'
|
193
|
+
when :error, :alert then 'ui negative message'
|
194
|
+
when :notice then 'ui info message'
|
174
195
|
else "ui #{level} message"
|
175
196
|
end
|
176
197
|
end
|
@@ -1,9 +1,15 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
1
3
|
class YamlValidator < ActiveModel::EachValidator
|
2
4
|
def validate_each(record, attribute, value)
|
3
5
|
return if value.blank?
|
4
|
-
|
5
|
-
|
6
|
-
|
6
|
+
|
7
|
+
object = begin
|
8
|
+
YAML.safe_load(value, permitted_classes: [Symbol, Date])
|
9
|
+
rescue # Psych::SyntaxError
|
10
|
+
nil
|
7
11
|
end
|
12
|
+
|
13
|
+
record.errors.add attribute, (options[:message] || 'is not valid YAML') unless object
|
8
14
|
end
|
9
15
|
end
|
@@ -0,0 +1,27 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module DocContract
|
4
|
+
class ApplicationRecord < ActiveRecord::Base
|
5
|
+
|
6
|
+
self.abstract_class = true
|
7
|
+
|
8
|
+
RANSACKABLE_ATTRIBUTES = [].freeze
|
9
|
+
RANSACKABLE_ASSOCIATIONS = [].freeze
|
10
|
+
|
11
|
+
def self.ransackable_attributes(_auth_object = nil)
|
12
|
+
if const_defined? :RANSACKABLE_ATTRIBUTES
|
13
|
+
const_get :RANSACKABLE_ATTRIBUTES
|
14
|
+
else
|
15
|
+
RANSACKABLE_ATTRIBUTES
|
16
|
+
end
|
17
|
+
end
|
18
|
+
|
19
|
+
def self.ransackable_associations(_auth_object = nil)
|
20
|
+
if const_defined? :RANSACKABLE_ASSOCIATIONS
|
21
|
+
const_get :RANSACKABLE_ASSOCIATIONS
|
22
|
+
else
|
23
|
+
RANSACKABLE_ASSOCIATIONS
|
24
|
+
end
|
25
|
+
end
|
26
|
+
end
|
27
|
+
end
|
@@ -1,8 +1,15 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
1
3
|
module DocContract
|
2
4
|
class ContractInstance < ApplicationRecord
|
3
|
-
|
5
|
+
|
4
6
|
belongs_to :contract_template
|
5
7
|
|
8
|
+
validates :name, presence: true
|
9
|
+
|
10
|
+
RANSACKABLE_ATTRIBUTES = %w[name].freeze
|
11
|
+
RANSACKABLE_ASSOCIATIONS = %w[contract_template].freeze
|
12
|
+
|
6
13
|
def config
|
7
14
|
own_config = YAML.safe_load(config_yml.to_s, permitted_classes: [Symbol, Date]) || {}
|
8
15
|
own_config.reverse_merge contract_template.config
|
@@ -27,7 +34,7 @@ module DocContract
|
|
27
34
|
config.merge! config_addittions unless config_addittions.blank?
|
28
35
|
result = DocContract::Handlebars.compile(markdown, config)
|
29
36
|
# prefix the markdown with the config (eisvogel pandoc)
|
30
|
-
"#{config.to_yaml}...\n\n"
|
37
|
+
"#{config.to_yaml}...\n\n#{result}"
|
31
38
|
end
|
32
39
|
|
33
40
|
def processed_html
|
@@ -1,6 +1,12 @@
|
|
1
|
+
# frozen_string_literal: true
|
1
2
|
module DocContract
|
2
3
|
class ContractTemplate < ApplicationRecord
|
3
|
-
|
4
|
+
|
5
|
+
HANDLEBARS_REGEX = /{{((#)?[a-z._ ]+)}}/
|
6
|
+
|
7
|
+
RANSACKABLE_ATTRIBUTES = %w[name].freeze
|
8
|
+
#RANSACKABLE_ASSOCIATIONS = %w[].freeze
|
9
|
+
|
4
10
|
has_many :contract_instances, dependent: :delete_all
|
5
11
|
|
6
12
|
validates :name, presence: true
|
@@ -10,6 +16,11 @@ module DocContract
|
|
10
16
|
YAML.safe_load(config_yml.to_s, permitted_classes: [Symbol, Date]) || {}
|
11
17
|
end
|
12
18
|
|
19
|
+
# Do not fail when an instance trait tries to access fallback template properties
|
20
|
+
def contract_template
|
21
|
+
self
|
22
|
+
end
|
23
|
+
|
13
24
|
def processed_markdown(config_addittions = {})
|
14
25
|
config = self.config
|
15
26
|
config.merge! config_addittions unless config_addittions.blank?
|
@@ -21,7 +32,7 @@ module DocContract
|
|
21
32
|
config.merge! config_addittions unless config_addittions.blank?
|
22
33
|
result = DocContract::Handlebars.compile(markdown, config)
|
23
34
|
# prefix the markdown with the config (eisvogel pandoc)
|
24
|
-
"#{config.to_yaml}...\n\n"
|
35
|
+
"#{config.to_yaml}...\n\n#{result}"
|
25
36
|
end
|
26
37
|
|
27
38
|
def processed_html
|
@@ -31,7 +42,7 @@ module DocContract
|
|
31
42
|
end
|
32
43
|
|
33
44
|
def present_handlebars_expressions
|
34
|
-
markdown.scan(HANDLEBARS_REGEX).map(&:first).uniq
|
45
|
+
markdown.to_s.scan(HANDLEBARS_REGEX).map(&:first).uniq
|
35
46
|
end
|
36
47
|
end
|
37
48
|
end
|
@@ -1 +1 @@
|
|
1
|
-
= link_to 'PDF', polymorphic_path(@record, action: :
|
1
|
+
= link_to 'PDF', polymorphic_path(@record, action: :pdf), class: 'ui button'
|
@@ -1,9 +1,13 @@
|
|
1
1
|
.ui.inverted.menu.fixed
|
2
2
|
= link_to main_app.root_path, class: 'header item'
|
3
|
-
= Rails.application.config.doc_contract.link_home_content.to_s.html_safe
|
3
|
+
= Rails.application.config.x.doc_contract.link_home_content.call.to_s.html_safe
|
4
|
+
= link_to doc_contract.root_path, class: 'header item'
|
5
|
+
= Rails.application.config.x.doc_contract.application_title.call.to_s.html_safe
|
4
6
|
- if current_user.present?
|
5
|
-
|
6
|
-
|
7
|
+
- if can? :read, DocContract::ContractTemplate
|
8
|
+
= link_to DocContract::ContractTemplate.model_name.human_plural, [DocContract::ContractTemplate], class: ['item', active_class('doc_contract/contract_templates')]
|
9
|
+
- if can? :read, DocContract::ContractInstance
|
10
|
+
= link_to DocContract::ContractInstance.model_name.human_plural, [DocContract::ContractInstance], class: ['item', active_class('doc_contract/contract_instances')]
|
7
11
|
.right.menu
|
8
12
|
- if current_user.present?
|
9
13
|
.ui.dropdown.item
|
@@ -1,6 +1,6 @@
|
|
1
1
|
.ui.two.item.menu
|
2
2
|
= link_to DocContract::ContractTemplate.model_name.human_plural, [DocContract::ContractTemplate], class: ['item', active_class('doc_contract/contract_templates')]
|
3
3
|
= link_to DocContract::ContractInstance.model_name.human_plural, [DocContract::ContractInstance], class: ['item', active_class('doc_contract/contract_instances')]
|
4
|
-
- if Rails.application.config.doc_contract.show_readme_on_main_page
|
4
|
+
- if Rails.application.config.x.doc_contract.show_readme_on_main_page
|
5
5
|
hr
|
6
6
|
#readme_container== readme_html
|
@@ -4,7 +4,10 @@
|
|
4
4
|
tbody
|
5
5
|
tr
|
6
6
|
td= at :name
|
7
|
-
td
|
7
|
+
td
|
8
|
+
.ui.input.error= f.text_field :name
|
9
|
+
- if @record.errors[:name].present?
|
10
|
+
.ui.left.pointing.red.basic.label= @record.errors[:name].to_sentence
|
8
11
|
tr
|
9
12
|
td
|
10
13
|
= DocContract::ContractTemplate.model_name.human
|
@@ -1,16 +1,19 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
1
3
|
module ActiveModel
|
2
4
|
class Name
|
5
|
+
|
3
6
|
def human_plural
|
4
7
|
# Try to find the plural name of a model. If none can be found try to find a non namespaced version and return that one
|
5
|
-
default =
|
6
|
-
if path.present? and path['/'] # non namespaced model a
|
7
|
-
unnamespaced_path = path.sub(
|
8
|
-
I18n.t(unnamespaced_path, default:
|
8
|
+
default = proc do |path|
|
9
|
+
if path.present? and path['/'] # non namespaced model a.b.c/d/e => a.b.c
|
10
|
+
unnamespaced_path = path.sub(%r{\.(\w+)/[\w\\]+}, '.\1')
|
11
|
+
I18n.t(unnamespaced_path, default: proc{ human.pluralize })
|
9
12
|
else
|
10
13
|
human.pluralize
|
11
14
|
end
|
12
15
|
end
|
13
|
-
I18n.t("#{@klass.i18n_scope}.models.plural.#{i18n_key}", default: default
|
16
|
+
I18n.t("#{@klass.i18n_scope}.models.plural.#{i18n_key}", default: default)
|
14
17
|
end
|
15
18
|
end
|
16
19
|
end
|
data/config/routes.rb
CHANGED
@@ -1,16 +1,17 @@
|
|
1
|
+
# frozen_string_literal: true
|
1
2
|
DocContract::Engine.routes.draw do
|
2
3
|
resources :contract_templates do
|
3
4
|
member do
|
4
5
|
post :processed_markdown
|
5
6
|
post :processed_html
|
6
|
-
get :
|
7
|
+
get :pdf
|
7
8
|
end
|
8
9
|
end
|
9
10
|
resources :contract_instances do
|
10
11
|
member do
|
11
12
|
post :processed_markdown
|
12
13
|
post :processed_html
|
13
|
-
get :
|
14
|
+
get :pdf
|
14
15
|
end
|
15
16
|
end
|
16
17
|
root 'application#main'
|
data/lib/doc_contract/engine.rb
CHANGED
@@ -1,27 +1,42 @@
|
|
1
|
-
|
1
|
+
# frozen_string_literal: true
|
2
2
|
require 'cancancan'
|
3
|
-
require '
|
3
|
+
require 'jquery-rails'
|
4
4
|
require 'kaminari'
|
5
|
+
require 'numbers_and_words'
|
6
|
+
require 'ransack'
|
7
|
+
require 'redcarpet'
|
8
|
+
|
5
9
|
module DocContract
|
6
10
|
class Engine < ::Rails::Engine
|
11
|
+
|
7
12
|
#isolate_namespace self.name.deconstantize.safe_constantize
|
8
13
|
isolate_namespace DocContract
|
9
|
-
|
14
|
+
|
15
|
+
initializer 'doc_contract.assets.precompile' do |main_app|
|
10
16
|
#app.config.assets.precompile << "config/engine_name_manifest.js"
|
11
|
-
main_app.config.assets.precompile <<
|
12
|
-
main_app.config.assets.precompile <<
|
13
|
-
|
14
|
-
|
15
|
-
|
17
|
+
main_app.config.assets.precompile << 'doc_contract/application.css'
|
18
|
+
main_app.config.assets.precompile << 'doc_contract/application.js'
|
19
|
+
|
20
|
+
# Engine specific config
|
21
|
+
# Allow setting false, so check for nil
|
22
|
+
main_app.config.x.doc_contract.show_readme_on_main_page = true if main_app.config.x.doc_contract.show_readme_on_main_page.nil?
|
23
|
+
main_app.config.x.doc_contract.application_title ||= -> { 'DOC-contract' }
|
24
|
+
main_app.config.x.doc_contract.link_home_content ||= -> { '<i class="arrow left icon"></i> Back' }
|
16
25
|
end
|
17
26
|
|
18
27
|
# add migrations to containing application
|
19
28
|
initializer 'doc_contract.append_migrations' do |app|
|
20
29
|
unless app.root.to_s.match root.to_s
|
21
|
-
config.paths[
|
22
|
-
app.config.paths[
|
30
|
+
config.paths['db/migrate'].expanded.each do |expanded_path|
|
31
|
+
app.config.paths['db/migrate'] << expanded_path
|
23
32
|
end
|
24
33
|
end
|
25
34
|
end
|
35
|
+
|
36
|
+
initializer 'doc_contract.ensure_set_field_error_proc' do |app|
|
37
|
+
app.config.action_view.field_error_proc ||= proc do |html_tag, _instance|
|
38
|
+
%|<div class="field_with_errors field error has-error">#{html_tag}</div>|.html_safe
|
39
|
+
end
|
40
|
+
end
|
26
41
|
end
|
27
42
|
end
|
@@ -1,25 +1,32 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
1
3
|
module DocContract
|
2
4
|
class Handlebars
|
5
|
+
|
3
6
|
COMPILE_ATTRIBUTES = %w[
|
4
7
|
title subtitle
|
5
8
|
header-left header-center header-right
|
6
9
|
footer-left footer-center footer-right
|
7
|
-
]
|
10
|
+
].freeze
|
11
|
+
|
8
12
|
class DummyEscaper
|
13
|
+
|
9
14
|
def self.escape(value)
|
10
15
|
value
|
11
16
|
end
|
12
17
|
end
|
13
18
|
|
19
|
+
# rubocop:disable Metrics/MethodLength
|
14
20
|
def self.renderer
|
15
|
-
return
|
21
|
+
return @handlebars_renderer if @handlebars_renderer
|
22
|
+
|
16
23
|
renderer = ::Handlebars::Handlebars.new
|
17
24
|
renderer.set_escaper DummyEscaper # Do not escape HTML
|
18
|
-
currency_helper =
|
19
|
-
if Integer
|
20
|
-
|
25
|
+
currency_helper = proc do |_context, value, currency_symbol|
|
26
|
+
if value.is_a?(Integer) or value.to_i == value
|
27
|
+
format("#{currency_symbol} %.0f,-", value) #.gsub(/(\d)(?=\d{3}+,)/, '\1.')
|
21
28
|
else
|
22
|
-
|
29
|
+
format("#{currency_symbol} %.2f", value.to_f) #.gsub(/(\d)(?=\d{3}+,)/, '\1.')
|
23
30
|
end
|
24
31
|
end
|
25
32
|
#renderer.register_helper(:euro) do |context, value|
|
@@ -29,15 +36,15 @@ module DocContract
|
|
29
36
|
# sprintf("€ %.2f", value.to_f) #.gsub(/(\d)(?=\d{3}+,)/, '\1.')
|
30
37
|
# end
|
31
38
|
#end
|
32
|
-
eval_boolean =
|
33
|
-
blocks, comparables = values.partition{|v| v.is_a? ::Handlebars::Tree::Block}
|
39
|
+
eval_boolean = proc do |context, values, compare_lambda|
|
40
|
+
blocks, comparables = values.partition{ |v| v.is_a? ::Handlebars::Tree::Block }
|
34
41
|
true_blk, false_blk = blocks
|
35
|
-
comparables = comparables.map{|v| v.is_a?(Parslet::Slice) ? v.to_s : v}
|
36
|
-
if compare_lambda.arity == 1
|
37
|
-
|
38
|
-
|
39
|
-
|
40
|
-
|
42
|
+
comparables = comparables.map{ |v| v.is_a?(Parslet::Slice) ? v.to_s : v }
|
43
|
+
are_equal = if compare_lambda.arity == 1
|
44
|
+
compare_lambda.call(comparables)
|
45
|
+
else
|
46
|
+
compare_lambda.call(*comparables)
|
47
|
+
end
|
41
48
|
if true_blk.items.present? # assumed internal helper usecase without blocks
|
42
49
|
are_equal ? true_blk.fn(context) : false_blk.try(:fn, context)
|
43
50
|
else
|
@@ -47,31 +54,38 @@ module DocContract
|
|
47
54
|
renderer.register_helper(:euro) { |context, value| currency_helper.call context, value, '€' }
|
48
55
|
renderer.register_helper(:dollar) { |context, value| currency_helper.call context, value, '$' }
|
49
56
|
renderer.register_helper(:peso) { |context, value| currency_helper.call context, value, '$' }
|
50
|
-
renderer.register_helper(:plus) {|
|
51
|
-
renderer.register_helper(:sum)
|
52
|
-
renderer.register_helper(:eq)
|
57
|
+
renderer.register_helper(:plus) { |_context, value, addition, *_rest| value.to_f + addition.to_f }
|
58
|
+
renderer.register_helper(:sum) { |_context, *values, _bkl| (values.first.is_a?(Array) ? values.first : values).map(&:to_f).sum }
|
59
|
+
renderer.register_helper(:eq) { |context, *values| eval_boolean.call context, values, ->(vals){ vals.uniq.length <= 1 } }
|
53
60
|
renderer.register_helper(:neq) { |context, *values| eval_boolean.call context, values, ->(vals){ vals.uniq.length > 1 } }
|
54
|
-
renderer.register_helper(:gt)
|
55
|
-
renderer.register_helper(:includes)
|
56
|
-
renderer.register_helper(:map) do |
|
61
|
+
renderer.register_helper(:gt) { |context, *values| eval_boolean.call context, values, ->(a, b){ a > b } }
|
62
|
+
renderer.register_helper(:includes) { |context, *values| eval_boolean.call context, values, ->(vals){ vals.first.include? vals.last } }
|
63
|
+
renderer.register_helper(:map) do |_context, array, key|
|
57
64
|
return [] unless array.present?
|
58
|
-
|
65
|
+
|
66
|
+
array.map{ |e| e[key.to_s] || e[key.to_sym] }
|
59
67
|
end
|
60
|
-
renderer.register_helper(:upcase) {|
|
61
|
-
renderer.register_helper(:downcase) {|
|
62
|
-
renderer.register_helper(:to_sentence) do |
|
68
|
+
renderer.register_helper(:upcase) { |_context, value| value.to_s.upcase }
|
69
|
+
renderer.register_helper(:downcase) { |_context, value| value.to_s.downcase }
|
70
|
+
renderer.register_helper(:to_sentence) do |_context, array, nester|
|
63
71
|
case nester
|
64
72
|
when String, Parslet::Slice
|
65
|
-
array.map{|e| e[nester.to_s] || e[nester.to_sym]}.to_sentence
|
73
|
+
array.map{ |e| e[nester.to_s] || e[nester.to_sym] }.to_sentence
|
66
74
|
else
|
67
75
|
array.to_sentence
|
68
76
|
end
|
69
77
|
end
|
70
|
-
renderer.register_helper(:to_words) { |
|
71
|
-
|
78
|
+
renderer.register_helper(:to_words) { |_context, value| value.to_words }
|
79
|
+
renderer.register_helper(:first_present) do |_context, *args|
|
80
|
+
args.find(&:present?)
|
81
|
+
end
|
82
|
+
@handlebars_renderer = renderer
|
72
83
|
end
|
84
|
+
# rubocop:enable Metrics/MethodLength
|
73
85
|
|
74
86
|
def self.compile(text, object, process_attributes: true)
|
87
|
+
return '' unless text.present?
|
88
|
+
|
75
89
|
object ||= {}
|
76
90
|
if process_attributes
|
77
91
|
object.stringify_keys!
|
@@ -83,13 +97,14 @@ module DocContract
|
|
83
97
|
|
84
98
|
def self.enrich_object(object)
|
85
99
|
object['today'] = Date.today.iso8601 if object['today'].blank?
|
86
|
-
object.keys
|
100
|
+
iteration_keys = object.keys
|
101
|
+
iteration_keys.each do |k|
|
87
102
|
if k =~ /_items$/ and object[k].is_a?(Array)
|
88
|
-
amounts_sum = object[k].map{|o| (o[:amount] || o['amount']).to_f}.sum
|
103
|
+
amounts_sum = object[k].map{ |o| (o[:amount] || o['amount']).to_f }.sum
|
89
104
|
object["#{k}_total"] = amounts_sum
|
90
105
|
end
|
91
106
|
if COMPILE_ATTRIBUTES.include? k
|
92
|
-
#TODO check self reference, maybe even circular?
|
107
|
+
#TODO: check self reference, maybe even circular?
|
93
108
|
object[k] = compile(object[k], object, process_attributes: false)
|
94
109
|
end
|
95
110
|
end
|
data/lib/doc_contract/version.rb
CHANGED
data/lib/doc_contract.rb
CHANGED
@@ -1,6 +1,7 @@
|
|
1
|
-
|
2
|
-
require
|
3
|
-
require
|
1
|
+
# frozen_string_literal: true
|
2
|
+
require 'doc_contract/version'
|
3
|
+
require 'doc_contract/engine'
|
4
|
+
require 'doc_contract/handlebars'
|
4
5
|
require 'ruby-handlebars'
|
5
6
|
require 'open3'
|
6
7
|
|
metadata
CHANGED
@@ -1,17 +1,17 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: doc_contract
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 0.
|
4
|
+
version: 0.2.0
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- Benjamin ter Kuile
|
8
8
|
autorequire:
|
9
9
|
bindir: bin
|
10
10
|
cert_chain: []
|
11
|
-
date:
|
11
|
+
date: 2023-08-07 00:00:00.000000000 Z
|
12
12
|
dependencies:
|
13
13
|
- !ruby/object:Gem::Dependency
|
14
|
-
name:
|
14
|
+
name: cancancan
|
15
15
|
requirement: !ruby/object:Gem::Requirement
|
16
16
|
requirements:
|
17
17
|
- - ">="
|
@@ -25,7 +25,7 @@ dependencies:
|
|
25
25
|
- !ruby/object:Gem::Version
|
26
26
|
version: '0'
|
27
27
|
- !ruby/object:Gem::Dependency
|
28
|
-
name:
|
28
|
+
name: jquery-rails
|
29
29
|
requirement: !ruby/object:Gem::Requirement
|
30
30
|
requirements:
|
31
31
|
- - ">="
|
@@ -39,7 +39,7 @@ dependencies:
|
|
39
39
|
- !ruby/object:Gem::Version
|
40
40
|
version: '0'
|
41
41
|
- !ruby/object:Gem::Dependency
|
42
|
-
name:
|
42
|
+
name: kaminari
|
43
43
|
requirement: !ruby/object:Gem::Requirement
|
44
44
|
requirements:
|
45
45
|
- - ">="
|
@@ -53,7 +53,7 @@ dependencies:
|
|
53
53
|
- !ruby/object:Gem::Version
|
54
54
|
version: '0'
|
55
55
|
- !ruby/object:Gem::Dependency
|
56
|
-
name:
|
56
|
+
name: numbers_and_words
|
57
57
|
requirement: !ruby/object:Gem::Requirement
|
58
58
|
requirements:
|
59
59
|
- - ">="
|
@@ -67,7 +67,7 @@ dependencies:
|
|
67
67
|
- !ruby/object:Gem::Version
|
68
68
|
version: '0'
|
69
69
|
- !ruby/object:Gem::Dependency
|
70
|
-
name:
|
70
|
+
name: rails
|
71
71
|
requirement: !ruby/object:Gem::Requirement
|
72
72
|
requirements:
|
73
73
|
- - ">="
|
@@ -81,7 +81,7 @@ dependencies:
|
|
81
81
|
- !ruby/object:Gem::Version
|
82
82
|
version: '0'
|
83
83
|
- !ruby/object:Gem::Dependency
|
84
|
-
name:
|
84
|
+
name: ransack
|
85
85
|
requirement: !ruby/object:Gem::Requirement
|
86
86
|
requirements:
|
87
87
|
- - ">="
|
@@ -95,7 +95,7 @@ dependencies:
|
|
95
95
|
- !ruby/object:Gem::Version
|
96
96
|
version: '0'
|
97
97
|
- !ruby/object:Gem::Dependency
|
98
|
-
name:
|
98
|
+
name: redcarpet
|
99
99
|
requirement: !ruby/object:Gem::Requirement
|
100
100
|
requirements:
|
101
101
|
- - ">="
|
@@ -109,13 +109,13 @@ dependencies:
|
|
109
109
|
- !ruby/object:Gem::Version
|
110
110
|
version: '0'
|
111
111
|
- !ruby/object:Gem::Dependency
|
112
|
-
name:
|
112
|
+
name: ruby-handlebars
|
113
113
|
requirement: !ruby/object:Gem::Requirement
|
114
114
|
requirements:
|
115
115
|
- - ">="
|
116
116
|
- !ruby/object:Gem::Version
|
117
117
|
version: '0'
|
118
|
-
type: :
|
118
|
+
type: :runtime
|
119
119
|
prerelease: false
|
120
120
|
version_requirements: !ruby/object:Gem::Requirement
|
121
121
|
requirements:
|
@@ -123,7 +123,7 @@ dependencies:
|
|
123
123
|
- !ruby/object:Gem::Version
|
124
124
|
version: '0'
|
125
125
|
- !ruby/object:Gem::Dependency
|
126
|
-
name:
|
126
|
+
name: coffee-rails
|
127
127
|
requirement: !ruby/object:Gem::Requirement
|
128
128
|
requirements:
|
129
129
|
- - ">="
|
@@ -137,7 +137,7 @@ dependencies:
|
|
137
137
|
- !ruby/object:Gem::Version
|
138
138
|
version: '0'
|
139
139
|
- !ruby/object:Gem::Dependency
|
140
|
-
name:
|
140
|
+
name: factory_bot_rails
|
141
141
|
requirement: !ruby/object:Gem::Requirement
|
142
142
|
requirements:
|
143
143
|
- - ">="
|
@@ -151,7 +151,7 @@ dependencies:
|
|
151
151
|
- !ruby/object:Gem::Version
|
152
152
|
version: '0'
|
153
153
|
- !ruby/object:Gem::Dependency
|
154
|
-
name:
|
154
|
+
name: rspec-rails
|
155
155
|
requirement: !ruby/object:Gem::Requirement
|
156
156
|
requirements:
|
157
157
|
- - ">="
|
@@ -187,6 +187,7 @@ files:
|
|
187
187
|
- app/controllers/doc_contract/contract_templates_controller.rb
|
188
188
|
- app/helpers/doc_contract/application_helper.rb
|
189
189
|
- app/models/concerns/yaml_validator.rb
|
190
|
+
- app/models/doc_contract/application_record.rb
|
190
191
|
- app/models/doc_contract/contract_instance.rb
|
191
192
|
- app/models/doc_contract/contract_template.rb
|
192
193
|
- app/views/doc_contract/application/_button-destroy-record.html.slim
|
@@ -217,6 +218,7 @@ files:
|
|
217
218
|
- app/views/doc_contract/kaminari/_paginator.html.slim
|
218
219
|
- app/views/doc_contract/kaminari/_prev_page.html.slim
|
219
220
|
- app/views/layouts/doc_contract/application.html.slim
|
221
|
+
- config/environment.rb
|
220
222
|
- config/initializers/human_plural.rb
|
221
223
|
- config/locales/en.yml
|
222
224
|
- config/routes.rb
|
@@ -262,7 +264,7 @@ required_rubygems_version: !ruby/object:Gem::Requirement
|
|
262
264
|
- !ruby/object:Gem::Version
|
263
265
|
version: '0'
|
264
266
|
requirements: []
|
265
|
-
rubygems_version: 3.
|
267
|
+
rubygems_version: 3.4.10
|
266
268
|
signing_key:
|
267
269
|
specification_version: 4
|
268
270
|
summary: Create nice and easy contracts based on pandoc
|