Trestle.resource(:users) do menu do item :users, icon: "fa fa-user", priority: 1 end form do |item| concat Trestle::Form::Automatic.new(admin).render(self, item) text_area :objective end # Customize the table columns shown on the index view. table do column :name column :created_at, align: :center actions do |toolbar, instance, admin| toolbar.edit if admin && admin.actions.include?(:edit) toolbar.delete if admin && admin.actions.include?(:destroy) toolbar.link 'CV', instance, action: :generate, method: :get, style: :primary, icon: "fa fa-file-pdf", target: "_blank" toolbar.link 'CLS', instance, action: :generate_cls, method: :get, style: :secondary, icon: "fa", target: "_blank" toolbar.link 'JSON', instance, action: :generate_json, method: :get, style: :secondary, icon: "fa", target: "_blank" end end controller do def generate tex_content = generate_tex destination_dir = "#{Rails.root}/app/views/templates/awesome" `cd #{destination_dir} && make clean` File.open("#{destination_dir}/resume.tex", "w") do |f| f << tex_content end `cd #{destination_dir} && make resume.pdf` render file: "#{destination_dir}/resume.pdf", layout: false end def generate_cls tex_content = generate_tex destination_dir = "#{Rails.root}/app/views/templates/awesome" File.open("#{destination_dir}/resume.tex", "w") do |f| f << tex_content end render file: "#{destination_dir}/resume.tex", layout: false end def generate_json generate_json_resume end def generate_json_resume user = admin.find_instance(params) split_list = lambda do |value| value.to_s.split(/[;,]/).map { |item| item.strip }.reject(&:blank?) end parse_period = lambda do |period| years = period.to_s.scan(/\b(19\d{2}|20\d{2})\b/).flatten return [nil, nil] if years.empty? [years[0], years[1]] end normalize_url = lambda do |value| return nil if value.blank? value.match?(/\Ahttps?:\/\//) ? value : "https://#{value}" end profile_from = lambda do |value, network, base_url| return nil if value.blank? if value.include?("/") || value.include?(".") url = normalize_url.call(value) username = url.to_s.split("/").last&.delete_prefix("@") else username = value.delete_prefix("@") url = base_url.present? ? "#{base_url}/#{username}" : nil end profile = { "network" => network } profile["username"] = username if username.present? profile["url"] = url if url.present? profile end basics = { "name" => user.name, "label" => user.title, "email" => user.email, "phone" => user.phone, "summary" => user.objective }.compact if user.address.present? basics["location"] = { "address" => user.address } end primary_url = user.other_page_url.presence || user.linkedin.presence || user.github.presence basics["url"] = normalize_url.call(primary_url) if primary_url.present? profiles = [] profiles << profile_from.call(user.linkedin, "LinkedIn", "https://www.linkedin.com/in") profiles << profile_from.call(user.github, "GitHub", "https://github.com") profiles << profile_from.call(user.other_page_url, "Website", nil) profiles.compact! basics["profiles"] = profiles if profiles.any? work = user.work_experiences.active.map do |work_entry| start_date, end_date = parse_period.call(work_entry.period) highlights = split_list.call(work_entry.items_csv) technologies = split_list.call(work_entry.technologies) highlights << "Technologies: #{technologies.join(', ')}" if technologies.any? summary = work_entry.achivements.presence summary = work_entry.period.presence if summary.blank? && start_date.blank? && end_date.blank? entry = { "name" => work_entry.employer, "position" => work_entry.title }.compact entry["summary"] = summary if summary.present? entry["highlights"] = highlights if highlights.any? entry["startDate"] = start_date if start_date.present? entry["endDate"] = end_date if end_date.present? if entry["startDate"].blank? && entry["endDate"].blank? && work_entry.reference_date.present? entry["endDate"] = work_entry.reference_date.iso8601 end entry end education = user.education_entries.active.map do |education_entry| start_date, end_date = parse_period.call(education_entry.period) study_type = education_entry.title area = nil if study_type.present? && study_type.match?(/\s+in\s+/i) study_type, area = study_type.split(/\s+in\s+/i, 2) end courses = split_list.call(education_entry.items_csv) entry = { "institution" => education_entry.institution }.compact entry["studyType"] = study_type if study_type.present? entry["area"] = area if area.present? entry["courses"] = courses if courses.any? entry["startDate"] = start_date if start_date.present? entry["endDate"] = end_date if end_date.present? if entry["startDate"].blank? && entry["endDate"].blank? && education_entry.reference_date.present? entry["endDate"] = education_entry.reference_date.iso8601 end entry end skills = user.skills.active.map do |skill| keywords = split_list.call(skill.detail) entry = { "name" => skill.title }.compact if keywords.any? entry["keywords"] = keywords elsif skill.detail.present? entry["level"] = skill.detail end entry end resume = { "basics" => basics } resume["work"] = work if work.any? resume["education"] = education if education.any? resume["skills"] = skills if skills.any? render json: resume, layout: false end def generate_tex(template: "resume") user = admin.find_instance(params) ac = ActionController::Base.new() ac.render_to_string(layout: false, template: "templates/awesome/#{template}", locals: { "@user": user } ) end end routes do get :generate, on: :member get :generate_cls, on: :member get :generate_json, on: :member end end