Created
July 4, 2026 15:18
-
-
Save alex-quiterio/46ec80beae5132376b5683a0eefa1dca to your computer and use it in GitHub Desktop.
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| #!/usr/bin/env ruby | |
| # frozen_string_literal: true | |
| # ============================================================================= | |
| # human_design.rb — compute a Human Design chart (Type, Authority, Profile, | |
| # defined Centers, active gates/channels) from birth data. | |
| # | |
| # Human Design is derived from TWO charts: | |
| # 1. Personality (conscious): the exact moment of birth. | |
| # 2. Design (unconscious): the moment the Sun was exactly 88 degrees of arc | |
| # BEFORE its birth position (~88 days before birth). | |
| # The 13 planetary activations from each are mapped onto the 64 I Ching gates, | |
| # then onto channels and the 9 centers. | |
| # | |
| # --------------------------------------------------------------------------- | |
| # SETUP (run once in your own terminal — the sandbox that generated this | |
| # file cannot install gems or run Ruby): | |
| # | |
| # gem install swe4r | |
| # | |
| # swe4r bundles the Swiss Ephemeris, so no extra data files are needed for | |
| # dates within a few centuries of today. | |
| # | |
| # RUN: | |
| # ruby human_design.rb | |
| # | |
| # The script will prompt you interactively for birth date, time, and UTC offset. | |
| # | |
| # Pass --with-num to also compute a Pythagorean numerology reading (Life Path, | |
| # Birthday, Expression, Soul Urge, Personality numbers), which additionally | |
| # prompts for your full name: | |
| # ruby human_design.rb --with-num | |
| # ============================================================================= | |
| require "date" | |
| begin | |
| require "swe4r" | |
| rescue LoadError | |
| abort <<~MSG | |
| The 'swe4r' gem is not installed. | |
| Install it first with: | |
| gem install swe4r | |
| then re-run: ruby human_design.rb | |
| MSG | |
| end | |
| module HumanDesign | |
| # Interactive stdin prompts shared by anything that needs user input. | |
| # Uses STDIN explicitly rather than bare `gets`, since bare `gets` reads | |
| # from files named in ARGV (e.g. "--with-num") instead of the terminal. | |
| module Prompt | |
| module_function | |
| def ask(prompt) | |
| print "#{prompt}: " | |
| $stdout.flush | |
| STDIN.gets&.strip | |
| end | |
| def integer(prompt, range) | |
| loop do | |
| value = ask(prompt) | |
| return value.to_i if value =~ /\A-?\d+\z/ && range.include?(value.to_i) | |
| puts " Please enter a whole number in #{range}." | |
| end | |
| end | |
| def float(prompt) | |
| loop do | |
| value = ask(prompt) | |
| return value.to_f if value =~ /\A-?\d+(\.\d+)?\z/ | |
| puts " Please enter a number." | |
| end | |
| end | |
| def text(prompt) | |
| loop do | |
| value = ask(prompt) | |
| return value if value && !value.empty? | |
| puts " Please enter a value." | |
| end | |
| end | |
| end | |
| # Wraps the Swiss Ephemeris calls needed to get a body's ecliptic longitude. | |
| class Ephemeris | |
| FLAGS = Swe4r::SEFLG_SWIEPH | Swe4r::SEFLG_SPEED | |
| def self.julian_day(year, month, day, hour, min, utc_offset) | |
| ut_hour = hour + min / 60.0 - utc_offset | |
| Swe4r.swe_julday(year, month, day, ut_hour) | |
| end | |
| # Ecliptic longitude (0..360) of one body at a given Julian Day. | |
| def self.longitude(jd, body) | |
| case body | |
| when :earth | |
| (longitude(jd, Swe4r::SE_SUN) + 180.0) % 360.0 | |
| when :south_node | |
| (longitude(jd, Swe4r::SE_TRUE_NODE) + 180.0) % 360.0 | |
| else | |
| Swe4r.swe_calc_ut(jd, body, FLAGS)[0] % 360.0 | |
| end | |
| end | |
| end | |
| # Local civil birth date/time, entered interactively. | |
| class BirthData | |
| attr_reader :year, :month, :day, :hour, :min, :utc_offset | |
| def self.prompt | |
| puts "Enter birth data (local clock date/time):" | |
| data = new( | |
| year: Prompt.integer("Birth year (e.g. 1990)", 1800..2100), | |
| month: Prompt.integer("Birth month (1-12)", 1..12), | |
| day: Prompt.integer("Birth day (1-31)", 1..31), | |
| hour: Prompt.integer("Birth hour, 24h local clock (0-23)", 0..23), | |
| min: Prompt.integer("Birth minute (0-59)", 0..59), | |
| utc_offset: Prompt.float("UTC offset in hours (e.g. 1 for UTC+1, -5 for UTC-5)") | |
| ) | |
| puts | |
| data | |
| end | |
| def initialize(year:, month:, day:, hour:, min:, utc_offset:) | |
| @year = year | |
| @month = month | |
| @day = day | |
| @hour = hour | |
| @min = min | |
| @utc_offset = utc_offset | |
| end | |
| def julian_day | |
| Ephemeris.julian_day(year, month, day, hour, min, utc_offset) | |
| end | |
| def to_s | |
| "#{day}/#{month}/#{year} " \ | |
| "#{format('%02d:%02d', hour, min)} " \ | |
| "(UTC#{format('%+d', utc_offset)})" | |
| end | |
| end | |
| # One of the 64 I Ching gates plus a line number (1..6), derived from an | |
| # ecliptic longitude. | |
| class Gate | |
| # The 64 gates in zodiacal order around the 360° ecliptic. HD places | |
| # Gate 41 at the start (2° Aquarius region); each gate spans 5.625°. | |
| WHEEL = [ | |
| 41, 19, 13, 49, 30, 55, 37, 63, 22, 36, 25, 17, 21, 51, 42, 3, | |
| 27, 24, 2, 23, 8, 20, 16, 35, 45, 12, 15, 52, 39, 53, 62, 56, | |
| 31, 33, 7, 4, 29, 59, 40, 64, 47, 6, 46, 18, 48, 57, 32, 50, | |
| 28, 44, 1, 43, 14, 34, 9, 5, 26, 11, 10, 58, 38, 54, 61, 60 | |
| ].freeze | |
| # The wheel starts at this absolute ecliptic longitude (start of Gate 41). | |
| # 302° = 2° Aquarius. Standard offset used by mainstream HD software. | |
| START_DEG = 302.0 | |
| SIZE = 360.0 / 64.0 # 5.625° | |
| LINE_SIZE = SIZE / 6.0 # each gate has 6 lines | |
| attr_reader :number, :line | |
| def self.at(longitude) | |
| offset = (longitude - START_DEG) % 360.0 | |
| idx = (offset / SIZE).floor | |
| within = offset - idx * SIZE | |
| new(WHEEL[idx], (within / LINE_SIZE).floor + 1) | |
| end | |
| def initialize(number, line) | |
| @number = number | |
| @line = line | |
| end | |
| def center | |
| Center.containing(number) | |
| end | |
| def to_s | |
| "#{number}.#{line}" | |
| end | |
| end | |
| # One of the 9 centers in the BodyGraph, and which gates belong to it. | |
| class Center | |
| GATES_BY_NAME = { | |
| "Head" => [64, 61, 63], | |
| "Ajna" => [47, 24, 4, 17, 43, 11], | |
| "Throat" => [62, 23, 56, 35, 12, 45, 33, 8, 31, 20, 16], | |
| "G" => [1, 13, 25, 46, 2, 15, 10, 7], | |
| "Heart" => [21, 40, 26, 51], # Ego / Will | |
| "Spleen" => [48, 57, 44, 50, 32, 28, 18], | |
| "SolarPlexus" => [6, 37, 22, 36, 30, 55, 49], # Emotional | |
| "Sacral" => [34, 5, 14, 29, 59, 9, 3, 42, 27], | |
| "Root" => [58, 38, 54, 53, 60, 52, 19, 39, 41] | |
| }.freeze | |
| ALL = GATES_BY_NAME.keys.freeze | |
| MOTORS = %w[Sacral Heart SolarPlexus Root].freeze | |
| def self.containing(gate_number) | |
| GATES_BY_NAME.find { |_name, gates| gates.include?(gate_number) }&.first | |
| end | |
| end | |
| # One of the 36 channels connecting two gates (and thus two centers). | |
| class Channel | |
| CENTERS_BY_GATES = { | |
| [1, 8] => %w[G Throat], | |
| [2, 14] => %w[G Sacral], | |
| [3, 60] => %w[Sacral Root], | |
| [4, 63] => %w[Ajna Head], | |
| [5, 15] => %w[Sacral G], | |
| [6, 59] => %w[SolarPlexus Sacral], | |
| [7, 31] => %w[G Throat], | |
| [9, 52] => %w[Sacral Root], | |
| [10, 20] => %w[G Throat], | |
| [10, 34] => %w[G Sacral], | |
| [10, 57] => %w[G Spleen], | |
| [11, 56] => %w[Ajna Throat], | |
| [12, 22] => %w[Throat SolarPlexus], | |
| [13, 33] => %w[G Throat], | |
| [16, 48] => %w[Throat Spleen], | |
| [17, 62] => %w[Ajna Throat], | |
| [18, 58] => %w[Spleen Root], | |
| [19, 49] => %w[Root SolarPlexus], | |
| [20, 34] => %w[Throat Sacral], | |
| [20, 57] => %w[Throat Spleen], | |
| [21, 45] => %w[Heart Throat], | |
| [23, 43] => %w[Throat Ajna], | |
| [24, 61] => %w[Ajna Head], | |
| [25, 51] => %w[G Heart], | |
| [26, 44] => %w[Heart Spleen], | |
| [27, 50] => %w[Sacral Spleen], | |
| [28, 38] => %w[Spleen Root], | |
| [29, 46] => %w[Sacral G], | |
| [30, 41] => %w[SolarPlexus Root], | |
| [32, 54] => %w[Spleen Root], | |
| [34, 57] => %w[Sacral Spleen], | |
| [35, 36] => %w[Throat SolarPlexus], | |
| [37, 40] => %w[SolarPlexus Heart], | |
| [39, 55] => %w[Root SolarPlexus], | |
| [42, 53] => %w[Sacral Root], | |
| [47, 64] => %w[Ajna Head] | |
| }.freeze | |
| attr_reader :gates, :centers | |
| def self.all | |
| @all ||= CENTERS_BY_GATES.map { |gates, centers| new(gates, centers) } | |
| end | |
| def initialize(gates, centers) | |
| @gates = gates | |
| @centers = centers | |
| end | |
| def includes_gate?(gate_number) | |
| gates.include?(gate_number) | |
| end | |
| def active?(active_gate_numbers) | |
| gates.all? { |g| active_gate_numbers.include?(g) } | |
| end | |
| def to_s | |
| "#{gates.min}-#{gates.max} (#{centers.join(' <-> ')})" | |
| end | |
| end | |
| # The 13 bodies used in HD (Sun..Pluto + the lunar Nodes), in chart order. | |
| class Planet | |
| BODY_BY_NAME = { | |
| "Sun" => Swe4r::SE_SUN, | |
| "Earth" => :earth, # Sun + 180° | |
| "Moon" => Swe4r::SE_MOON, | |
| "NorthNode" => Swe4r::SE_TRUE_NODE, | |
| "SouthNode" => :south_node, # NorthNode + 180° | |
| "Mercury" => Swe4r::SE_MERCURY, | |
| "Venus" => Swe4r::SE_VENUS, | |
| "Mars" => Swe4r::SE_MARS, | |
| "Jupiter" => Swe4r::SE_JUPITER, | |
| "Saturn" => Swe4r::SE_SATURN, | |
| "Uranus" => Swe4r::SE_URANUS, | |
| "Neptune" => Swe4r::SE_NEPTUNE, | |
| "Pluto" => Swe4r::SE_PLUTO | |
| }.freeze | |
| ALL = BODY_BY_NAME.keys.freeze | |
| attr_reader :name | |
| def initialize(name) | |
| @name = name | |
| end | |
| def body | |
| BODY_BY_NAME[name] | |
| end | |
| def gate_at(jd) | |
| Gate.at(Ephemeris.longitude(jd, body)) | |
| end | |
| end | |
| # All 13 planetary gate/line activations for one moment in time (either the | |
| # Personality/conscious chart or the Design/unconscious chart). | |
| class ActivationSet | |
| def initialize(jd) | |
| @jd = jd | |
| @gates_by_planet = Planet::ALL.each_with_object({}) do |name, hash| | |
| hash[name] = Planet.new(name).gate_at(jd) | |
| end | |
| end | |
| def gate_for(planet_name) | |
| @gates_by_planet.fetch(planet_name) | |
| end | |
| def sun_gate | |
| gate_for("Sun") | |
| end | |
| def each_gate(&block) | |
| @gates_by_planet.each(&block) | |
| end | |
| def gate_numbers | |
| @gates_by_planet.values.map(&:number).uniq | |
| end | |
| end | |
| # Finds the Design Julian Day: the moment the Sun was exactly 88° of arc | |
| # before its birth longitude. Solved by bisection (~88 days earlier). | |
| class DesignMoment | |
| ARC_DEGREES = 88.0 | |
| SEARCH_WINDOW = (95.0..80.0) | |
| ITERATIONS = 40 | |
| def self.julian_day(birth_jd) | |
| new(birth_jd).julian_day | |
| end | |
| def initialize(birth_jd) | |
| @birth_jd = birth_jd | |
| @target = (Ephemeris.longitude(birth_jd, Swe4r::SE_SUN) - ARC_DEGREES) % 360.0 | |
| end | |
| def julian_day | |
| lo = @birth_jd - SEARCH_WINDOW.begin | |
| hi = @birth_jd - SEARCH_WINDOW.end | |
| ITERATIONS.times do | |
| mid = (lo + hi) / 2.0 | |
| diff = signed_difference(Ephemeris.longitude(mid, Swe4r::SE_SUN)) | |
| diff.positive? ? hi = mid : lo = mid | |
| end | |
| (lo + hi) / 2.0 | |
| end | |
| private | |
| def signed_difference(longitude) | |
| ((longitude - @target + 180.0) % 360.0) - 180.0 | |
| end | |
| end | |
| # Given the set of active gate numbers (from both charts), works out which | |
| # channels are complete, which centers are defined, and how they connect. | |
| class BodyGraph | |
| def initialize(active_gate_numbers) | |
| @active_gate_numbers = active_gate_numbers.uniq | |
| @active_channels = Channel.all.select { |c| c.active?(@active_gate_numbers) } | |
| @defined_centers = @active_channels.flat_map(&:centers).uniq | |
| @adjacency = build_adjacency | |
| end | |
| attr_reader :active_gate_numbers, :active_channels, :defined_centers | |
| def undefined_centers | |
| Center::ALL - defined_centers | |
| end | |
| def hanging_gates | |
| @hanging_gates ||= active_gate_numbers.reject do |g| | |
| active_channels.any? { |c| c.includes_gate?(g) } | |
| end | |
| end | |
| def hanging_gates_by_center | |
| hanging_gates.group_by { |g| Center.containing(g) } | |
| end | |
| def connected?(from_center, to_center) | |
| return false unless defined_centers.include?(from_center) | |
| reachable_from(from_center).include?(to_center) | |
| end | |
| def components | |
| seen = [] | |
| defined_centers.each_with_object([]) do |center, comps| | |
| next if seen.include?(center) | |
| comp = reachable_from(center) | |
| seen.concat(comp) | |
| comps << comp | |
| end | |
| end | |
| def definition_type | |
| case components.size | |
| when 0 then "No Definition" | |
| when 1 then "Single Definition" | |
| when 2 then "Split Definition" | |
| when 3 then "Triple Split Definition" | |
| when 4 then "Quadruple Split Definition" | |
| else "#{components.size}-Way Split Definition" | |
| end | |
| end | |
| def motor_connected_to_throat? | |
| return false unless defined_centers.include?("Throat") | |
| Center::MOTORS.any? { |m| defined_centers.include?(m) && connected?(m, "Throat") } | |
| end | |
| private | |
| def build_adjacency | |
| adj = Hash.new { |h, k| h[k] = [] } | |
| active_channels.each do |c| | |
| a, b = c.centers | |
| adj[a] << b | |
| adj[b] << a | |
| end | |
| adj | |
| end | |
| def reachable_from(start) | |
| seen = [] | |
| stack = [start] | |
| until stack.empty? | |
| n = stack.pop | |
| next if seen.include?(n) | |
| seen << n | |
| @adjacency[n].each { |m| stack << m unless seen.include?(m) } | |
| end | |
| seen | |
| end | |
| end | |
| # The full reading: Type, Strategy, Authority, Profile, Definition — derived | |
| # from a BirthData's Personality and Design activation sets. | |
| class Chart | |
| STRATEGIES = { | |
| "Generator" => "To respond", | |
| "Manifesting Generator" => "To respond, then inform", | |
| "Manifestor" => "To inform before you act", | |
| "Projector" => "Wait for the invitation", | |
| "Reflector" => "Wait a lunar cycle before deciding" | |
| }.freeze | |
| NOT_SELF_THEMES = { | |
| "Generator" => "Frustration", | |
| "Manifesting Generator" => "Frustration and Anger", | |
| "Manifestor" => "Anger", | |
| "Projector" => "Bitterness", | |
| "Reflector" => "Disappointment" | |
| }.freeze | |
| attr_reader :birth, :personality, :design, :body_graph | |
| def initialize(birth_data) | |
| @birth = birth_data | |
| birth_jd = birth_data.julian_day | |
| @personality = ActivationSet.new(birth_jd) | |
| @design = ActivationSet.new(DesignMoment.julian_day(birth_jd)) | |
| @body_graph = BodyGraph.new(personality.gate_numbers + design.gate_numbers) | |
| end | |
| def type | |
| return "Reflector" if body_graph.defined_centers.empty? | |
| if body_graph.defined_centers.include?("Sacral") | |
| body_graph.motor_connected_to_throat? ? "Manifesting Generator" : "Generator" | |
| elsif body_graph.motor_connected_to_throat? | |
| "Manifestor" | |
| else | |
| "Projector" | |
| end | |
| end | |
| def strategy | |
| STRATEGIES.fetch(type) | |
| end | |
| def not_self_theme | |
| NOT_SELF_THEMES.fetch(type) | |
| end | |
| # Highest-priority defined center present. | |
| def authority | |
| return "Emotional (Solar Plexus)" if center_defined?("SolarPlexus") | |
| return "Sacral" if center_defined?("Sacral") | |
| return "Splenic" if center_defined?("Spleen") | |
| return "Ego / Heart" if center_defined?("Heart") | |
| return "Self-Projected (G)" if center_defined?("G") | |
| return "Lunar" if type == "Reflector" | |
| "Mental / Environmental (no inner authority)" | |
| end | |
| def profile | |
| "#{personality.sun_gate.line}/#{design.sun_gate.line}" | |
| end | |
| private | |
| def center_defined?(name) | |
| body_graph.defined_centers.include?(name) | |
| end | |
| end | |
| # Pythagorean numerology reading, derived from a full name and BirthData. | |
| # Only ASCII letters A-Z contribute to the letter-based numbers. | |
| class Numerology | |
| LETTER_VALUES = { | |
| "A" => 1, "B" => 2, "C" => 3, "D" => 4, "E" => 5, "F" => 6, "G" => 7, "H" => 8, "I" => 9, | |
| "J" => 1, "K" => 2, "L" => 3, "M" => 4, "N" => 5, "O" => 6, "P" => 7, "Q" => 8, "R" => 9, | |
| "S" => 1, "T" => 2, "U" => 3, "V" => 4, "W" => 5, "X" => 6, "Y" => 7, "Z" => 8 | |
| }.freeze | |
| VOWELS = %w[A E I O U].freeze | |
| MASTER_NUMBERS = [11, 22, 33].freeze | |
| attr_reader :full_name, :birth | |
| def initialize(full_name, birth) | |
| @full_name = full_name | |
| @birth = birth | |
| end | |
| # Reduce day, month, and year separately, then sum and reduce again. | |
| def life_path_number | |
| reduce(reduce(birth.day) + reduce(birth.month) + reduce(birth.year)) | |
| end | |
| def birthday_number | |
| reduce(birth.day) | |
| end | |
| def expression_number | |
| reduce(letter_values(letters).sum) | |
| end | |
| def soul_urge_number | |
| reduce(letter_values(letters.select { |l| VOWELS.include?(l) }).sum) | |
| end | |
| def personality_number | |
| reduce(letter_values(letters.reject { |l| VOWELS.include?(l) }).sum) | |
| end | |
| private | |
| def letters | |
| full_name.upcase.scan(/[A-Z]/) | |
| end | |
| def letter_values(chars) | |
| chars.map { |c| LETTER_VALUES.fetch(c) } | |
| end | |
| def reduce(number) | |
| loop do | |
| return number if number <= 9 || MASTER_NUMBERS.include?(number) | |
| number = number.digits.sum | |
| end | |
| end | |
| end | |
| # Formats a Chart as plain-text output. | |
| class ChartReport | |
| def initialize(chart, numerology: nil) | |
| @chart = chart | |
| @numerology = numerology | |
| end | |
| def render | |
| render_header | |
| puts | |
| render_summary | |
| puts | |
| render_centers | |
| puts | |
| render_channels | |
| puts | |
| render_hanging_gates | |
| puts | |
| render_activations | |
| puts | |
| render_footer | |
| if numerology | |
| puts | |
| render_numerology | |
| end | |
| end | |
| private | |
| attr_reader :chart, :numerology | |
| def render_header | |
| puts "=" * 60 | |
| puts "HUMAN DESIGN CHART" | |
| puts "Birth: #{chart.birth}" | |
| puts "=" * 60 | |
| end | |
| def render_summary | |
| bg = chart.body_graph | |
| puts "TYPE : #{chart.type}" | |
| puts "STRATEGY : #{chart.strategy}" | |
| puts "NOT-SELF THEME: #{chart.not_self_theme}" | |
| puts "AUTHORITY : #{chart.authority}" | |
| puts "PROFILE : #{chart.profile}" | |
| puts "DEFINITION : #{bg.definition_type}" | |
| return unless bg.components.size > 1 | |
| puts " #{bg.components.map { |c| c.sort.join('+') }.join(' | ')}" | |
| end | |
| def render_centers | |
| bg = chart.body_graph | |
| puts "DEFINED CENTERS (#{bg.defined_centers.size}/9):" | |
| puts " #{bg.defined_centers.sort.join(', ')}" | |
| puts | |
| puts "OPEN / UNDEFINED CENTERS:" | |
| puts " #{bg.undefined_centers.sort.join(', ')}" | |
| end | |
| def render_channels | |
| channels = chart.body_graph.active_channels | |
| puts "ACTIVE CHANNELS (#{channels.size}):" | |
| channels.sort_by { |c| c.gates.min }.each { |c| puts " #{c}" } | |
| end | |
| def render_hanging_gates | |
| bg = chart.body_graph | |
| puts "HANGING GATES (#{bg.hanging_gates.size}):" | |
| if bg.hanging_gates.empty? | |
| puts " none" | |
| else | |
| bg.hanging_gates_by_center.sort.each do |center, gates| | |
| puts " #{center}: #{gates.sort.join(', ')}" | |
| end | |
| end | |
| end | |
| def render_activations | |
| puts "ACTIVATIONS (Personality = conscious, Design = unconscious):" | |
| puts format(" %-10s %-12s %-12s", "", "Personality", "Design") | |
| Planet::ALL.each do |name| | |
| p_gate = chart.personality.gate_for(name) | |
| d_gate = chart.design.gate_for(name) | |
| puts format(" %-10s %-12s %-12s", name, p_gate.to_s, d_gate.to_s) | |
| end | |
| end | |
| def render_footer | |
| puts "Note: verify against myBodyGraph or Genetic Matrix. The wheel offset" | |
| puts "and node method (True Node) match mainstream HD software; tiny" | |
| puts "differences near a gate/line boundary can shift a line by one." | |
| end | |
| def render_numerology | |
| puts "-" * 60 | |
| puts "NUMEROLOGY (#{numerology.full_name})" | |
| puts "-" * 60 | |
| puts "LIFE PATH : #{numerology.life_path_number}" | |
| puts "BIRTHDAY : #{numerology.birthday_number}" | |
| puts "EXPRESSION : #{numerology.expression_number}" | |
| puts "SOUL URGE : #{numerology.soul_urge_number}" | |
| puts "PERSONALITY : #{numerology.personality_number}" | |
| end | |
| end | |
| end | |
| if __FILE__ == $PROGRAM_NAME | |
| with_numerology = ARGV.include?("--with-num") | |
| birth = HumanDesign::BirthData.prompt | |
| chart = HumanDesign::Chart.new(birth) | |
| numerology = nil | |
| if with_numerology | |
| full_name = HumanDesign::Prompt.text("Full name (for numerology)") | |
| numerology = HumanDesign::Numerology.new(full_name, birth) | |
| puts | |
| end | |
| HumanDesign::ChartReport.new(chart, numerology: numerology).render | |
| end |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment