bridgetown-core 0.17.0 → 0.18.3

Sign up to get free protection for your applications and to get access to all the features.
Files changed (48) hide show
  1. checksums.yaml +4 -4
  2. data/.yardopts +1 -0
  3. data/bridgetown-core.gemspec +1 -0
  4. data/lib/bridgetown-core.rb +44 -29
  5. data/lib/bridgetown-core/collection.rb +5 -1
  6. data/lib/bridgetown-core/commands/apply.rb +2 -2
  7. data/lib/bridgetown-core/commands/new.rb +1 -1
  8. data/lib/bridgetown-core/concerns/layout_placeable.rb +1 -1
  9. data/lib/bridgetown-core/concerns/liquid_renderable.rb +10 -0
  10. data/lib/bridgetown-core/concerns/site/configurable.rb +21 -23
  11. data/lib/bridgetown-core/concerns/site/content.rb +46 -33
  12. data/lib/bridgetown-core/concerns/site/extensible.rb +14 -13
  13. data/lib/bridgetown-core/concerns/site/localizable.rb +6 -2
  14. data/lib/bridgetown-core/concerns/site/processable.rb +12 -15
  15. data/lib/bridgetown-core/concerns/site/renderable.rb +34 -26
  16. data/lib/bridgetown-core/concerns/site/writable.rb +7 -15
  17. data/lib/bridgetown-core/concerns/validatable.rb +2 -2
  18. data/lib/bridgetown-core/configuration.rb +10 -4
  19. data/lib/bridgetown-core/converter.rb +0 -42
  20. data/lib/bridgetown-core/converters/erb_templates.rb +80 -16
  21. data/lib/bridgetown-core/converters/liquid_templates.rb +103 -0
  22. data/lib/bridgetown-core/converters/markdown.rb +0 -3
  23. data/lib/bridgetown-core/document.rb +34 -21
  24. data/lib/bridgetown-core/drops/site_drop.rb +4 -0
  25. data/lib/bridgetown-core/drops/unified_payload_drop.rb +0 -1
  26. data/lib/bridgetown-core/drops/url_drop.rb +19 -3
  27. data/lib/bridgetown-core/excerpt.rb +1 -1
  28. data/lib/bridgetown-core/filters.rb +36 -7
  29. data/lib/bridgetown-core/generators/prototype_generator.rb +42 -25
  30. data/lib/bridgetown-core/helpers.rb +84 -0
  31. data/lib/bridgetown-core/liquid_renderer.rb +1 -1
  32. data/lib/bridgetown-core/log_writer.rb +2 -2
  33. data/lib/bridgetown-core/page.rb +8 -2
  34. data/lib/bridgetown-core/plugin_manager.rb +34 -1
  35. data/lib/bridgetown-core/reader.rb +1 -4
  36. data/lib/bridgetown-core/readers/data_reader.rb +3 -3
  37. data/lib/bridgetown-core/readers/defaults_reader.rb +1 -1
  38. data/lib/bridgetown-core/readers/post_reader.rb +28 -15
  39. data/lib/bridgetown-core/renderer.rb +42 -162
  40. data/lib/bridgetown-core/ruby_template_view.rb +26 -26
  41. data/lib/bridgetown-core/site.rb +12 -2
  42. data/lib/bridgetown-core/tags/render_content.rb +2 -2
  43. data/lib/bridgetown-core/utils.rb +14 -0
  44. data/lib/bridgetown-core/utils/ruby_exec.rb +1 -1
  45. data/lib/bridgetown-core/version.rb +2 -2
  46. data/lib/bridgetown-core/watcher.rb +1 -0
  47. data/lib/site_template/src/images/.keep +1 -0
  48. metadata +21 -3
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: bd231215591b9251d63525c740620bf4cd6da0beb5e48abd420ce745355ac0bb
4
- data.tar.gz: 82dff2c41efbac2e9ea4f60cc441f8665044c3c8e6576695a50db376951c5866
3
+ metadata.gz: aff1b7af860afa9442e9a9cc6f34b0dd48fa6677248a118acc794a60de6f6328
4
+ data.tar.gz: 3cfc657c9c5958cdf45cddf6bac5b1c84de276a8b10000b6b7a4c28d977f4ec1
5
5
  SHA512:
