awestruct 0.0.4 → 0.0.5

Sign up to get free protection for your applications and to get access to all the features.
@@ -7,6 +7,7 @@ require 'optparse'
7
7
  require 'awestruct/commands/init'
8
8
  require 'awestruct/commands/generate'
9
9
  require 'awestruct/commands/server'
10
+ require 'awestruct/commands/deploy'
10
11
 
11
12
  def parse(args)
12
13
  options = OpenStruct.new( {
@@ -20,6 +21,8 @@ def parse(args)
20
21
  :framework=>'compass',
21
22
  :scaffold=>true,
22
23
  :base_url=>nil,
24
+ :profile=>nil,
25
+ :deploy=>false,
23
26
  } )
24
27
 
25
28
  opts = OptionParser.new do |opts|
@@ -42,12 +45,21 @@ def parse(args)
42
45
  opts.on( '-u', '--url URL', 'Set site.base_url' ) do |url|
43
46
  options.base_url = url
44
47
  end
45
- opts.on( '-d', '--dev', 'Run in development mode (--auto, --server and --url http://localhost:4242)' ) do |url|
46
- options.base_url = 'http://localhost:4242'
48
+ opts.on( '-d', '--dev', 'Run in development mode (--auto, --server and -profile development)' ) do |url|
47
49
  options.server = true
48
50
  options.auto = true
49
51
  options.port = 4242
52
+ options.profile = 'development'
50
53
  end
54
+
55
+ opts.on( '-P', '--profile PROFILE', 'Specify a profile' ) do |profile|
56
+ options.profile = profile
57
+ end
58
+
59
+ opts.on( '--deploy', 'Deploy site' ) do |deploy|
60
+ options.deploy = deploy
61
+ end
62
+
51
63
  opts.on( '-a', '--auto', 'Auto-generate when changes are noticed' ) do |a|
52
64
  options.auto = a
53
65
  end
@@ -80,22 +92,79 @@ options = parse(ARGV)
80
92
  if ( options.init )
81
93
  if ( options.generate || options.auto || options.server )
82
94
  $stderr.puts "--init may not be used with --generate, --auto or --server"
95
+ exit
83
96
  end
84
97
  cmd = Awestruct::Commands::Init.new( Dir.pwd, options.framework, options.scaffold )
85
98
  cmd.run
86
99
  exit
87
100
  end
88
101
 
102
+ if ( options.deploy )
103
+ if ( options.auto || options.server )
104
+ $stderr.puts "--init may not be used with --generate, --auto or --server"
105
+ exit
106
+ end
107
+
108
+ if ( ! options.profile )
109
+ $stderr.puts "You must specify a --profile when using --deploy"
110
+ exit
111
+ end
112
+
113
+
114
+ site_dir = File.join( Dir.pwd, '_site' )
115
+ if ( ! File.exist?( site_dir ) )
116
+ $stderr.puts "No _site/ generated yet."
117
+ exit
118
+ end
119
+
120
+ site_yaml_file = File.join( Dir.pwd, '_config', 'site.yml' )
121
+
122
+ site_yaml = YAML.load( File.read( site_yaml_file ) )
123
+
124
+ profiles_data = site_yaml['profiles']
125
+
126
+ if ( profiles_data.nil? )
127
+ $stderr.puts "_config/site.yml must define a 'profiles' section"
128
+ exit
129
+ end
130
+
131
+ profile_data = profiles_data[options.profile]
132
+
133
+ if ( profile_data.nil? )
134
+ $stderr.puts "Unknown profile '#{options.profile}'"
135
+ exit
136
+ end
137
+
138
+ deploy_data = profile_data['deploy']
139
+
140
+ if ( deploy_data.nil? )
141
+ $stderr.puts "No configuration for 'deploy'"
142
+ exit
143
+ end
144
+
145
+ cmd = Awestruct::Commands::Deploy.new( site_dir, deploy_data )
146
+
147
+ cmd.run
148
+ exit
149
+ end
150
+
89
151
  threads = []
90
152
 
153
+ default_base_url = 'http://localhost:4242'
154
+
155
+ if ( options.server )
156
+ hostname = ( options.bind_addr == '0.0.0.0' ? 'localhost' : options.bind_addr )
157
+ default_base_url = "http://#{hostname}:#{options.port}"
158
+ end
159
+
91
160
  if ( options.generate )
