Class: MasterView::TemplateSpec

Inherits:
Object
  • Object
show all
Includes:
Analyzer::Common, DirectiveHelpers
Defined in:
lib/masterview/template_spec.rb

Overview

TemplateSpec is a class which contains the information about how to build a template. It contains the information on all the parts that make up the template and the status of them (whether they are up to date or out of sync).

Defined Under Namespace

Classes: CreateShellERBValues, Status

Constant Summary

Constants included from DirectiveHelpers

DirectiveHelpers::CRLF, DirectiveHelpers::ERB_CONTENT_END, DirectiveHelpers::ERB_CONTENT_START, DirectiveHelpers::ERB_EVAL_END, DirectiveHelpers::ERB_EVAL_START

Constants included from Analyzer::Common

Analyzer::Common::ImportToNonImportDir, Analyzer::Common::NonImportToImportDir

Class Attribute Summary collapse

Instance Attribute Summary collapse

Class Method Summary collapse

Instance Method Summary collapse

Methods included from DirectiveHelpers

#delete_last_in_parent, #find_last_in_parent, #find_string_val_in_string_hash, #parse, #quote, #quote_if, #render_partial_name_to_file_name

Methods included from Analyzer::Common

#calc_hash, #convert_import_to_non_import_dirs, #convert_non_import_to_import_dirs, #src_hash

Constructor Details

#initialize(path = nil) ⇒ TemplateSpec

Returns a new instance of TemplateSpec.



28
29
30
31
32
33
34
# File 'lib/masterview/template_spec.rb', line 28

def initialize(path = nil)
  @path = path
  @status = ''
  @message = ''
  @gen_parts = []
  @build_list = []
end

Class Attribute Details

.gen_partial_regex_cachedObject

Returns the value of attribute gen_partial_regex_cached.



13
14
15
# File 'lib/masterview/template_spec.rb', line 13

def gen_partial_regex_cached
  @gen_partial_regex_cached
end

Instance Attribute Details

#build_listObject

Returns the value of attribute build_list.



9
10
11
# File 'lib/masterview/template_spec.rb', line 9

def build_list
  @build_list
end

#gen_partsObject

Returns the value of attribute gen_parts.



9
10
11
# File 'lib/masterview/template_spec.rb', line 9

def gen_parts
  @gen_parts
end

#messageObject

Returns the value of attribute message.



9
10
11
# File 'lib/masterview/template_spec.rb', line 9

def message
  @message
end

#pathObject

Returns the value of attribute path.



9
10
11
# File 'lib/masterview/template_spec.rb', line 9

def path
  @path
end

#statusObject

Returns the value of attribute status.



9
10
11
# File 'lib/masterview/template_spec.rb', line 9

def status
  @status
end

Class Method Details

.create_empty_shell(template_spec_to_copy, content_hash, content_to_insert, options = {}) ⇒ Object

create empty shell file consisting of layout and a comment for where to insert new content, return contents



209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
# File 'lib/masterview/template_spec.rb', line 209

def self.create_empty_shell(template_spec_to_copy, content_hash, content_to_insert, options = {} )
  from_spec = template_spec_to_copy
  content_hash['empty_shell_contents'] = []
  content_hash['empty_shell_contents'] << MasterView::Analyzer::ContentEntry.new(content_to_insert)
  to_spec = TemplateSpec.new
  to_spec.build_list = []
  li = from_spec.build_list.first
  li.import = true
  to_spec.build_list << li
  to_spec.build_list << MasterView::Analyzer::ListEntry.new('empty_shell_contents', -1, false, {} )
  li = from_spec.build_list.last
  li.import = true
  to_spec.build_list << li
  to_spec.rebuild_template(content_hash)
end

.create_empty_shell_for_action(path_to_copy_shell_from, action_to_create, empty_insert_erb, options = {}) ⇒ Object

create empty shell file consisting of layout and a comment for where to insert new content. Use action_to_create to infer the destination name controller_action.html and to customize the inserted place holder. Pass the empty insert erb (rhtml) content in which will be rendered with appropriate controller and action values. Valid options:

:write_to_file => true (write to file and return filename, if false then simply return generated contents)
:template_source => source_for_template (use this source instead of reading from file)
:content_hash => use this content_hash, otherwise it will scan the masterview template directory to create content_hash


247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
# File 'lib/masterview/template_spec.rb', line 247

