doc_contract 0.1.5 → 0.2.0

Sign up to get free protection for your applications and to get access to all the features.
Files changed (33) hide show
  1. checksums.yaml +4 -4
  2. data/README.md +3 -2
  3. data/Rakefile +6 -5
  4. data/app/assets/javascripts/doc_contract/application.coffee +2 -0
  5. data/app/controllers/concerns/doc_contract/shared_template_and_instance_methods.rb +24 -7
  6. data/app/controllers/doc_contract/application_controller.rb +4 -1
  7. data/app/controllers/doc_contract/contract_instances_controller.rb +15 -13
  8. data/app/controllers/doc_contract/contract_templates_controller.rb +15 -26
  9. data/app/helpers/doc_contract/application_helper.rb +38 -17
  10. data/app/models/concerns/yaml_validator.rb +9 -3
  11. data/app/models/doc_contract/application_record.rb +27 -0
  12. data/app/models/doc_contract/contract_instance.rb +10 -3
  13. data/app/models/doc_contract/contract_template.rb +15 -4
  14. data/app/views/doc_contract/application/_button-preview-pdf.html.slim +1 -1
  15. data/app/views/doc_contract/application/_navigation.html.slim +7 -3
  16. data/app/views/doc_contract/application/main.html.slim +1 -1
  17. data/app/views/doc_contract/contract_instances/_form.html.slim +4 -1
  18. data/app/views/doc_contract/contract_instances/index.html.slim +2 -2
  19. data/app/views/doc_contract/contract_templates/index.html.slim +1 -1
  20. data/config/environment.rb +4 -0
  21. data/config/initializers/human_plural.rb +8 -5
  22. data/config/routes.rb +3 -2
  23. data/db/migrate/20220118150923_create_doc_contract_contract_templates.rb +1 -0
  24. data/db/migrate/20220121160034_add_markdown_to_doc_contract_contract_templates.rb +1 -0
  25. data/db/migrate/20220121165825_add_config_yml_to_doc_contract_contract_templates.rb +1 -0
  26. data/db/migrate/20220122151402_create_doc_contract_contract_instances.rb +1 -0
  27. data/db/migrate/20220125183437_add_titlepage_background_to_contract_templates.rb +1 -0
  28. data/lib/doc_contract/engine.rb +25 -10
  29. data/lib/doc_contract/handlebars.rb +45 -30
  30. data/lib/doc_contract/version.rb +3 -1
  31. data/lib/doc_contract.rb +4 -3
  32. data/lib/tasks/doc_contract_tasks.rake +1 -0
  33. metadata +17 -15
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: a8bf71ecf102ece032fb389c712f87952492ffab66f2316b9f2a08a14c78a377
4
- data.tar.gz: 315c59f51e3f2639cb3dc98a714b8be9cbe528bb407ce5bea0076bbeacf0a038
3
+ metadata.gz: a3a6c918f368226c070e81509c178e61d83f45cd1f239a126ae66a355f50c512
4
+ data.tar.gz: 7bb72324e156c3fc3451e7379b9ab642ce395808ca0d3c2117819fd0231a86c8
5
5
  SHA512:
6
- metadata.gz: 2d5e249c5e2e04e8d1129ae3e58a1fff499632cf2887e0d603c7fb6b4935961bbdd3a1bd690fa6746754375356011f64215d842c715e58828f8d9fcf2cb59551
7
- data.tar.gz: a1c6c894ac00bd866f5e86d2e4ed657763e5c4114a80c38a62fc1d2c161fa35b0f4074514463eb8820a922ed3e69389cf2fa492dd2902477c0094db0bfa5f834
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
  ![Edit template scnreenshot](/spec/dummy/public/DocContract-template-edit.png)
data/Rakefile CHANGED
@@ -1,8 +1,9 @@
1
- require "bundler/setup"
1
+ # frozen_string_literal: true
2
+ require 'bundler/setup'
2
3
 