92
- cmd = Awestruct::Commands::Generate.new( Dir.pwd, options.base_url, options.force )
161
+ cmd = Awestruct::Commands::Generate.new( Dir.pwd, options.profile, options.base_url, default_base_url, options.force )
93
162
  cmd.run
94
163
  end
95
164
 
96
165
  if ( options.auto )
97
166
  threads << Thread.new {
98
- generate_cmd = Awestruct::Commands::Generate.new( Dir.pwd, options.base_url )
167
+ generate_cmd = Awestruct::Commands::Generate.new( Dir.pwd, options.profile, options.base_url, default_base_url )
99
168
  while ( true )
100
169
  generate_cmd.run
101
170
  sleep(1)
@@ -0,0 +1,36 @@
1
+ require 'open3'
2
+
3
+ module Awestruct
4
+ module Commands
5
+
6
+ class Deploy
7
+ def initialize(site_path, opts)
8
+ @site_path = File.join( site_path, '/' )
9
+ @host = opts['host']
10
+ @path = File.join( opts['path'], '/' )
11
+ end
12
+
13
+ def run
14
+ cmd = "rsync -rv --delete #{@site_path} #{@host}:#{@path}"
15
+ puts "running #{cmd}"
16
+ Open3.popen3( cmd ) do |stdin, stdout, stderr|
17
+ stdin.close
18
+ threads = []
19
+ threads << Thread.new(stdout) do |i|
20
+ while ( ! i.eof? )
21
+ line = i.readline
22
+ puts line
23
+ end
24
+ end
25
+ threads << Thread.new(stderr) do |i|
26
+ while ( ! i.eof? )
27
+ line = i.readline
28
+ puts line
29
+ end
30
+ end
31
+ threads.each{|t|t.join}
32
+ end
33
+ end
34
+ end
35
+ end
36
+ end
@@ -4,16 +4,18 @@ module Awestruct
4
4
  module Commands
5
5
  class Generate
6
6
 
7
- def initialize(dir=Dir.pwd, base_url=nil, force=false)
8
- @dir = dir
9
- @base_url = base_url
10
- @force = force
11
- @engine = Awestruct::Engine.new( @dir )
7
+ def initialize(dir=Dir.pwd, profile=nil, base_url=nil, default_base_url='http://localhost:4242', force=false)
8
+ @dir = dir
9
+ @profile = profile
10
+ @base_url = base_url
11
+ @default_base_url = default_base_url
12
+ @force = force
13
+ @engine = Awestruct::Engine.new( @dir )
12
14
  end
13
15
 
14
16
  def run()
15
17
  begin
16
- @engine.generate( @base_url, @force )
18
+ @engine.generate( @profile, @base_url, @default_base_url, @force )
17
19
  rescue =>e
18
20
  puts e
19
21
  puts e.backtrace
@@ -0,0 +1,34 @@
1
+ require 'hpricot'
2
+
3
+ module Awestruct
4
+ module ContextHelper
5
+ def html_to_text(str)
6
+ str.gsub( /<[^>]+>/, '' )
7
+ end
8
+
9
+ def summarize(text, numwords=20)
10
+ text.split()[0, numwords].join(' ')
11
+ end
12
+
13
+ def fully_qualify_urls(base_url, text)
14
+ doc = Hpricot( text )
15
+
16
+ doc.search( "//a" ).each do |a|
17
+ a['href'] = fix_url( base_url, a['href'] )
18
+ end
19
+ doc.search( "//link" ).each do |link|
20
+ link['href'] = fix_url( base_url, link['href'] )
21
+ end
22
+ doc.search( "//img" ).each do |img|
23
+ img['src'] = fix_url( base_url, img['src'] )
24
+ end
25
+ return doc.to_s
26
+ end
27
+
28
+ def fix_url(base_url, url)
29
+ return url unless ( url =~ /^\// )
30
+ "#{base_url}#{url}"
31
+ end
32
+ end
33
+
34
+ end
@@ -11,13 +11,15 @@ require 'awestruct/maruku_file'
11
11
  require 'awestruct/sass_file'
12
12
  require 'awestruct/verbatim_file'
13
13
 
14
- require 'awestruct/haml_helpers'
14
+ require 'awestruct/context_helper'
15
15
 
16
16
  require 'awestruct/extensions/pipeline'
17
17
  require 'awestruct/extensions/posts'
18
18
  require 'awestruct/extensions/indexifier'
19
19
  require 'awestruct/extensions/paginator'
20
20
  require 'awestruct/extensions/atomizer'
21
+ require 'awestruct/extensions/intense_debate'
22
+ require 'awestruct/extensions/google_analytics'
21
23
 
22
24
  require 'awestruct/util/inflector'
23
25
  require 'awestruct/util/default_inflections'
@@ -36,18 +38,24 @@ module Awestruct
36
38
  @site = Site.new( @dir )
37
39
  @site.engine = self
38
40
  @site.tmp_dir = File.join( dir, '_tmp' )
41
+ @helpers = []
39
42
  FileUtils.mkdir_p( @site.tmp_dir )
40
43
  FileUtils.mkdir_p( @site.output_dir )
41
44
  @max_yaml_mtime = nil
42
45
  end
43
46
 
44
- def generate(base_url=nil, force=false)
45
- @base_url = base_url
46
- load_layouts
47
+ def generate(profile=nil, base_url=nil, default_base_url=nil, force=false)
48
+ @base_url = base_url
49
+ @default_base_url = default_base_url
50
+ @max_yaml_mtime = nil
51
+ load_site_yaml(profile)
47
52
  load_yamls
53
+ set_base_url
54
+ load_layouts
48
55
  load_pages
49
56
  load_extensions
50
57
  set_urls
58
+ configure_compass
51
59
  generate_files(force)
52
60
  end
53
61
 
@@ -88,9 +96,54 @@ module Awestruct
88
96
  page
89
97
  end
90
98
 
99
+ def create_context(page, content='')
100
+ context = OpenStruct.new( :site=>site, :content=>content )
101
+ context.extend( Awestruct::ContextHelper )
102
+ @helpers.each do |h|
103
+ context.extend( h )
104
+ end
105
+ context.page = page
106
+ class << context
107
+ def interpolate_string(str)
108
+ str = str || ''
109
+ str = str.gsub( /\\/, '\\\\\\\\' )
110
+ str = str.gsub( /\\\\#/, '\\#' )
111
+ str = str.gsub( '@', '\@' )
112
+ str = "%@#{str}@"
113
+ result = instance_eval( str )
114
+ result
115
+ end
116
+ end
117
+ context
118
+ end
91
119
 
92
120
  private
93
121
 
122
+ def configure_compass
123
+ Compass.configuration.project_type = :standalone
124
+ Compass.configuration.project_path = dir
125
+ Compass.configuration.css_dir = File.join( dir, config.output_dir, 'stylesheets' )
126
+ Compass.configuration.sass_dir = File.join( dir, 'stylesheets' )
127
+ Compass.configuration.images_dir = File.join( dir, 'images' )
128
+ Compass.configuration.javascripts_dir = File.join( dir, 'javascripts' )
129
+ end
130
+
131
+ def set_base_url
132
+ if ( @base_url )
133
+ site.base_url = @base_url
134
+ end
135
+
136
+ if ( site.base_url.nil? )
137
+ site.base_url = @default_base_url
138
+ end
139
+
140
+ if ( site.base_url )
141
+ if ( site.base_url =~ /^(.*)\/$/ )
142
+ site.base_url = $1
143
+ end
144
+ end
145
+ end
146
+
94
147
  def set_urls
95
148
  site.pages.each do |page|
96
149
  page_path = page.output_path
@@ -145,20 +198,8 @@ module Awestruct
145
198
  false
146
199
  end
147
200
 
148
- def self.create_context(site, page, content='')
149
- context = OpenStruct.new( :site=>site, :content=>content )
150
- context.page = page
151
- class << context
152
- def interpolate_string(str)
153
- result = instance_eval("%@#{(str||'').gsub('@', '\@')}@")
154
- result
155
- end
156
- end
157
- context
158
- end
159
-
160
201
  def render_page(page, with_layouts=true)
161
- context = Engine.create_context( site, page )
202
+ context = create_context( page )
162
203
  rendered = page.render( context )
163
204
  if ( with_layouts )
164
205
  cur = page
@@ -183,26 +224,45 @@ module Awestruct
183
224
  end
184
225
  end
185
226
 
186
- def load_yamls
187
- @max_yaml_mtime = nil
188
- Dir[ File.join( dir, config.config_dir, '*.yml' ) ].each do |yaml_path|
189
- mtime = File.mtime( yaml_path )
227
+ def load_site_yaml(profile)
228
+ site_yaml = File.join( dir, config.config_dir, 'site.yml' )
229
+ if ( File.exist?( site_yaml ) )
230
+ mtime = File.mtime( site_yaml )
190
231
  if ( mtime > ( @max_yaml_mtime || Time.at(0) ) )
191
232
  @max_yaml_mtime = mtime
192
233
  end
193
- data = YAML.load( File.read( yaml_path ) )
194
- name = File.basename( yaml_path, '.yml' )
195
- if ( name == 'site' )
196
- data.each do |k,v|
234
+ data = YAML.load( File.read( site_yaml ) )
235
+ profile_data = {}
236
+ data.each do |k,v|
237
+ if ( ( k == 'profiles' ) && ( ! profile.nil? ) )
238
+ profile_data = ( v[profile] || {} )
239
+ else
197
240
  site.send( "#{k}=", v )
198
241
  end
199
- ( site.base_url = @base_url ) if ( @base_url )
200
- else
201
- site.send( "#{name}=", massage_yaml( data ) )
242
+ end
243
+
244
+ profile_data.each do |k,v|
245
+ site.send( "#{k}=", v )
202
246
  end
203
247
  end
204
248
  end
205
249
 
250
+ def load_yamls
251
+ Dir[ File.join( dir, config.config_dir, '*.yml' ) ].each do |yaml_path|
252
+ load_yaml( yaml_path ) unless ( File.basename( yaml_path ) == 'site.yml' )
253
+ end
254
+ end
255
+
256
+ def load_yaml(yaml_path)
257
+ mtime = File.mtime( yaml_path )
258
+ if ( mtime > ( @max_yaml_mtime || Time.at(0) ) )
259
+ @max_yaml_mtime = mtime
260
+ end
261
+ data = YAML.load( File.read( yaml_path ) )
262
+ name = File.basename( yaml_path, '.yml' )
263
+ site.send( "#{name}=", massage_yaml( data ) )
264
+ end
265
+
206
266
  def load_pages()
207
267
  dir_pathname = Pathname.new( dir )
208
268
  site.pages.clear
@@ -251,25 +311,7 @@ module Awestruct
251
311
  end
252
312
  pipeline = eval File.read( pipeline_file )
253
313
  pipeline.execute( site )
254
- end
255
-
256
- def old_load_extensions
257
- ext_dir_pathname = Pathname.new( File.join( dir, config.extension_dir ) )
258
- Dir[ File.join( dir, config.extension_dir, '*.rb' ) ].each do |path|
259
- ext_pathname = Pathname.new( path )
260
- relative_path = ext_pathname.relative_path_from( ext_dir_pathname ).to_s
261
- dir_name = File.dirname( relative_path )
262
- if ( dir_name == '.' )
263
- simple_path = File.basename( relative_path, '.rb' )
264
- else
265
- simple_path = File.join( dir_name, File.basename( relative_path, '.rb' ) )
266
- end
267
- ext_classname = camelize(simple_path)
268
- require File.join( dir, config.extension_dir, simple_path )
269
- ext_class = eval( ext_classname )
270
- ext = ext_class.new
271
- ext.execute( site )
272
- end
314
+ @helpers = pipeline.helpers || []
273
315
  end
274
316
 
275
317
  def camelize(lower_case_and_underscored_word, first_letter_in_uppercase = true)
@@ -0,0 +1,22 @@
1
+
2
+ module Awestruct
3
+ module Extensions
4
+ module GoogleAnalytics
5
+
6
+ def google_analytics()
7
+ html = ''
8
+ html += %Q(<script type="text/javascript">\n)
9
+ html += %Q(var gaJsHost = (("https:" == document.location.protocol) ? "https://ssl." : "http://www.");\n)
10
+ html += %Q(document.write(unescape("%3Cscript src='" + gaJsHost + "google-analytics.com/ga.js' type='text/javascript'%3E%3C/script%3E"));\n)
11
+ html += %Q(</script>\n)
12
+ html += %Q(<script type="text/javascript">\n)
13
+ html += %Q(try {\n)
14
+ html += %Q(var pageTracker = _gat._getTracker("#{site.google_analytics}");\n)
15
+ html += %Q(pageTracker._trackPageview();\n)
16
+ html += %Q(} catch(err) {}</script>\n)
17
+ html
18
+ end
19
+ end
20
+ end
21
+
22
+ end
@@ -1,26 +1,19 @@
1
1
 
2
- #module Awestruct
3
- #module Extension
4
- #class IntenseDebate
5
- #
6
- #def execute(site)
7
- #end
8
- #
9
- #end
10
- #end
11
- #end
2
+ module Awestruct
3
+ module Extensions
4
+ module IntenseDebate
12
5
 
13
- module Haml::Helpers
14
-
15
- def intense_debate_comments()
16
- html = %Q(<script>\n)
17
- html += %Q( var idcomments_acct='#{site.intense_debate_acct}';\n)
18
- html += %Q( var idcomments_post_id;\n)
19
- html += %Q( var idcomments_post_url;\n)
20
- html += %Q(</script>\n)
21
- html += %Q(<span id="IDCommentsPostTitle" style="display:none"></span>\n)
22
- html += %Q(<script type='text/javascript' src='http://www.intensedebate.com/js/genericCommentWrapperV2.js'></script>\n)
23
- html
6
+ def intense_debate_comments()
7
+ html = %Q(<script>\n)
8
+ html += %Q( var idcomments_acct='#{site.intense_debate_acct}';\n)
9
+ html += %Q( var idcomments_post_id;\n)
10
+ html += %Q( var idcomments_post_url;\n)
11
+ html += %Q(</script>\n)
12
+ html += %Q(<span id="IDCommentsPostTitle" style="display:none"></span>\n)
13
+ html += %Q(<script type='text/javascript' src='http://www.intensedebate.com/js/genericCommentWrapperV2.js'></script>\n)
14
+ html
15
+ end
16
+ end
24
17
  end
25
18
 
26
19
  end
@@ -4,9 +4,11 @@ module Awestruct
4
4
  class Pipeline
5
5
 
6
6
  attr_reader :extensions
7
+ attr_reader :helpers
7
8
 
8
9
  def initialize(&block)
9
10
  @extensions = []
11
+ @helpers = []
10
12
  instance_eval &block if block
11
13
  end
12
14
 
@@ -14,6 +16,10 @@ module Awestruct
14
16
  @extensions << ext
15
17
  end
16
18
 
19
+ def helper(helper)
20
+ @helpers << helper
21
+ end
22
+
17
23
  def execute(site)
18
24
  extensions.each do |ext|
19
25
  ext.execute( site )
@@ -3,7 +3,7 @@ module Awestruct
3
3
  module Extensions
4
4
  class Posts
5
5
 
6
- def initialize(path_prefix, assign_to=:posts)
6
+ def initialize(path_prefix='', assign_to=:posts)
7
7
  @path_prefix = path_prefix
8
8
  @assign_to = assign_to
9
9
  end
@@ -32,10 +32,11 @@ module Awestruct
32
32
  posts = posts.sort_by{|each| [each.date, File.mtime( each.source_path ), each.slug ] }.reverse
33
33
 
34
34
  last = nil
35
+ singular = @assign_to.to_s.singularize
35
36
  posts.each do |e|
36
37
  if ( last != nil )
37
- e.next = last
38
- last.previous = e
38
+ e.send( "next_#{singular}=", last )
39
+ last.send( "previous_#{singular}=", e )
39
40
  end
40
41
  last = e
41
42
  end
@@ -14,7 +14,7 @@ module Awestruct
14
14
  end
15
15
 
16
16
  def content
17
- context = OpenStruct.new( :site=>site, :page=>self )
17
+ context = site.engine.create_context( self )
18
18
  render( context )
19
19
  end
20
20
  end
@@ -16,7 +16,7 @@ module Awestruct
16
16
  end
17
17
 
18
18
  def content
19
- context = Awestruct::Engine.create_context( site, self )
19
+ context = site.engine.create_context( self )
20
20
  render( context )
21
21
  end
22
22
  end
@@ -6,13 +6,17 @@ module Awestruct
6
6
  module Sassable
7
7
 
8
8
  def render(context)
9
- sass_opts = {
10
- :load_paths => [
11
- File.dirname( self.source_path ),
12
- Compass::Frameworks['compass'].stylesheets_directory,
13
- Compass::Frameworks['blueprint'].stylesheets_directory,
14
- ],
15
- }
9
+ #sass_opts = {
10
+ #:load_paths => [
11
+ #File.dirname( self.source_path ),
12
+ #Compass::Frameworks['compass'].stylesheets_directory,
13
+ #Compass::Frameworks['blueprint'].stylesheets_directory,
14
+ #],
15
+ #}
16
+ sass_opts = Compass.sass_engine_options
17
+ sass_opts[:load_paths] ||= []
18
+ sass_opts[:load_paths] << File.dirname( self.source_path )
19
+
16
20
  sass_engine = Sass::Engine.new( raw_page_content, sass_opts )
17
21
  sass_engine.render
18
22
  end
@@ -10,7 +10,7 @@ module Awestruct
10
10
  attr_accessor :pages
11
11
 
12
12
  def initialize(dir)
13
- super({ :base_url=>'http://localhost:4242' })
13
+ super({})
14
14
 
15
15
  @dir = dir
16
16
  @output_dir = File.join( dir, '_site' )
metadata CHANGED
@@ -5,8 +5,8 @@ version: !ruby/object:Gem::Version
5
5
  segments:
6
6
  - 0
7
7
  - 0
8
- - 4
9
- version: 0.0.4
8
+ - 5
9
+ version: 0.0.5
10
10
  platform: ruby
11
11
  authors:
12
12
  - Bob McWhirter
@@ -14,7 +14,7 @@ autorequire:
14
14
  bindir: bin
15
15
  cert_chain: []
16
16
 
17
- date: 2010-02-28 00:00:00 -05:00
17
+ date: 2010-03-01 00:00:00 -05:00
18
18
  default_executable:
19
19
  dependencies:
20
20
  - !ruby/object:Gem::Dependency
@@ -87,13 +87,16 @@ extra_rdoc_files: []
87
87
 
88
88
  files:
89
89
  - lib/awestruct/commands/base_pipeline.rb
90
+ - lib/awestruct/commands/deploy.rb
90
91
  - lib/awestruct/commands/generate.rb
91
92
  - lib/awestruct/commands/init.rb
92
93
  - lib/awestruct/commands/manifest.rb
93
94
  - lib/awestruct/commands/server.rb
94
95
  - lib/awestruct/config.rb
96
+ - lib/awestruct/context_helper.rb
95
97
  - lib/awestruct/engine.rb
96
98
  - lib/awestruct/extensions/atomizer.rb
99
+ - lib/awestruct/extensions/google_analytics.rb
97
100
  - lib/awestruct/extensions/indexifier.rb
98
101
  - lib/awestruct/extensions/intense_debate.rb
99
102
  - lib/awestruct/extensions/paginator.rb
@@ -101,7 +104,6 @@ files:
101
104
  - lib/awestruct/extensions/posts.rb
102
105
  - lib/awestruct/front_matter_file.rb
103
106
  - lib/awestruct/haml_file.rb
104
- - lib/awestruct/haml_helpers.rb
105
107
  - lib/awestruct/hamlable.rb
106
108
  - lib/awestruct/maruku_file.rb
107
109
  - lib/awestruct/marukuable.rb
@@ -1,32 +0,0 @@
1
- require 'hpricot'
2
-
3
- module Haml::Helpers
4
- def html_to_text(str)
5
- str.gsub( /<[^>]+>/, '' )
6
- end
7
-
8
- def summarize(text, numwords=20)
9
- text.split()[0, numwords].join(' ')
10
- end
11
-
12
- def fully_qualify_urls(base_url, text)
13
- doc = Hpricot( text )
14
-
15
- doc.search( "//a" ).each do |a|
16
- a['href'] = fix_url( base_url, a['href'] )
17
- end
18
- doc.search( "//link" ).each do |link|
19
- link['href'] = fix_url( base_url, link['href'] )
20
- end
21
- doc.search( "//img" ).each do |img|
22
- img['src'] = fix_url( base_url, img['src'] )
23
- end
24
- return doc.to_s
25
- end
26
-
27
- def fix_url(base_url, url)
28
- return url unless ( url =~ /^\// )
29
- "#{base_url}#{url}"
30
- end
31
-
32
- end