def self.create_empty_shell_for_action(path_to_copy_shell_from, action_to_create, empty_insert_erb, options={} )
  path = IOMgr.template.cleanup_path_get_relative_pathname(path_to_copy_shell_from).to_s
  controller_name = Pathname.for_path(path).dirname.to_s
  extension = IOMgr.template.default_extension
  erb_values = CreateShellERBValues.new(controller_name, action_to_create)
  template = ERB.new(empty_insert_erb)
  content_to_insert = template.result(erb_values.get_binding).strip #clear off surrounding whitespace that makes it difficult to debug

  basename_with_extension = erb_values.action_name
  basename_with_extension += extension if extension
  dst_path = (Pathname.for_path(path).dirname+(basename_with_extension)).to_s
  src = (tsrc = options[:template_source]) ? tsrc : IOMgr.template.path(path).read

  template_specs = {}
  content_hash = options[:content_hash]
  template_specs, content_hash = TemplateSpec.scan unless content_hash
  template_spec_to_copy = template_specs[path] || TemplateSpec.scan_template(src, path)

  result = TemplateSpec.create_empty_shell( template_spec_to_copy, content_hash, content_to_insert)
  if options[:write_to_file]
    template_mio = IOMgr.template.path(dst_path)
    raise 'Template '+dst_path+' already exists, operation aborted.' if template_mio.exist?
    template_mio.write(result)
    return dst_path
  end
  result
end

.gen_partial_reObject



15
16
17
18
# File 'lib/masterview/template_spec.rb', line 15

def gen_partial_re
  self.gen_partial_regex_cached ||= Regexp.new(
      "#{DirectiveRegistry.current.mv_namespace_prefix}gen_partial\\s*=\\s*((\")([^\"]*)\"|(')([^']*'))" )
end

.scan(options = {}, &block) ⇒ Object

scan the directory of templates, building template_specs and the content_hash



47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
# File 'lib/masterview/template_spec.rb', line 47

def self.scan(options = {}, &block)
  content_hash = {}
  template_specs = {}

  IOMgr.template.find(:pattern => TemplateFilenamePattern) do |mio|
    template_specs[mio.pathname.to_s] = scan_template_mio(mio, content_hash)
  end

  IOMgr.template.find(:pattern => TemplateFilenamePattern) do |mio|
    path = mio.pathname.to_s
    if template_specs[path].status == Status::OK
      invalid_parts = template_mio_out_of_sync?(path, content_hash)
      template_spec = template_specs[path]
      template_spec.update_status_from_invalid_parts(invalid_parts)
    end
    yield template_specs[path], content_hash if block_given?
  end
  return template_specs, content_hash
end

.scan_template(template, path, content_hash = {}) ⇒ Object

create a template_spec



81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
# File 'lib/masterview/template_spec.rb', line 81

def self.scan_template(template, path, content_hash = {})
  template_spec = TemplateSpec.new(path)
  listener = MasterView::Analyzer::Listener.new( :template_pathname => Pathname.for_path(path) )
  begin
    MasterView::Parser.parse( template, :rescue_exceptions => false, :listeners => [listener])
    template_spec.build_list = listener.list
    conflicts = content_hash.keys & listener.content.keys
    content_hash.merge! listener.content
    if conflicts.empty?
      template_spec.status = Status::OK
    else
      template_spec.status = Status::Conflicts
      template_spec.message = 'Duplicated generation of: ' + conflicts.sort.join(', ')
    end
    template_spec.gen_parts = listener.content.keys.sort
  rescue REXML::ParseException => e
    template_spec.status = Status::InvalidXHTML
    template_spec.message = e.to_s
  end
  template_spec
end

.scan_template_mio(mio, content_hash = {}) ⇒ Object



67
68
69
70
71
# File 'lib/masterview/template_spec.rb', line 67

def self.scan_template_mio(mio, content_hash = {} )
  template = mio.read(:disable_cache => true)
  path = mio.pathname.to_s
  self.scan_template(template, path, content_hash)
end

.template_mio_out_of_sync?(path, content_hash) ⇒ Boolean

Returns:

  • (Boolean)


103
104
105
106
107
# File 'lib/masterview/template_spec.rb', line 103

def self.template_mio_out_of_sync?(path, content_hash)
  mio = IOMgr.template.path(path)
  template = mio.read(:disable_cache => true)
  self.template_out_of_sync?(template, path, content_hash)
end

.template_out_of_sync?(template, path, content_hash) ⇒ Boolean

check if the template is out of sync with the source content (check imports), return array of invalid

Returns:

  • (Boolean)


110
111
112
113
114
115
116
117
118
# File 'lib/masterview/template_spec.rb', line 110

def self.template_out_of_sync?(template, path, content_hash)
  invalid = []
  listener = MasterView::Analyzer::Listener.new(:content_hash => content_hash, :template_pathname => Pathname.for_path(path), :only_check_hash => true)
  Parser.parse( template, :rescue_exceptions => false, :listeners => [listener] )
  invalid_list_items = listener.list.find_all { |li| li.hash_invalid? }
  invalid_with_dups = invalid_list_items.collect { |li| li.name }
  invalid = invalid_with_dups.uniq.sort
  invalid
