require 'xcres/builder/file_builder' require 'xcres/helper/file_helper' class XCRes::ResourcesBuilder < XCRes::FileBuilder include XCRes::FileHelper BANNER = <<EOS // generated by xcres // // DO NOT EDIT. This file is machine-generated and constantly overwritten. // See https://github.com/mrackwitz/xcres for more info. // EOS COMPILER_KEYWORDS = %w{ auto break case char const continue default do double else enum extern float for goto if inline int long register restrict return short signed sizeof static struct switch typedef union unsigned void volatile while } # @return [String] # the name of the constant in the generated file(s) attr_accessor :resources_constant_name # @return [Bool] # whether the generated resources constant should contain inline # documentation for each key, true by default attr_accessor :documented alias :documented? :documented # @return [Hash{String => {String => String}}] # the sections, which will been written to the built files attr_reader :sections # Initialize a new instance # def initialize @sections = {} self.documented = true end # Extract resource name from #output_path, if not customized # # @return [String] # def resources_constant_name @resources_constant_name ||= basename_without_ext output_path end def add_section name, items, options = {} raise ArgumentError.new 'No items given!' if items.nil? transformed_items = {} for key, value in items transformed_key = transform_key key, options # Skip invalid key names if transformed_key.length == 0 logger.warn "Skip invalid key: '%s'. (Was transformed to empty text)", key next end # Skip compiler keywords if COMPILER_KEYWORDS.include? transformed_key logger.warn "Skip invalid key: '%s'. (Was transformed to keyword '%s')", key, transformed_key next end transformed_items[transformed_key] = value end @sections[name] = transformed_items end def build super # Build file contents and write them to disk write_file_eventually "#{output_path}.h", (build_contents do |h_file| build_header_contents h_file end) write_file_eventually "#{output_path}.m", (build_contents do |m_file| build_impl_contents m_file end) end protected def transform_key key, options # Split the key into components components = key.underscore.split /[_\/ ]/ # Build the new key incremental result = '' for component in components # Ignore empty components next unless component.length > 0 # Ignore components which are already contained in the key, if enabled if options[:shorten_keys] next unless result.downcase.scan(component).blank? end # Clean component from non alphanumeric characters clean_component = component.gsub /[^a-zA-Z0-9]/, '' # Skip if empty next unless clean_component.length > 0 if result.length == 0 result += clean_component else result += clean_component[0].upcase + clean_component[1..-1] end end result end def build_header_contents h_file h_file.writeln BANNER h_file.writeln h_file.writeln '#import <Foundation/Foundation.h>' h_file.writeln h_file.writeln 'extern const struct %s {' % resources_constant_name h_file.section do |struct| enumerate_sections do |section_key, enumerate_keys| struct.writeln 'struct %s {' % section_key struct.section do |section_struct| enumerate_keys.call do |key, value, comment| if documented? section_struct.writeln '/// %s' % (comment || value) #unless comment.nil? end section_struct.writeln '__unsafe_unretained NSString *%s;' % key end end struct.writeln '} %s;' % section_key end end h_file.writeln '} %s;' % resources_constant_name end def build_impl_contents m_file m_file.writeln BANNER m_file.writeln m_file.writeln '#import "R.h"' m_file.writeln m_file.writeln 'const struct %s %s = {' % [resources_constant_name, resources_constant_name] m_file.section do |struct| enumerate_sections do |section_key, enumerate_keys| struct.writeln '.%s = {' % section_key struct.section do |section_struct| enumerate_keys.call do |key, value| section_struct.writeln '.%s = @"%s",' % [key, value] end end struct.writeln '},' end end m_file.writeln '};' end def enumerate_sections # Iterate sections ordered by key for section_key, section_content in @sections.sort # Pass section key and block to yield the keys ordered proc = Proc.new do |&block| for key, value in section_content.sort if value.is_a? Hash block.call key, value[:value], value[:comment] else block.call key, value, nil end end end yield section_key, proc end end end