6
- metadata.gz: 4606e45f6a843e7de6e9e81c873cb5082ca958277631d83dd260b0eef64289e8c93259d771fee5501dd5c7ec92747d278faffbaf3802ee72457e300aee96714e
7
- data.tar.gz: 82cd06e0dd5a11061d2c37d3defb8d57e381f4129abed12bd47fa4af8c2bbaee3372c3aac09fcf3b9b6fd92a8152dd458b79da710fc575c8e91096946e5ced87
6
+ metadata.gz: ab2136623a924cead3223313b7ccd2cf46ffcc3c1c34de6096162d309e7e91ae2e1ce7af61abba4bc564cef67906977efead5780a27e5c4c407f14c2250e591e
7
+ data.tar.gz: 0a6a770a52c1d23906c5bfbe7fc45a60666dcdb25359eb184585594d94ef4de9ef97c4587b4272a3dc95f1a3a2bdeb8b9327ab92b621a0a9672e3ec33aeb7b83
@@ -0,0 +1 @@
1
+ -m markdown
@@ -38,6 +38,7 @@ Gem::Specification.new do |s|
38
38
  s.add_runtime_dependency("erubi", "~> 1.9")
39
39
  s.add_runtime_dependency("faraday", "~> 1.0")
40
40
  s.add_runtime_dependency("faraday_middleware", "~> 1.0")
41
+ s.add_runtime_dependency("hash_with_dot_access", "~> 1.0")
41
42
  s.add_runtime_dependency("i18n", "~> 1.0")
42
43
  s.add_runtime_dependency("kramdown", "~> 2.1")
43
44
  s.add_runtime_dependency("kramdown-parser-gfm", "~> 1.0")
@@ -29,8 +29,9 @@ require "csv"
29
29
  require "json"
30
30
 
31
31
  # 3rd party
32
- require "active_support/core_ext/hash/indifferent_access"
32
+ require "active_support/core_ext/object/blank"
33
33
  require "active_support/core_ext/string/inflections"
34
+ require "hash_with_dot_access"
34
35
  require "pathutil"
35
36
  require "addressable/uri"
36
37
  require "safe_yaml/load"
@@ -114,24 +115,23 @@ module Bridgetown
114
115
  require_all "bridgetown-core/tags"
115
116
 
116
117
  class << self
117
- # Public: Tells you which Bridgetown environment you are building in so
118
- # you can skip tasks if you need to.
119
-
118
+ # Tells you which Bridgetown environment you are building in so
119
+ # you can skip tasks if you need to.
120
120
  def environment
121
121
  ENV["BRIDGETOWN_ENV"] || "development"
122
122
  end
123
123
  alias_method :env, :environment
124
124
 
125
- # Public: Generate a Bridgetown configuration Hash by merging the default
126
- # options with anything in bridgetown.config.yml, and adding the given
127
- # options on top.
125
+ # Generate a Bridgetown configuration hash by merging the default
126
+ # options with anything in bridgetown.config.yml, and adding the given
127
+ # options on top.
128
128
  #
129
- # override - A Hash of config directives that override any options in both
130
- # the defaults and the config file.
131
- # See Bridgetown::Configuration::DEFAULTS for a
132
- # list of option names and their defaults.
129
+ # @param override [Hash] - A an optional hash of config directives that override
130
+ # any options in both the defaults and the config file. See
131
+ # {Bridgetown::Configuration::DEFAULTS} for a list of option names and their
132
+ # defaults.
133
133
  #
134
- # Returns the final configuration Hash.
134
+ # @return [Hash] The final configuration hash.
135
135
  def configuration(override = {})
136
136
  config = Configuration.new
137
137
  override = Configuration[override].stringify_keys
@@ -146,15 +146,29 @@ module Bridgetown
146
146
  end
147
147
 
148
148
  # Conveinence method to register a new Thor command
149
+ #
150
+ # @see Bridgetown::Commands::Registrations.register
149
151
  def register_command(&block)
150
152
  Bridgetown::Commands::Registrations.register(&block)
151
153
  end
152
154
 
