benoit 0.1.0

Sign up to get free protection for your applications and to get access to all the features.
Files changed (164) hide show
  1. data/.gitignore +4 -0
  2. data/.gitmodules +3 -0
  3. data/.rspec +1 -0
  4. data/.rspec-turnip +1 -0
  5. data/.ruby-version +1 -0
  6. data/Assetfile +44 -0
  7. data/Gemfile +5 -0
  8. data/Gemfile.lock +110 -0
  9. data/Rakefile +9 -0
  10. data/benoit.gemspec +50 -0
  11. data/bin/benoit +121 -0
  12. data/bin/bundle-development +1 -0
  13. data/bin/bundle-sandbox +1 -0
  14. data/lib/benoit.rb +36 -0
  15. data/lib/benoit/cadenza.rb +11 -0
  16. data/lib/benoit/cadenza/output_filters.rb +21 -0
  17. data/lib/benoit/cleaner.rb +10 -0
  18. data/lib/benoit/compiler_error.rb +49 -0
  19. data/lib/benoit/configuration.rb +24 -0
  20. data/lib/benoit/current_site.rb +12 -0
  21. data/lib/benoit/file_wrapper_extensions.rb +13 -0
  22. data/lib/benoit/filters.rb +15 -0
  23. data/lib/benoit/filters/base_filter.rb +44 -0
  24. data/lib/benoit/filters/blacklist_filter.rb +18 -0
  25. data/lib/benoit/filters/cadenza_filter.rb +75 -0
  26. data/lib/benoit/filters/content_page_filter.rb +45 -0
  27. data/lib/benoit/filters/markdown_filter.rb +19 -0
  28. data/lib/benoit/filters/metadata_cleaner.rb +30 -0
  29. data/lib/benoit/filters/move_to_root_filter.rb +22 -0
  30. data/lib/benoit/filters/pagination_filter.rb +23 -0
  31. data/lib/benoit/filters/pass_thru_filter.rb +22 -0
  32. data/lib/benoit/filters/sass_filter.rb +74 -0
  33. data/lib/benoit/filters/set_metadata_filter.rb +15 -0
  34. data/lib/benoit/logger.rb +69 -0
  35. data/lib/benoit/page.rb +88 -0
  36. data/lib/benoit/page_metadata.rb +8 -0
  37. data/lib/benoit/page_metadata/container.rb +74 -0
  38. data/lib/benoit/page_metadata/json_converter.rb +28 -0
  39. data/lib/benoit/page_metadata/parser.rb +38 -0
  40. data/lib/benoit/page_metadata/store.rb +75 -0
  41. data/lib/benoit/pipeline.rb +1 -0
  42. data/lib/benoit/pipeline/dsl_extensions.rb +8 -0
  43. data/lib/benoit/pipeline/pagination_matcher.rb +53 -0
  44. data/lib/benoit/pipeline_project.rb +81 -0
  45. data/lib/benoit/site_context.rb +123 -0
  46. data/lib/benoit/utils/finds_layouts_for_template.rb +77 -0
  47. data/lib/benoit/utils/normalizes_path_to_template.rb +43 -0
  48. data/lib/benoit/utils/paginated_list.rb +102 -0
  49. data/lib/benoit/version.rb +3 -0
  50. data/lib/build_notifiers/file_built_notifier.rb +20 -0
  51. data/lib/build_notifiers/progress_notifier.rb +57 -0
  52. data/lib/cli.rb +39 -0
  53. data/spec/.rbenv-gemsets +1 -0
  54. data/spec/Gemfile +9 -0
  55. data/spec/Gemfile.lock +57 -0
  56. data/spec/bin/autospec +16 -0
  57. data/spec/bin/cucumber +16 -0
  58. data/spec/bin/htmldiff +16 -0
  59. data/spec/bin/ldiff +16 -0
  60. data/spec/bin/rspec +16 -0
  61. data/spec/features/build_command.feature +46 -0
  62. data/spec/features/frontmatter_metadata.feature +99 -0
  63. data/spec/features/javascript_files.feature +35 -0
  64. data/spec/features/output_filters.feature +20 -0
  65. data/spec/features/page_layouts.feature +72 -0
  66. data/spec/features/pagination.feature +58 -0
  67. data/spec/features/sass_files.feature +30 -0
  68. data/spec/features/version.feature +5 -0
  69. data/spec/lib/filters/base_filter_spec.rb +141 -0
  70. data/spec/lib/filters/markdown_filter_spec.rb +65 -0
  71. data/spec/lib/filters/sass_filter_spec.rb +73 -0
  72. data/spec/lib/metadata_json_converter_spec.rb +65 -0
  73. data/spec/lib/metadata_store_spec.rb +148 -0
  74. data/spec/lib/page_spec.rb +19 -0
  75. data/spec/lib/site_context_spec.rb +106 -0
  76. data/spec/spec_helper.rb +16 -0
  77. data/spec/steps/output_file_steps.rb +45 -0
  78. data/spec/steps/run_steps.rb +71 -0
  79. data/spec/steps/staticly_steps.rb +194 -0
  80. data/spec/support/aruba/rspec.rb +66 -0
  81. data/spec/support/files/img.png +0 -0
  82. data/spec/support/files/input.css.scss +7 -0
  83. data/spec/support/files/input.scss +7 -0
  84. data/spec/support/spec_helpers/file_helpers.rb +8 -0
  85. data/spec/support/spec_helpers/memory_file_wrapper.rb +43 -0
  86. data/spec/support/spec_helpers/memory_manifest.rb +19 -0
  87. data/spec/turnip_helper.rb +19 -0
  88. data/vendor/frontmatter/frontmatter.gemspec +24 -0
  89. data/vendor/frontmatter/lib/frontmatter.rb +92 -0
  90. data/vendor/frontmatter/lib/frontmatter/version.rb +3 -0
  91. data/vendor/rake-pipeline/.gitignore +18 -0
  92. data/vendor/rake-pipeline/.rspec +1 -0
  93. data/vendor/rake-pipeline/.travis.yml +12 -0
  94. data/vendor/rake-pipeline/.yardopts +2 -0
  95. data/vendor/rake-pipeline/GETTING_STARTED.md +268 -0
  96. data/vendor/rake-pipeline/Gemfile +14 -0
  97. data/vendor/rake-pipeline/LICENSE +20 -0
  98. data/vendor/rake-pipeline/README.markdown +11 -0
  99. data/vendor/rake-pipeline/README.yard +178 -0
  100. data/vendor/rake-pipeline/Rakefile +21 -0
  101. data/vendor/rake-pipeline/bin/rakep +4 -0
  102. data/vendor/rake-pipeline/examples/copying_files.md +12 -0
  103. data/vendor/rake-pipeline/examples/minifying_files.md +37 -0
  104. data/vendor/rake-pipeline/examples/modifying_pipelines.md +67 -0
  105. data/vendor/rake-pipeline/examples/multiple_pipelines.md +77 -0
  106. data/vendor/rake-pipeline/lib/generators/rake/pipeline/install/install_generator.rb +70 -0
  107. data/vendor/rake-pipeline/lib/rake-pipeline.rb +509 -0
  108. data/vendor/rake-pipeline/lib/rake-pipeline/cli.rb +57 -0
  109. data/vendor/rake-pipeline/lib/rake-pipeline/dsl.rb +9 -0
  110. data/vendor/rake-pipeline/lib/rake-pipeline/dsl/pipeline_dsl.rb +246 -0
  111. data/vendor/rake-pipeline/lib/rake-pipeline/dsl/project_dsl.rb +108 -0
  112. data/vendor/rake-pipeline/lib/rake-pipeline/dynamic_file_task.rb +194 -0
  113. data/vendor/rake-pipeline/lib/rake-pipeline/error.rb +17 -0
  114. data/vendor/rake-pipeline/lib/rake-pipeline/file_wrapper.rb +195 -0
  115. data/vendor/rake-pipeline/lib/rake-pipeline/filter.rb +267 -0
  116. data/vendor/rake-pipeline/lib/rake-pipeline/filters.rb +4 -0
  117. data/vendor/rake-pipeline/lib/rake-pipeline/filters/concat_filter.rb +63 -0
  118. data/vendor/rake-pipeline/lib/rake-pipeline/filters/gsub_filter.rb +56 -0
  119. data/vendor/rake-pipeline/lib/rake-pipeline/filters/ordering_concat_filter.rb +38 -0
  120. data/vendor/rake-pipeline/lib/rake-pipeline/filters/pipeline_finalizing_filter.rb +21 -0
  121. data/vendor/rake-pipeline/lib/rake-pipeline/graph.rb +178 -0
  122. data/vendor/rake-pipeline/lib/rake-pipeline/manifest.rb +82 -0
  123. data/vendor/rake-pipeline/lib/rake-pipeline/manifest_entry.rb +34 -0
  124. data/vendor/rake-pipeline/lib/rake-pipeline/matcher.rb +141 -0
  125. data/vendor/rake-pipeline/lib/rake-pipeline/middleware.rb +73 -0
  126. data/vendor/rake-pipeline/lib/rake-pipeline/precompile.rake +8 -0
  127. data/vendor/rake-pipeline/lib/rake-pipeline/project.rb +338 -0
  128. data/vendor/rake-pipeline/lib/rake-pipeline/rails_plugin.rb +10 -0
  129. data/vendor/rake-pipeline/lib/rake-pipeline/railtie.rb +34 -0
  130. data/vendor/rake-pipeline/lib/rake-pipeline/reject_matcher.rb +29 -0
  131. data/vendor/rake-pipeline/lib/rake-pipeline/server.rb +15 -0
  132. data/vendor/rake-pipeline/lib/rake-pipeline/sorted_pipeline.rb +19 -0
  133. data/vendor/rake-pipeline/lib/rake-pipeline/version.rb +6 -0
  134. data/vendor/rake-pipeline/rails/init.rb +2 -0
  135. data/vendor/rake-pipeline/rake-pipeline.gemspec +24 -0
  136. data/vendor/rake-pipeline/spec/cli_spec.rb +73 -0
  137. data/vendor/rake-pipeline/spec/concat_filter_spec.rb +37 -0
  138. data/vendor/rake-pipeline/spec/dsl/pipeline_dsl_spec.rb +165 -0
  139. data/vendor/rake-pipeline/spec/dsl/project_dsl_spec.rb +41 -0
  140. data/vendor/rake-pipeline/spec/dynamic_file_task_spec.rb +119 -0
  141. data/vendor/rake-pipeline/spec/encoding_spec.rb +106 -0
  142. data/vendor/rake-pipeline/spec/file_wrapper_spec.rb +132 -0
  143. data/vendor/rake-pipeline/spec/filter_spec.rb +367 -0
  144. data/vendor/rake-pipeline/spec/graph_spec.rb +56 -0
  145. data/vendor/rake-pipeline/spec/gsub_filter_spec.rb +87 -0
  146. data/vendor/rake-pipeline/spec/manifest_entry_spec.rb +46 -0
  147. data/vendor/rake-pipeline/spec/manifest_spec.rb +67 -0
  148. data/vendor/rake-pipeline/spec/matcher_spec.rb +141 -0
  149. data/vendor/rake-pipeline/spec/middleware_spec.rb +199 -0
  150. data/vendor/rake-pipeline/spec/ordering_concat_filter_spec.rb +42 -0
  151. data/vendor/rake-pipeline/spec/pipeline_spec.rb +232 -0
  152. data/vendor/rake-pipeline/spec/project_spec.rb +295 -0
  153. data/vendor/rake-pipeline/spec/rake_acceptance_spec.rb +720 -0
  154. data/vendor/rake-pipeline/spec/rake_tasks_spec.rb +21 -0
  155. data/vendor/rake-pipeline/spec/reject_matcher_spec.rb +31 -0
  156. data/vendor/rake-pipeline/spec/sorted_pipeline_spec.rb +27 -0
  157. data/vendor/rake-pipeline/spec/spec_helper.rb +38 -0
  158. data/vendor/rake-pipeline/spec/support/spec_helpers/file_utils.rb +35 -0
  159. data/vendor/rake-pipeline/spec/support/spec_helpers/filters.rb +37 -0
  160. data/vendor/rake-pipeline/spec/support/spec_helpers/input_helpers.rb +23 -0
  161. data/vendor/rake-pipeline/spec/support/spec_helpers/memory_file_wrapper.rb +35 -0
  162. data/vendor/rake-pipeline/spec/support/spec_helpers/memory_manifest.rb +19 -0
  163. data/vendor/rake-pipeline/tools/perfs +101 -0
  164. metadata +497 -0