end

Instance Method Details

#backup_file(options = {}) ⇒ Object

create backup file by appending secs since epoch to filename



182
183
184
185
186
187
188
# File 'lib/masterview/template_spec.rb', line 182

def backup_file(options={} )
  return unless IOMgr.backup
  contents_to_backup = IOMgr.template.path(self.path).read
  backup_path = self.path+'.'+Time.new.to_i.to_s
  Log.debug { 'creating backup file '+backup_path }
  IOMgr.backup.path(backup_path).write(contents_to_backup)
end

#basenameObject

get short base name of path



37
38
39
# File 'lib/masterview/template_spec.rb', line 37

def basename
  File.basename @path
end

#file_exist?Boolean

file exist?

Returns:

  • (Boolean)


204
205
206
# File 'lib/masterview/template_spec.rb', line 204

def file_exist?
  IOMgr.template.path(self.path).exist?
end

#long_pathObject

get path relative to working dir



42
43
44
# File 'lib/masterview/template_spec.rb', line 42

def long_path
  IOMgr.template.path(@path).full_pathname.to_s
end

#rebuild_template(content_hash, options = {}) ⇒ Object

rebuild template updating all imports, returns the string contents, if options = true then it will write the contents to file if different and return true, if identical then returns false otherwise this method returns the content of the template raise error for any other problems. options = true to make backup before rebuilding, uses MasterView::IOMgr.backup to determine path for where to store backup files. If MasterView::IOMgr.backup is nil, then no backup is created.



125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
# File 'lib/masterview/template_spec.rb', line 125

def rebuild_template(content_hash, options = {} )
  out = []
  builder = MasterView::Analyzer::Builder.new(content_hash)
  @build_list.each do |li|
    #Log.debug { li.inspect }
    con = builder.data(li.name, li.index)

    unless li.page_attributes.empty? # if we have page_attributes, reinsert them now, currently only inserting for partials
      gpmatch = TemplateSpec.gen_partial_re.match(con)

      if gpmatch
        gen_partial_all = gpmatch[0]
        gp_quote_char = gpmatch[2] || gpmatch[4]
        gen_partial_fullattr = gpmatch[3] || gpmatch[5]

        # determine what partial page we are rendering to see if we have any page specific attributes
        partial = find_string_val_in_string_hash(gen_partial_fullattr, :partial)
        if partial
          partial_pa = li.page_attributes[partial]   # retrieve page specific attribute by file name
          if partial_pa  # if we have one, replace it in the text
            mv_ns = DirectiveRegistry.current.mv_namespace_prefix
            con.gsub! TemplateSpec.gen_partial_re, "#{mv_ns}gen_partial=#{gp_quote_char}#{partial_pa}#{gp_quote_char}"
          end
        end
      end

#           sorted_page_att_arr = li.page_attributes.sort{ |a,b| a[0].to_s <=> b[0].to_s } #[[a, 2], [b, 1]]
#           sorted_page_att = sorted_page_att_arr.collect{ |a| "#{a[0]}=\"#{a[1]}\"" } # a="2"
#           pg_att_ins = sorted_page_att.join(' ')
#           mv_ns = DirectiveRegistry.current.mv_namespace_prefix
#           con.gsub! "#{mv_ns}gen_partial", "#{pg_att_ins} #{mv_ns}gen_partial"

      #Log.debug { con.inspect }
    end

    if li.import
      con = convert_non_import_to_import_dirs(con) #gsub mv:gen_partial to mv:import_render and mv:generate to mv:import
    end
    out << con
  end
  template = out.join
  if options[:write_to_file]
    backup = !options[:backup].nil? ? options[:backup] : !IOMgr.backup.nil?
    orig = IOMgr.template.path(self.path).read(:disable_cache => true)
    file_written = false
    unless template == orig
      self.backup_file if backup
      IOMgr.template.path(self.path).write(template, :force => true) #force write in case tidy had cleaned up
      file_written = true
    end
    return file_written
  end
  template
end

#remove_rhtml(options = {}) ⇒ Object

remove file



191
192
193
194
195
196
197
198
199
200
201
# File 'lib/masterview/template_spec.rb', line 191

def remove_rhtml(options={})
  removed = []
  self.gen_parts.each do |gen_rhtml|
    mio = IOMgr.erb.path(gen_rhtml)
    if mio.exist?
      mio.remove
      removed << gen_rhtml
    end
  end
  removed
end

#update_status_from_invalid_parts(invalid_parts) ⇒ Object



73
74
75
76
77
78
# File 'lib/masterview/template_spec.rb', line 73

def update_status_from_invalid_parts(invalid_parts)
  unless invalid_parts.empty?
    @status = Status::ImportsOutdated
    @message = "Outdated parts: "+invalid_parts.join(', ')
  end
end