153
- # Public: Set the TZ environment variable to use the timezone specified
155
+ # Determines the correct Bundler environment block method to use and passes
156
+ # the block on to it.
157
+ #
158
+ # @return [void]
159
+ def with_unbundled_env(&block)
160
+ if Bundler.bundler_major_version >= 2
161
+ Bundler.method(:with_unbundled_env).call(&block)
162
+ else
163
+ Bundler.method(:with_clean_env).call(&block)
164
+ end
165
+ end
166
+
167
+ # Set the TZ environment variable to use the timezone specified
154
168
  #
155
- # timezone - the IANA Time Zone
169
+ # @param timezone [String] the IANA Time Zone
156
170
  #
157
- # Returns nothing
171
+ # @return [void]
158
172
  # rubocop:disable Naming/AccessorMethodName
159
173
  def set_timezone(timezone)
160
174
  ENV["TZ"] = if Utils::Platforms.really_windows?
@@ -165,38 +179,39 @@ module Bridgetown
165
179
  end
166
180
  # rubocop:enable Naming/AccessorMethodName
167
181
 
168
- # Public: Fetch the logger instance for this Bridgetown process.
182
+ # Fetch the logger instance for this Bridgetown process.
169
183
  #
170
- # Returns the LogAdapter instance.
184
+ # @return [LogAdapter]
171
185
  def logger
172
186
  @logger ||= LogAdapter.new(LogWriter.new, (ENV["BRIDGETOWN_LOG_LEVEL"] || :info).to_sym)
173
187
  end
174
188
 
175
- # Public: Set the log writer.
176
- # New log writer must respond to the same methods
177
- # as Ruby's interal Logger.
189
+ # Set the log writer. New log writer must respond to the same methods as Ruby's
190
+ # internal Logger.
178
191
  #
179
- # writer - the new Logger-compatible log transport
192
+ # @param writer [Object] the new Logger-compatible log transport
180
193
  #
181
- # Returns the new logger.
194
+ # @return [LogAdapter]
182
195
  def logger=(writer)
183
196
  @logger = LogAdapter.new(writer, (ENV["BRIDGETOWN_LOG_LEVEL"] || :info).to_sym)
184
197
  end
185
198
 
186
- # Public: An array of sites
199
+ # An array of sites. Currently only ever a single entry.
187
200
  #
188
- # Returns the Bridgetown sites created.
201
+ # @return [Array<Bridgetown::Site>] the Bridgetown sites created.
189
202
  def sites
190
203
  @sites ||= []
191
204
  end
192
205
 
193
- # Public: Ensures the questionable path is prefixed with the base directory
194
- # and prepends the questionable path with the base directory if false.
206
+ # Ensures the questionable path is prefixed with the base directory
207
+ # and prepends the questionable path with the base directory if false.
195
208
  #
196
- # base_directory - the directory with which to prefix the questionable path
197
- # questionable_path - the path we're unsure about, and want prefixed
209
+ # @param base_directory [String] the directory with which to prefix the
210
+ # questionable path
211
+ # @param questionable_path [String] the path we're unsure about, and want
212
+ # prefixed
198
213
  #
199
- # Returns the sanitized path.
214
+ # @return [String] the sanitized path
200
215
  def sanitized_path(base_directory, questionable_path)
201
216
  return base_directory if base_directory.eql?(questionable_path)
202
217
 
@@ -2,7 +2,10 @@
2
2
 
3
3
  module Bridgetown
4
4
  class Collection
5
- attr_reader :site, :label, :metadata
5
+ # @return [Bridgetown::Site]
6
+ attr_reader :site
7
+
8
+ attr_reader :label, :metadata
6
9
  attr_writer :docs
7
10
 
8
11
  # Create a new Collection.
@@ -65,6 +68,7 @@ module Bridgetown
65
68
  read_static_file(file_path, full_path)
66
69
  end
67
70
  end
71
+ site.static_files.concat(files)
68
72
  sort_docs!
69
73
  end
70
74
 
@@ -44,7 +44,7 @@ module Bridgetown
44
44
 
45
45
  def apply_after_new_command
46
46
  # Coming from the new command, so set up proper bundler env
47
- Bundler.with_clean_env do
47
+ Bridgetown.with_unbundled_env do
48
48
  self.destination_root = New.created_site_dir
49
49
  inside(New.created_site_dir) do
50
50
  apply_from_url options[:apply]
@@ -62,7 +62,7 @@ module Bridgetown
62
62
  " current folder."
63
63
  end
64
64
 