3
- APP_RAKEFILE = File.expand_path("spec/dummy/Rakefile", __dir__)
4
- load "rails/tasks/engine.rake"
4
+ APP_RAKEFILE = File.expand_path('spec/dummy/Rakefile', __dir__)
5
+ load 'rails/tasks/engine.rake'
5
6
 
6
- load "rails/tasks/statistics.rake"
7
+ load 'rails/tasks/statistics.rake'
7
8
 
8
- require "bundler/gem_tasks"
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
- def get_pdf
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 = %( pandoc --template=vendor/assets/doc-contract/pandoc-templates/eisvogel.tex --css=app/assets/stylesheets/doc_contract/pandoc.css #{@tempfile_markdown.path} -o #{@tempfile_pdf.path} )
40
- command << " --variable top-level-division=chapter" if @record.config['book']
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 << " --listings" # if some conditions TODO: make this a database boolean
43
- command << " --filter pandoc-latex-environment" if template_markdown['pandoc-latex-environment:']
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
- @tempfile_markdown.unlink rescue nil
56
- @tempfile_pdf.unlink rescue nil
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,8 +1,11 @@
1
+ # frozen_string_literal: true
2
+
1
3
  module DocContract
2
- class ApplicationController < ApplicationController
4
+ class ApplicationController < ::ApplicationController
3
5
  helper_method :query, :readme_html
4
6
 
5
7
  def main
8
+ # root landing action
6
9
  end
7
10
 
8
11
  private
@@ -1,3 +1,4 @@
1
+ # frozen_string_literal: true
1
2
  module DocContract
2
3
  class ContractInstancesController < ApplicationController
3
4
  include SharedTemplateAndInstanceMethods
@@ -7,23 +8,19 @@ module DocContract
7
8
  def index
8
9
  authorize! :index, record_class
9
10
  @q = record_class.ransack query
10
- @q.sorts = "created_at desc" if @q.sorts.empty?
11
+ @q.sorts = 'name asc' if @q.sorts.empty?
11
12
  @records = @q.result.includes(:contract_template).page(params[:page]).per(100)
12
13
  respond_with(@records)
13
14
  end
14
15
 
15
16
  def new
16
17
  @record = record_class.new
17
- end
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! :update, @record
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
@@ -7,23 +8,19 @@ module DocContract
7
8
  def index
8
9
  authorize! :index, record_class
9
10
  @q = record_class.ransack query
10
- @q.sorts = 'created_at desc' if @q.sorts.empty?
11
+ @q.sorts = 'name asc' if @q.sorts.empty?
11
12
  @records = @q.result.page(params[:page]).per(100)
12
13
  respond_with(@records)
13
14
  end
14
15
 
15
16
  def new
16
17
  @record = record_class.new
17
- end
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! :update, @record
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, options = {})
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
- title = t('action.index.label', models: model.model_name.human_plural).html_safe
83
- else
84
- title = t("action.#{args.first}.label", model: model.model_name.human).html_safe
85
- end
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, class: classes )
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(content_tag(:i,nil, class: 'folder open icon'), path || record, class: 'table-link show ui mini basic primary icon button')
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(content_tag(:i,nil, class: 'download icon'), path || [:download, record], class: 'table-link download ui mini violet icon button')
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(content_tag(:i, nil, class: 'trash icon'), path || record, method: :delete, data: { confirm: confirm_text }, class: 'table-link destroy ui mini negative icon button')
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 "ui positive message"
172
- when :error, :alert then "ui negative message"
173
- when :notice then "ui info message"
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
- object = YAML.load(value) rescue nil
5
- unless object
6
- record.errors.add attribute, (options[:message] || "is not valid YAML")
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,10 +1,17 @@
1
+ # frozen_string_literal: true
2
+
1
3
  module DocContract
2
4
  class ContractInstance < ApplicationRecord
3
- validates :name, presence: true
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
- own_config = YAML.load(config_yml.to_s) || {}
14
+ own_config = YAML.safe_load(config_yml.to_s, permitted_classes: [Symbol, Date]) || {}
8
15
  own_config.reverse_merge contract_template.config
9
16
  end
10
17
 
@@ -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"+result
37
+ "#{config.to_yaml}...\n\n#{result}"
31
38
  end
32
39
 
33
40
  def processed_html
@@ -1,13 +1,24 @@
1
+ # frozen_string_literal: true
1
2
  module DocContract
2
3
  class ContractTemplate < ApplicationRecord
3
- HANDLEBARS_REGEX = /{{((#)?[a-z\._ ]+)}}/
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
7
13
  validates :config_yml, yaml: true
8
14
 
9
15
  def config
10
- YAML.load(config_yml.to_s) || {}
16
+ YAML.safe_load(config_yml.to_s, permitted_classes: [Symbol, Date]) || {}
17
+ end
18
+
19
+ # Do not fail when an instance trait tries to access fallback template properties
20
+ def contract_template
21
+ self
11
22
  end
12
23
 
13
24
  def processed_markdown(config_addittions = {})
@@ -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"+result
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: :get_pdf), class: 'ui button'
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
- = link_to DocContract::ContractTemplate.model_name.human_plural, [DocContract::ContractTemplate], class: ['item', active_class('doc_contract/contract_templates')]
6
- = link_to DocContract::ContractInstance.model_name.human_plural, [DocContract::ContractInstance], class: ['item', active_class('doc_contract/contract_instances')]
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: .ui.input= f.text_field :name
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
@@ -6,8 +6,8 @@
6
6
  table.ui.compact.top.attached.striped.table
7
7
  thead
8
8
  tr
9
- th= at :name
10
- th= DocContract::ContractTemplate.model_name.human
9
+ th= sort_link @q, :name, at(:name), default_order: :asc
10
+ th= sort_link @q, :contract_template_name, DocContract::ContractTemplate.model_name.human
11
11
  th.column-filter.actions
12
12
  = search_result_info @records
13
13
  = search_row class: 'ui mini form' do
@@ -6,7 +6,7 @@
6
6
  table.ui.compact.top.attached.striped.table
7
7
  thead
8
8
  tr
9
- th= at :name
9
+ th= sort_link @q, :name, at(:name), default_order: :asc
10
10
  th.column-filter.actions
11
11
  = search_result_info @records
12
12
  = search_row class: 'ui mini form' do
@@ -0,0 +1,4 @@
1
+ # frozen_string_literal: true
2
+
3
+ ### NOTE:
4
+ # This is a dummy file to let the vim-rails vim plugin work with the rails engine
@@ -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 = Proc.new do |path|
6
- if path.present? and path['/'] # non namespaced model a/b/c => a
7
- unnamespaced_path = path.sub(/\.(\w+)\/[\w\\]+/, '.\1')
8
- I18n.t(unnamespaced_path, default: Proc.new{ human.pluralize })
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 :get_pdf
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 :get_pdf
14
+ get :pdf
14
15
  end
15
16
  end
16
17
  root 'application#main'
@@ -1,3 +1,4 @@
1
+ # frozen_string_literal: true
1
2
  class CreateDocContractContractTemplates < ActiveRecord::Migration[7.0]
2
3
  def change
3
4
  create_table :doc_contract_contract_templates do |t|
@@ -1,3 +1,4 @@
1
+ # frozen_string_literal: true
1
2
  class AddMarkdownToDocContractContractTemplates < ActiveRecord::Migration[7.0]
2
3
  def change
3
4
  add_column :doc_contract_contract_templates, :markdown, :text
@@ -1,3 +1,4 @@
1
+ # frozen_string_literal: true
1
2
  class AddConfigYmlToDocContractContractTemplates < ActiveRecord::Migration[7.0]
2
3
  def change
3
4
  add_column :doc_contract_contract_templates, :config_yml, :text
@@ -1,3 +1,4 @@
1
+ # frozen_string_literal: true
1
2
  class CreateDocContractContractInstances < ActiveRecord::Migration[7.0]
2
3
  def change
3
4
  create_table :doc_contract_contract_instances do |t|
@@ -1,3 +1,4 @@
1
+ # frozen_string_literal: true
1
2
  class AddTitlepageBackgroundToContractTemplates < ActiveRecord::Migration[7.0]
2
3
  def change
3
4
  add_column :doc_contract_contract_templates, :titlepage_background, :string
@@ -1,27 +1,42 @@
1
- require 'numbers_and_words'
1
+ # frozen_string_literal: true
2
2
  require 'cancancan'
3
- require 'ransack'
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
- initializer "doc_contract.assets.precompile" do |main_app|
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 << "doc_contract/application.css"
12
- main_app.config.assets.precompile << "doc_contract/application.js"
13
- main_app.config.doc_contract = ActiveSupport::OrderedOptions.new
14
- main_app.config.doc_contract.show_readme_on_main_page = true
15
- main_app.config.doc_contract.link_home_content = '<i class="arrow left icon"></i> Back'
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["db/migrate"].expanded.each do |expanded_path|
22
- app.config.paths["db/migrate"] << expanded_path
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 @@handlebars_renderer if class_variable_defined? :@@handlebars_renderer
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 = Proc.new do |context, value, currency_symbol|
19
- if Integer === value or value.to_i == value
20
- sprintf("#{currency_symbol} %.0f,-", value) #.gsub(/(\d)(?=\d{3}+,)/, '\1.')
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
- sprintf("#{currency_symbol} %.2f", value.to_f) #.gsub(/(\d)(?=\d{3}+,)/, '\1.')
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("&euro; %.2f", value.to_f) #.gsub(/(\d)(?=\d{3}+,)/, '\1.')
30
37
  # end
31
38
  #end
32
- eval_boolean = Proc.new do |context, values, compare_lambda|
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
- are_equal = compare_lambda.call(comparables)
38
- else
39
- are_equal = compare_lambda.call(*comparables)
40
- end
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, '&euro;' }
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) {|context, value, addition, *rest| value.to_f + addition.to_f }
51
- renderer.register_helper(:sum) {|context, *values, bkl| (values.first.is_a?(Array) ? values.first : values).map(&:to_f).sum }
52
- renderer.register_helper(:eq) { |context, *values| eval_boolean.call context, values, ->(vals){ vals.uniq.length <= 1 } }
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) { |context, *values| eval_boolean.call context, values, ->(a, b){ a > b } }
55
- renderer.register_helper(:includes) { |context, *values| eval_boolean.call context, values, ->(vals){ vals.first.include? vals.last } }
56
- renderer.register_helper(:map) do |context, array, key|
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
- array.map{|e| e[key.to_s] || e[key.to_sym]}
65
+
66
+ array.map{ |e| e[key.to_s] || e[key.to_sym] }
59
67
  end