@@ -0,0 +1,15 @@
1
+ module Benoit::Filters
2
+ class SetMetadataFilter < Rake::Pipeline::Filter
3
+ def initialize(metadata={}, &block)
4
+ @metadata = metadata
5
+ super &block
6
+ end
7
+
8
+ def generate_output(inputs, output)
9
+ inputs.each do |input|
10
+ FrontMatterStore.set_metadata_for_page input.path, @metadata
11
+ output.write(input.read)
12
+ end
13
+ end
14
+ end
15
+ end
@@ -0,0 +1,69 @@
1
+ require 'syslog'
2
+
3
+ module Benoit
4
+ class Logger
5
+
6
+ def self.report_error(ex)
7
+ original_error = ex.original_error if ex.respond_to? :original_error
8
+
9
+ sub_message =
10
+ if original_error
11
+ "#{original_error.message}\n#{original_error.backtrace.join("\n")}"
12
+ else
13
+ ex.backtrace.join("\n")
14
+ end
15
+
16
+ message = <<-EOM
17
+ Benoit encountered an error.
18
+
19
+ #{ex.message}
20
+ #{sub_message}
21
+
22
+ #{ENV.map { |k,v| "#{k}=#{v}" }.join("\n")}
23
+ EOM
24
+
25
+ error message
26
+
27
+ if Benoit.config.output_mode == :app
28
+ $stderr.puts ex.to_json
29
+ else
30
+ $stderr.puts message
31
+ end
32
+ rescue StandardError => ex
33
+ if Benoit.config.output_mode == :app
34
+ $stderr.puts({ line_no: nil, message: ex.message, path: __FILE__ }.to_json)
35
+ else
36
+ $stderr.puts ex.message
37
+ $stderr.puts ex.backtrace
38
+ end
39
+ end
40
+
41
+ def self.critical(msg)
42
+ self.log(:crit, msg)
43
+ end
44
+
45
+ def self.error(msg)
46
+ self.log(:err, msg)
47
+ end
48
+
49
+ def self.warn(msg)
50
+ self.log(:warning, msg)
51
+ end
52
+
53
+ def self.emergency(*msg)
54
+ self.log(:emerg, msg)
55
+ end
56
+
57
+ def self.notice(*msg)
58
+ self.log(:notice, msg)
59
+ end
60
+
61
+ def self.log(level, msg)
62
+ logger.send(level, '%s', msg)
63
+ end
64
+
65
+ def self.logger
66
+ @logger ||= Syslog.open("com.densitypop.Benoit", Syslog::LOG_CONS, Syslog::LOG_USER)
67
+ end
68
+ end
69
+ end
@@ -0,0 +1,88 @@
1
+ module Benoit
2
+ class Page
3
+ attr_reader :attrs_hash, :siteContext, :permalink
4
+
5
+ def self.from_hash(page_attrs)
6
+ new(page_attrs)
7
+ end
8
+
9
+ def initialize(attrs)
10
+ @attr_names = @attrNames = []
11
+ @original_path = attrs["_original_path"]
12
+ setFileType!(attrs)
13
+ set_permalink!(attrs)
14
+ @metadata = methodsFromAttrs(attrs)
15
+ end
16
+
17
+ def template
18
+ # For compat with other site generators
19
+ @template || @layout
20
+ end
21
+
22
+ def has_value?(value)
23
+ @metadata.has_value?(value)
24
+ end
25
+
26
+ def key?(key)
27
+ @metadata.key?(key)
28
+ end
29
+
30
+ def [](key)
31
+ @metadata[key]
32
+ end
33
+
34
+ def []=(key, value)
35
+ instance_variable_set("@#{key}", value)
36
+ @metadata[key] = value
37
+ end
38
+
39
+ def keys
40
+ @metadata.keys
41
+ end
42
+
43
+ def set_permalink!(attrs)
44
+ @permalink = "/#{@original_path}"
45
+ @attrNames << :permalink
46
+ end
47
+
48
+ def to_json(options={})
49
+ attrsFromMethods(@attrNames).to_json
50
+ end
51
+
52
+ def to_hash(options={})
53
+ attrsFromMethods(@attrNames)
54
+ end
55
+
56
+ private
57
+
58
+ def setFileType!(attrs)
59
+ attrs["_type"] = attrs["type"] || "page"
60
+ end
61
+
62
+ def methodsFromAttrs(attrs)
63
+ self.class.send :attr_reader, *attrs.keys.map(&:to_sym)
64
+ attrs.each do |k,v|
65
+ @attrNames << k.to_sym
66
+ self.instance_variable_set "@#{k}", v
67
+ end
68
+ end
69
+
70
+ def attrsFromMethods(attrs, &transformer)
71
+ attrs.inject({}) do |acc,attr,v|
72
+ value = send attr
73
+ acc[attr] = value
74
+ acc
75
+ end
76
+ end
77
+
78
+ def pagination_config(attrs, &block)
79
+ attrs.select do |k,v|
80
+ keys = k.to_s.scan(/(\w+)_per_page/)
81
+ if keys.any?
82
+ block.(v)
83
+ end
84
+ end
85
+ end
86
+
87
+ end
88
+ end
@@ -0,0 +1,8 @@
1
+ module Benoit
2
+ module PageMetadata
3
+ require_relative "./page_metadata/store"
4
+ require_relative "./page_metadata/container"
5
+ require_relative "./page_metadata/json_converter"
6
+ require_relative "./page_metadata/parser"
7
+ end
8
+ end
@@ -0,0 +1,74 @@
1
+ module Benoit
2
+ module PageMetadata
3
+ class Container
4
+
5
+ def self.[](hash)
6
+ hash.inject(new) do |inst,(key,value)|
7
+ inst[key] = value
8
+ inst
9
+ end
10
+ end
11
+
12
+ def []=(key,value)
13
+ create_method key do
14
+ value
15
+ end
16
+ end
17
+
18
+ def [](key)
19
+ return unless respond_to? key
20
+ send key
21
+ end
22
+
23
+ def fetch(key)
24
+ raise KeyError.new(key) unless key?(key)
25
+ self[key]
26
+ end
27
+
28
+ def keys
29
+ singleton_class.instance_methods(false)
30
+ end
31
+
32
+ def values
33
+ keys.map do |key|
34
+ send key
35
+ end
36
+ end
37
+
38
+ def delete(key)
39
+ destroy_method(key)
40
+ end
41
+
42
+ def clear
43
+ keys.each do |key|
44
+ delete key
45
+ end
46
+ end
47
+
48
+ def to_hash
49
+ Hash[keys.zip(values)]
50
+ end
51
+
52
+ def key?(key)
53
+ respond_to? key
54
+ end
55
+ alias has_key? key?
56
+
57
+ private
58
+
59
+ def create_method(name, &body)
60
+ singleton_class.instance_eval do
61
+ define_method name, &body
62
+ end
63
+ end
64
+
65
+ def destroy_method(name)
66
+ singleton_class.instance_eval do
67
+ remove_method name
68
+ end
69
+ end
70
+
71
+ end
72
+ end
73
+ end
74
+
@@ -0,0 +1,28 @@
1
+ module Benoit
2
+ module PageMetadata
3
+ class JsonConverter
4
+
5
+ def self.import!(json, store=Store.current)
6
+ hash = JSON.parse(json)
7
+ store.import_all!(hash)
8
+ end
9
+
10
+ def self.export(store=Store.current)
11
+ store.to_hash.to_json
12
+ end
13
+
14
+ end
15
+
16
+ class SiteContextConverter
17
+
18
+ def self.export(store=Store.current)
19
+ store.to_hash.inject({}) do |ctxt,(key,metadata)|
20
+ new_key = store.path_from_key(key)
21
+ ctxt.merge(new_key => metadata)
22
+ end
23
+ end
24
+
25
+ end
26
+ end
27
+ end
28
+
@@ -0,0 +1,38 @@
1
+ require 'psych'
2
+ require 'frontmatter'
3
+ require 'kronic'
4
+
5
+ module Benoit
6
+ module PageMetadata
7
+ class Parser
8
+
9
+ KEY_CONVERTERS = {
10
+ "date" => ->(value){
11
+ Kronic.parse(value)
12
+ }
13
+ }
14
+
15
+ def self.should_parse?(path)
16
+ FrontMatter.file_might_have_frontmatter?(path) ||
17
+ FrontMatter.file_has_frontmatter?(path)
18
+ end
19
+
20
+ def self.parse(input)
21
+ content = input.read
22
+ if FrontMatter.has_frontmatter?(content)
23
+ response = FrontMatter.parse(content)
24
+ response.merge(response) do |key,original_value|
25
+ if KEY_CONVERTERS.key?(key)
26
+ KEY_CONVERTERS[key].(original_value)
27
+ else
28
+ original_value
29
+ end
30
+ end
31
+ else
32
+ {}
33
+ end
34
+ end
35
+
36
+ end
37
+ end
38
+ end
@@ -0,0 +1,75 @@
1
+ require 'singleton'
2
+
3
+ module Benoit
4
+ module PageMetadata
5
+
6
+ class Store
7
+ include Singleton
8
+
9
+ class << self
10
+ alias current instance
11
+ end
12
+
13
+ def initialize
14
+ @container = container_class.new
15
+ end
16
+
17
+ def import_from_page(input)
18
+ metadata = Parser.parse(input)
19
+ metadata["_original_path"] = input.path
20
+ import_metadata(input.path, metadata)
21
+ end
22
+
23
+ def import_all!(hash)
24
+ @container = container_class[hash]
25
+ end
26
+
27
+ def import_metadata(path, metadata)
28
+ key = path_to_key(path)
29
+ if @container.respond_to?(:key?) and @container.key?(key)
30
+ @container[key]
31
+ else
32
+ @container ||= container_class.new
33
+ @container[key] = metadata
34
+ end
35
+ end
36
+
37
+ def [](wrapper)
38
+ import_from_page(wrapper) unless has_page?(wrapper.path)
39
+ @container[path_to_key(wrapper.path)] || {}
40
+ end
41
+
42
+ def has_page?(page_name)
43
+ return unless @container
44
+ @container.key?(path_to_key(page_name))
45
+ end
46
+
47
+ def to_hash
48
+ return unless @container
49
+ @container.to_hash
50
+ end
51
+
52
+ def expire!
53
+ @container.clear
54
+ @container = container_class.new
55
+ end
56
+
57
+ def path_from_key(key)
58
+ key.to_s.sub /__(.+)_metadata/ do |match,blah|
59
+ $1
60
+ end
61
+ end
62
+
63
+ def path_to_key(path)
64
+ :"__#{path}_metadata"
65
+ end
66
+
67
+ private
68
+
69
+ def container_class
70
+ Class.new(Container)
71
+ end
72
+
73
+ end
74
+ end
75
+ end
@@ -0,0 +1 @@
1
+ require_relative "./pipeline/pagination_matcher"
@@ -0,0 +1,8 @@
1
+ class Rake::Pipeline::DSL::PipelineDSL
2
+ def needs_pagination(pattern, &block)
3
+ matcher = pipeline.copy Benoit::Pipeline::PaginationMatcher, &block
4
+ matcher.glob = pattern
5
+ matcher.add_filter Benoit::Filters::PaginationFilter.new
6
+ pipeline.add_filter matcher
7
+ end
8
+ end
@@ -0,0 +1,53 @@
1
+ module Benoit
2
+ module Pipeline
3
+ class PaginationMatcher < Rake::Pipeline::Matcher
4
+ attr_reader :previous_input_files, :per_page_counts, :collection_totals
5
+
6
+ def setup_filters
7
+ @previous_input_files = pipeline.input_files
8
+ calculate_collection_totals
9
+ calculate_per_page_counts
10
+ super
11
+ end
12
+
13
+ def output_files
14
+ super + input_files
15
+ end
16
+
17
+ def eligible_input_files
18
+ per_page_counts.keys
19
+ end
20
+
21
+ private
22
+
23
+ def calculate_collection_totals
24
+ @collection_totals ||=
25
+ previous_input_files.group_by do |i|
26
+ i.read.scan(/type: (\w+)/).flatten.first
27
+ end.delete_if do |key,_|
28
+ key.nil?
29
+ end.reduce({}) do |acc,(key,val)|
30
+ acc.merge(key => val.length)
31
+ end
32
+ end
33
+
34
+ def calculate_per_page_counts
35
+ @per_page_counts =
36
+ previous_input_files.reduce([]) do |acc,i|
37
+ collection, count = i.read.scan(/(\w+)_per_page: (\d+)/).flatten
38
+ if collection
39
+ count = count.to_i
40
+ Benoit::SiteContext.paginate_collection collection, count
41
+ collection = Inflecto.singularize(collection)
42
+ acc << [i, collection, count]
43
+ end
44
+ acc
45
+ end.reduce({}) do |acc,(input,collection,count)|
46
+ total = collection_totals[collection]
47
+ acc.merge(input => total / count + total % count)
48
+ end
49
+ end
50
+
51
+ end
52
+ end
53
+ end