65
- Bundler.with_clean_env do
65
+ Bridgetown.with_unbundled_env do
66
66
  apply_from_url automation_command
67
67
  end
68
68
  rescue ArgumentError => e
@@ -115,7 +115,7 @@ module Bridgetown
115
115
  def bundle_install(path)
116
116
  unless Bridgetown.environment == "test"
117
117
  require "bundler"
118
- Bundler.with_clean_env do
118
+ Bridgetown.with_unbundled_env do
119
119
  inside(path) do
120
120
  run "bundle install", abort_on_failure: true
121
121
  end
@@ -11,7 +11,7 @@ module Bridgetown
11
11
  end
12
12
 
13
13
  def no_layout?
14
- data["layout"] == "none"
14
+ data["layout"] == "none" || data["layout"] == false
15
15
  end
16
16
  end
17
17
  end
@@ -8,10 +8,20 @@ module Bridgetown
8
8
  # contain any Liquid Tags or Variables, true otherwise.
9
9
  def render_with_liquid?
10
10
  return false if data["render_with_liquid"] == false
11
+ return false unless liquid_engine_configured?
11
12
 
12
13
  !(yaml_file? || !Utils.has_liquid_construct?(content))
13
14
  end
14
15
 
16
+ def liquid_engine_configured?
17
+ data["template_engine"] == "liquid" ||
18
+ (
19
+ data["template_engine"].nil? && (
20
+ site.config[:template_engine].nil? || site.config[:template_engine] == "liquid"
21
+ )
22
+ )
23
+ end
24
+
15
25
  # Override in individual classes
16
26
  def yaml_file?
17
27
  false
@@ -1,18 +1,16 @@
1
1
  # frozen_string_literal: true
2
2
 
3
- module Bridgetown
4
- module Site::Configurable
3
+ class Bridgetown::Site
4
+ module Configurable
5
5
  # Set the site's configuration. This handles side-effects caused by
6
- # changing values in the configuration.
6
+ # changing values in the configuration.
7
7
  #
8
- # @param config [Bridgetown::Configuration]
9
- # An instance of {Bridgetown::Configuration},
8
+ # @param config [Configuration]
9
+ # An instance of {Configuration},
10
10
  # containing the new configuration.
11
11
  #
12
- # @return [Bridgetown::Configuration]
13
- # A new instance of {Bridgetown::Configuration}
14
- #
15
- # @see Bridgetown::Configuration
12
+ # @return [Configuration]
13
+ # The processed instance of {Configuration}
16
14
  def config=(config)
17
15
  @config = config.clone
18
16
 
@@ -38,21 +36,21 @@ module Bridgetown
38
36
  end
39
37
 
40
38
  def defaults_reader
41
- @defaults_reader ||= DefaultsReader.new(self)
39
+ @defaults_reader ||= Bridgetown::DefaultsReader.new(self)
42
40
  end
43
41
 
44
42
  # Returns the current instance of {FrontmatterDefaults} or
45
- # creates a new instance {FrontmatterDefaults} if it doesn't already exist.
43
+ # creates a new instance {FrontmatterDefaults} if it doesn't already exist.
46
44
  #
47
45
  # @return [FrontmatterDefaults]
48
46
  # Returns an instance of {FrontmatterDefaults}
49
47
  def frontmatter_defaults
50
- @frontmatter_defaults ||= FrontmatterDefaults.new(self)
48
+ @frontmatter_defaults ||= Bridgetown::FrontmatterDefaults.new(self)
51
49
  end
52
50
 
53
51
  # Whether to perform a full rebuild without incremental regeneration.
54
- # If either +override+["incremental"] or +config+["incremental"] are true,
55
- # fully rebuild the site. If not, incrementally build the site.
52
+ # If either `override["incremental"]` or `config["incremental"]` are true,
53
+ # fully rebuild the site. If not, incrementally build the site.
56
54
  #
57
55
  # @param [Hash] override
58
56
  # An override hash to override the current config value
@@ -63,11 +61,11 @@ module Bridgetown
63
61
  end
64
62
 
65
63
  # Returns the current instance of {Publisher} or creates a new instance of
66
- # {Publisher} if one doesn't exist.
64
+ # {Publisher} if one doesn't exist.
67
65
  #
68
66
  # @return [Publisher] Returns an instance of {Publisher}