60
- renderer.register_helper(:upcase) {|context, value| value.to_s.upcase }
61
- renderer.register_helper(:downcase) {|context, value| value.to_s.downcase }
62
- renderer.register_helper(:to_sentence) do |context, array, nester|
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) { |context, value| value.to_words }
71
- @@handlebars_renderer = renderer
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.each do |k|
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
@@ -1,3 +1,5 @@
1
+ # frozen_string_literal: true
2
+
1
3
  module DocContract
2
- VERSION = "0.1.5"
4
+ VERSION = '0.2.0'
3
5
  end
data/lib/doc_contract.rb CHANGED
@@ -1,6 +1,7 @@
1
- require "doc_contract/version"
2
- require "doc_contract/engine"
3
- require "doc_contract/handlebars"
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
 
@@ -1,3 +1,4 @@
1
+ # frozen_string_literal: true
1
2
  # desc "Explaining what the task does"
2
3
  # task :doc_contract do
3
4
  # # Task goes here
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.1.5
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: 2022-09-17 00:00:00.000000000 Z
11
+ date: 2023-08-07 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
- name: rails
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: ruby-handlebars
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: redcarpet
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: cancancan
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: ransack
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: kaminari
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: numbers_and_words
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: rspec-rails
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: :development
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: factory_bot_rails
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: jquery-rails
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: coffee-rails
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.3.7
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