0

I am building a website that can store body measurements and calculate body fat using jackson/ pollock 7 method formula. I am pretty new to rails and not sure how I can output the calculation from the measurements controller into the show.html.

Measurements_controller.rb calculate method:

def calculate

  bodyDensityMale = 1.112 - (0.00043499 * (@measurement.chest + @measurement.Midaxillary + @[email protected][email protected]
[email protected][email protected])) + (0.00000055 * (@[email protected][email protected][email protected][email protected]
[email protected][email protected])^2) - (0.00028826 * @measurement.age)

  bodyDensityFemale = 1.097 - (0.00046971 * (@measurement.chest + @[email protected][email protected][email protected]
[email protected][email protected])) + (0.00000056 * (@[email protected][email protected][email protected][email protected]
[email protected][email protected])^2) - (0.00012828 * @measurement.age)

  bodyFatMale = (495 / bodyDensityMale) - 450
  bodyFatFemale = (495 / bodyDensityFemale) - 450

  if (@measurement.gender_type = 'Male')
    puts bodyFatMale
  else
    puts bodyFatFemale
  end
end

show.html.erb

<p>
    <strong>Body Fat (%): </strong>
    <%= @measurement.calculate %>
</p>

When I create the entry there are no errors but the Body fat % field shows up empty. I appreciate any help!

1
  • why do you need any of this complex logic in the controller? where is '@mesurement` defined? If it's an instance of a model, then move this logic to a model method. Commented Apr 25, 2018 at 18:47

2 Answers 2

2

It seems like your primary problem is that you're calling calculate on @measurement here:

<p>
  <strong>Body Fat (%): </strong>
  <%= @measurement.calculate %>
</p>

But, it looks like the calculate logic is in the controller. I don't know what the calculate method looks like on @measurement, but apparently it's return nil or some such thing.

If I were you, I'd move all this logic into a BodyFatCalculator class, something like:

class BodyFatCalculator

  # using a constant makes it a *little* easier to make sure you don't have
  # a typo in your constant values
  CONST_VALUES = {
    male: {
      one:    1.112,
      two:    0.00043499,
      three:  0.00000055,
      four:   0.00028826
    },
    female: {
      one:    1.097,
      two:    0.00046971,
      three:  0.00000056,
      four:   0.00012828
    }
  }

  # this just defines the methods 'const_one', 'const_two', etc. that
  # return the appropriate constant from above. The const_val
  # method is defined below.
  [:one, :two, :three, :four].each do |const_ref|
    define_method("const_#{const_ref}") do 
      const_val const_ref
    end
  end

  # provides a getter/setter for @measurement
  attr_accessor :measurement

  class << self

    # class-level method that lets you do BodyFatCalculator.calculate.
    # Requires that a 'measurement' is passed in as an argument.
    def calculate(measurement)
      new(measurement).calculate
    end

  end

  # sets the @measurement instance variable.
  def initialize(measurement)
    @measurement = measurement
  end

  # does the calculation
  def calculate
    (495 / body_density) - 450
  end

  # calculates body density. I think this is a *little* more readable
  # so that it's easier to see if you have the formula right. The 'const_one',
  # 'const_two', etc. methods were defined at the top. 
  # The `sum_two` method is defined below.
  def body_density
    const_one - ( const_two * sum_two ) + ( const_three * sum_two^2 ) + ( const_four * measurement.age )
  end

  # given a type (like ':one', ':two', etc.), uses 'measurement.gender_type'
  # to return the correct constant.
  def const_value(type)
    CONST_VALUES[measurement.gender_type.underscore.to_sym][type]
  end

  # given an array of names, will sum the values from '@measurement'
  # that correspond to the name.
  def sum_of *names
    names.each_with_object(0) do |name, sum|
      sum += measurement.send(name)
    end
  end

  # sum of a specific set of values from '@measurement'.
  def sum_two
    @sum_two ||= sum_of(*w(chest Midaxillary Tricep Subscapular Abdominal Suprailiac Thigh))
  end

end

(WARNING: Hasn't been tested.)

Then, in your controller, do something like:

def show
  @body_fat = BodyFatCalculator.calculate(@measurement)
end

And in your view:

<p>
  <strong>Body Fat (%): </strong>
  <%= @body_fat %>
</p>
Sign up to request clarification or add additional context in comments.

Comments

1

A few notes:

1) Be sure to follow Ruby naming conventions: use snake case for symbols, methods, and variables (e.g. bodyDensityMale should be body_density_male and Midaxillary, Tricep, etc. should be midaxillary, tricep)

2) ^ is the Ruby binary XOR operator. ** is for exponents.

3) There is a bug in your controller condition due to = vs ==.

if (@measurement.gender_type = 'Male')

should be

if (@measurement.gender_type == 'Male')

The first is assigning the value, so it will always be true. == is for equality comparison.

The calculation logic is best moved out of the controller and into a model or service class. Here are two solutions for putting it in the Measurement model:

################################################################################
# Solution #1
################################################################################
class Measurement
  MALE = 'Male'

  def body_fat
    (495 / body_density) - 450
  end

  def body_density
    gender_type == MALE ? body_density_male : body_density_female
  end

  def body_density_male
    1.112 -
    (0.00043499 * body_density_sum) +
    (0.00000055 * body_density_sum ** 2) -
    (0.00028826 * age)
  end

  def body_density_female
    1.097 -
    (0.00046971 * body_density_sum) +
    (0.00000056 * body_density_sum ** 2) -
    (0.00012828 * age)
  end

  def body_density_sum
    [chest, midaxillary, tricep, subscapular, abdominal, suprailiac, thigh].sum
  end
end

################################################################################
# Solution #2 using subclasses
################################################################################
class Measurement
  def body_fat
    (495 / body_density) - 450
  end

  def body_density_sum
    [chest, midaxillary, tricep, subscapular, abdominal, suprailiac, thigh].sum
  end
end

class MaleMeasurement < Measurement
  def body_density
    1.112 -
    (0.00043499 * body_density_sum) +
    (0.00000055 * body_density_sum ** 2) -
    (0.00028826 * age)
  end
end

class FemaleMeasurement < Measurement
  def body_density
    1.097 -
    (0.00046971 * body_density_sum) +
    (0.00000056 * body_density_sum ** 2) -
    (0.00012828 * age)
  end
end

To use Solution #2 with subclasses you would have to have set up the Measurement model and database table using single table inheritance. Solution #1 would require no database changes and is probably quickest/easiest (you could refactor down the road).

With either solution though, in your view:

<p>
  <strong>Body Fat (%): </strong>
  <%= @measurement.body_fat %>
</p>

1 Comment

Or, Solution #3, you could create a BodyFatCalculator class... ;-) Nice work!

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.