69
67
  def publisher
70
- @publisher ||= Publisher.new(self)
68
+ @publisher ||= Bridgetown::Publisher.new(self)
71
69
  end
72
70
 
73
71
  # Prefix a path or paths with the {#root_dir} directory.
@@ -126,16 +124,16 @@ module Bridgetown
126
124
  end
127
125
 
128
126
  # The full path to the directory that houses all the registered collections
129
- # for the current site.
127
+ # for the current site.
130
128
  #
131
- # If +@collections_path+ is specified use its value.
129
+ # If `@collections_path` is specified use its value.
132
130
  #
133
- # If +@collections+ is not specified and +config+["collections_dir"] is
134
- # specified, prepend it with {#source} and assign it to
135
- # {#collections_path}.
131
+ # If `@collections` is not specified and `config["collections_dir"]` is
132
+ # specified, prepend it with {#source} and assign it to
133
+ # {#collections_path}.
136
134
  #
137
- # If +@collections+ is not specified and +config+["collections_dir"] is not
138
- # specified, assign {#source} to +@collections_path+
135
+ # If `@collections` is not specified and `config["collections_dir"]` is not
136
+ # specified, assign {#source} to `@collections_path`
139
137
  #
140
138
  # @return [String] Returns the full path to the collections directory
141
139
  # @see #config
@@ -1,27 +1,24 @@
1
1
  # frozen_string_literal: true
2
2
 
3
- module Bridgetown
4
- module Site::Content
3
+ class Bridgetown::Site
4
+ # Content is king!
5
+ module Content
5
6
  # Construct a Hash of Posts indexed by the specified Post attribute.
6
7
  #
7
- # @param post_attr [String] The String name of the Post attribute.
8
- #
9
- # @example
10
- # Returns a hash like so: { attr => posts } where
8
+ # Returns a hash like so: `{ attr => posts }` where:
11
9
  #
12
- # attr - One of the values for the requested attribute.
10
+ # * `attr` - One of the values for the requested attribute.
11
+ # * `posts` - The array of Posts with the given attr value.
13
12
  #
14
- # posts - The Array of Posts with the given attr value.
13
+ # @param post_attr [String] The String name of the Post attribute.
15
14
  #
16
15
  # @example
17
- #
18
16
  # post_attr_hash('categories')
19
17
  # # => { 'tech' => [<Post A>, <Post B>],
20
18
  # # 'ruby' => [<Post B>] }
21
19
  #
22
20
  # @return [Hash{String, Symbol => Array<Post>}]
23
21
  # Returns a hash of !{attr => posts}
24
- #
25
22
  def post_attr_hash(post_attr)
26
23
  # Build a hash map based on the specified post attribute ( post attr =>
27
24
  # array of posts ) then sort each array in reverse order.
@@ -36,7 +33,7 @@ module Bridgetown
36
33
  end
37
34
 
38
35
  # Returns a hash of "tags" using {#post_attr_hash} where each tag is a key
39
- # and each value is a post which contains the key.
36
+ # and each value is a post which contains the key.
40
37
  # @example
41
38
  # tags
42
39
  # # => { 'tech': [<Post A>, <Post B>],
@@ -48,7 +45,7 @@ module Bridgetown
48
45
  end
49
46
 
50
47
  # Returns a hash of "categories" using {#post_attr_hash} where each tag is
51
- # a key and each value is a post which contains the key.
48
+ # a key and each value is a post which contains the key.
52
49
  # @example
53
50
  # categories
54
51
  # # => { 'tech': [<Post A>, <Post B>],
@@ -60,11 +57,11 @@ module Bridgetown
60
57
  post_attr_hash("categories")
61
58
  end
62
59
 
63
- # Returns the value of +data+["site_metadata"] or creates a new instance of
64
- # +ActiveSupport::HashWithIndifferentAccess+
60
+ # Returns the value of `data["site_metadata"]` or creates a new instance of
61
+ # `HashWithDotAccess::Hash`
65
62
  # @return [Hash] Returns a hash of site metadata
66
63
  def metadata
67
- data["site_metadata"] ||= ActiveSupport::HashWithIndifferentAccess.new
64
+ data["site_metadata"] ||= HashWithDotAccess::Hash.new
68
65
  end
69
66
 
70
67
  # The Hash payload containing site-wide data.
@@ -98,34 +95,33 @@ module Bridgetown
98
95
  #
99
96
  # @see #post_attr_hash
100
97
  def site_payload
101
- Drops::UnifiedPayloadDrop.new self
98
+ Bridgetown::Drops::UnifiedPayloadDrop.new self
102
99
  end
103
100
  alias_method :to_liquid, :site_payload
104
101
 
105
- # The list of {#collections} and their corresponding {Bridgetown::Collection} instances.
102
+ # The list of collections labels and their corresponding {Collection} instances.
106
103
  #
107
- # If +config+['collections'] is set, a new instance of {Bridgetown::Collection} is created
108
- # for each entry in the collections configuration.
104
+ # If `config['collections']` is set, a new instance of {Collection} is created
105
+ # for each entry in the collections configuration.
109
106
  #
110
- # If +config+["collections"] is not specified, a blank hash is returned.
107
+ # If `config["collections"]` is not specified, a blank hash is returned.
111
108
  #
112
- # @return [Hash{String, Symbol => Bridgetown::Collection}] A Hash
109
+ # @return [Hash{String, Symbol => Collection}] A Hash
113
110
  # containing a collection name-to-instance pairs.
114
111
  #
115
112
  # @return [Hash] Returns a blank hash if no items found
116
- # @see Collection
117
113
  def collections
118
114
  @collections ||= collection_names.each_with_object(
119
- ActiveSupport::HashWithIndifferentAccess.new
115
+ HashWithDotAccess::Hash.new
120
116
  ) do |name, hsh|
121
117
  hsh[name] = Bridgetown::Collection.new(self, name)
122
118
  end
123
119
  end
124
120
 
125
121
  # An array of collection names.
126
- # @return [Array<Collection>] an array of collection names from the configuration,
127
- # or an empty array if the +config+["collections"] key is not set.
128
- # @raise ArgumentError Raise an error if +config+["collections"] is not
122
+ # @return [Array<String>] an array of collection names from the configuration,
123
+ # or an empty array if the `config["collections"]` key is not set.
124
+ # @raise ArgumentError Raise an error if `config["collections"]` is not
129
125
  # an Array or a Hash
130
126
  def collection_names
131
127
  case config["collections"]
@@ -141,17 +137,18 @@ module Bridgetown
141
137
  end
142
138
 
143
139
  # Get all documents.
144
- # @return [Array<String>] an array of documents from the configuration
140
+ # @return [Array<Document>] an array of documents from the
141
+ # configuration
145
142
  def documents
146
143
  collections.each_with_object(Set.new) do |(_, collection), set|
147
- set.merge(collection.docs).merge(collection.files)
144
+ set.merge(collection.docs)
148
145
  end.to_a
149
146
  end
150
147
 
151
148
  # Get the documents to be written
152
149
  #
153
- # @return [Array<String, File>] an Array of Documents which should be written and
154
- # that +respond_to :write?+
150
+ # @return [Array<Document>] an array of documents which should be
151
+ # written and that `respond_to :write?`
155
152
  # @see #documents
156
153
  # @see Collection
157
154
  def docs_to_write
@@ -160,11 +157,27 @@ module Bridgetown
160
157
 
161
158
  # Get all posts.
162
159
  #
163
- # @return [Collection] A #Collection of posts. Returns +#collections+["posts"]
164
- # @return [Collection] Return a new #Collection if +#collections+["posts"] is nil
160
+ # @return [Collection] Returns {#collections}`["posts"]`, creating it if need be
165
161
  # @see Collection
166
162
  def posts
167
- collections["posts"] ||= Collection.new(self, "posts")
163
+ collections["posts"] ||= Bridgetown::Collection.new(self, "posts")
164
+ end
165
+
166
+ # Get the static files to be written
167
+ #
168
+ # @return [Array<StaticFile>] an array of files which should be
169
+ # written and that `respond_to :write?`
170
+ # @see #static_files
171
+ # @see StaticFile
172
+ def static_files_to_write
173
+ static_files.select(&:write?)
174
+ end
175
+
176
+ # Get all pages and documents (posts and collection items) in a single array.
177
+ #
178
+ # @return [Array]
179
+ def contents
180
+ pages + documents
168
181
  end
169
182
  end
170
183
  end