# Some gems for some useful functions I've made

**URL:** <https://www.ruby-forum.com/t/some-gems-for-some-useful-functions-ive-made/263694>\
**Category:** Development\
**Created:** [March 2, 2023, 11:01pm UTC](https://www.ruby-forum.com/t/some-gems-for-some-useful-functions-ive-made/263694 "2023-03-02T23:01:08Z")\
**Posts on this page:** 7\
**Page:** 1

<div class="post-metadata">

**Author:** ![jgomo3](https://www.ruby-forum.com/user_avatar/www.ruby-forum.com/jgomo3/32/950_2.png) [@jgomo3](https://www.ruby-forum.com/u/jgomo3)\
**Post date:** [March 2, 2023, 11:01pm UTC](https://www.ruby-forum.com/t/some-gems-for-some-useful-functions-ive-made/263694/1 "2023-03-02T23:01:09Z")

</div>

I’ve made some gems for functions I’ve used in private projects that I found somehow useful.

They are small, focusing on a few functionalities.

Funny enough, I found myself using only a couple of those functions.

The main one ([jgomo3-func](https://github.com/jgomo3/jgomo3-func)) is `then-if` (or `then-unless`) which allows me to write patterns like this:

```auto
data.then_if(&:predicate?){ process(_1) }

```

Another one ([ruby\_case\_struct](https://github.com/jgomo3/ruby_case_struct)) allows me to write `case when` expressions as Hash-like structures. Example:

```auto
cases = {
  (1..5) => :low,
  (6..10) => :moderate,
  (11..) => :high,
}.extend(CaseStruct)

cases[3] # => :low
cases[8] # => :moderate
cases[100] # => :high_brightness: 

```

Which I think is useful for writing simple rule engines.

The last one ([procer](https://github.com/jgomo3/procer)) defines a default `to_proc` for all objects. So we can use all objects as blocks with some intuitive results:

```auto
[1, 2, '3', '4', 5, 6].filter(&Numeric)
# => [1, 2, 5, 6]

[-10, 100, -2, 3, 20, -33].filter(&(0..50))
# => [3, 20]

```

What do you think about such functionalities? Any feedback is welcome.

---

<div class="post-metadata">

**Author:** ![AndyMaleh](https://www.ruby-forum.com/user_avatar/www.ruby-forum.com/andymaleh/32/2475_2.png) [@AndyMaleh](https://www.ruby-forum.com/u/AndyMaleh)\
**Post date:** [March 5, 2023, 1:48pm UTC](https://www.ruby-forum.com/t/some-gems-for-some-useful-functions-ive-made/263694/2 "2023-03-05T13:48:19Z")

</div>

In some projects that I’ve worked on, I sometimes discover code like this that people think is clever:

> [@jgomo3](#):
>
> `data.then_if(&:predicate?){ process(_1) }`

I just rewrite like this:

```ruby
process(data) if data.predicate?

```

Boom! That’s a lot simpler and more readable! I don’t understand why some people like to write weird code like that thinking it is useful. It’s not useful at all. Also, the new numbered block variables are only meant to be used for Ruby scripts that are replicating bash scripts with `$1` variables. They are not readable at all in standard business applications since you’d have to go back while reading to remember what the `_1` is. It would be way better if it is a variable name like `value`, `customer`, `order`, etc…

Just write idiomatic Ruby code! That’s what is demanded of collaborative Ruby software engineers who want to enable code to be readable and maintainable by others in a team, not just by themselves as individuals.

Here is another example:

```ruby
cases = {
  (1..5) => :low,
  (6..10) => :moderate,
  (11..) => :high,
}.extend(CaseStruct)

cases[3] # => :low
cases[8] # => :moderate
cases[100] # => :high_brightness: 

```

Just write this:

```ruby
def status(value)
  case value
  when 1..5
    :low
  when 6..10
    :moderate
  when 11..
    :high
  end
end

status(3) # => :low 
status(8) # => :moderate
status(100) # => :high_brightness

```

Boom! That’s a lot more Ruby-idiomatic and readable by newcomers to the codebase. It’s just standard Ruby.

There is no need for over-engineering or meta-programming if it only saves a couple lines of code. Usually meta-programming is only useful when it saves tens, hundreds or thousands of lines of code, or provides a language that is closer to reality (not farther as in the examples above). Otherwise, it’s considered over-engineering that makes code look foreign to newcomers to the codebase.

If you really want to use a hash though, you could do something like this:

```ruby
RANGE_STATUSES = {
  (1..5) => :low,
  (6..10) => :moderate,
  (11..) => :high,
}

def status(value)
  RANGE_STATUSES.find {|range, status| range.include?(value)}[1]
end

status(3) # => :low 
status(8) # => :moderate
status(100) # => :high_brightness

```

This code is still basic Ruby. No special meta-programming was involved. If it is preferred to use a hash, consider this approach first before looking into meta-programming.

Now, if you want to generalize the solution to reuse many times (not just once), you can still avoid meta-programing by having users rely on a normal class instead of the weird `extend`:

```ruby
# an incomplete naive implementation that does not handle edge cases
class RangeHash
  def initialize(hash)
    @hash = hash
  end

  def [](value)
    @hash.find {|range, status| range.include?(value)}[1]
  end
end

# This is used as a normal class now instead of extending hash
status = RangeHash.new(
  (1..5) => :low,
  (6..10) => :moderate,
  (11..) => :high,
)

status[3] # => :low 
status[8] # => :moderate
status[100] # => :high_brightness

```

Users can now read the code and understand it more simply given that `RangeHash` is a standard class that enables picking values based on `Range` keys, so there is no mysterious meta-programming. It’s just standard Ruby.

Everytime I catch over-use of meta-programming in my projects by other developers, I delete it and replace it with simple Ruby idiomatic code while lecturing those developers on how to write Ruby-idiomatic code that enables collaboration with others in a team.

Don’t listen to public blog posts about writing “functional”-style Ruby code without applying your own software engineering reasoning to the problem. Many blog posts on the Internet are written by uneducated people who do programming through monkey-see-monkey-do because they never got a full 4-year Computer Science (or computer related) university degree (they are either self-taught or only went to a short bootcamp), so their knowledge is very weak (they never mastered Object-Oriented Programming), and they tend to repeat things they heard from others that they deem “important” without them truly understanding the words they repeat. You can easily reveal their lack of depth of knowledge by simply asking them a question outside the words they repeat, and those programmers will always break down and fail at answering your question.

Functional programming is only useful for implementing highly mathematical logic, but for most business applications, Object-Oriented Programming results in the most productive and maintainable code that is closest to reality. Ruby is a hybrid OO/FP language. You should take advantage of that and use the right tool for the job instead of trying to force functional style everywhere like what bad unpragmatic programmers do (who are 60% of programmers nowadays sadly).

In conclusion, write Ruby-idiomatic code, don’t re-invent the wheel, think of other developers that might join your project team, use the right software paradigm for the job (like OOP), and avoid meta-programming (e.g. CaseStruct) unless it is absolutely needed, which is very rare.

---

<div class="post-metadata">

**Author:** ![jgomo3](https://www.ruby-forum.com/user_avatar/www.ruby-forum.com/jgomo3/32/950_2.png) [@jgomo3](https://www.ruby-forum.com/u/jgomo3)\
**Post date:** [March 5, 2023, 3:18pm UTC](https://www.ruby-forum.com/t/some-gems-for-some-useful-functions-ive-made/263694/3 "2023-03-05T15:18:55Z")

</div>

Thank you very much for your honest feedback @AndyMaleh . That’s precisely what I was looking for, some honest opinion about these ideas.

I foresaw rejection as these ideas contrast with those principles you list (shared by most of the Ruby community) at first sight. I’m conscious of those, that’s why I was looking for feedback.

There is obviously justification for each of the functions, but as with everything in our field (and everywhere)… “it depends” 😃.

I’ll present those functions’ justifications as I know these are easy points to miss, but I would like to know if those are still overengineering from your point of view.

# then-if

First a small correction, the equivalent form should be a ternary expression, not the simple guarded by an `if` expression, because `thien-if` yield `self` if `condition` is false. But your point is still valid, you would prefer:

```auto
data.predicate? ? process(data) : data

```

(or something similar),  
Over:

```auto
data.then_if(&:predicate?){ process(_1) }

```

Now the justification.

The idea of `then-if` is to be something similar to the standard Ruby function `then`. If `then` has it’s place, why not `then-if`? That’s a first one. But the main point is (as it is for `then`) to be used in chaining expressions.

So if you have some code in pipeline style:

```auto
data.then{}.then{}...then{}

```

You can add a new step with:

```auto
data.then{}.then_if(Condition){}...then{}

```

It could be useful in the same places where `then` is or could be useful.

There is a technique that I’ve been thinking that could make this function unnecessary, and it is that `then` without block returns an **Enumerator** that could be used for that conditional part, but I can’t find an easy way to express the idea of “Apply the block only if the condition is true and let the object flow otherwise”. For completion, check this example from “[then](https://docs.ruby-lang.org/en/3.2/Kernel.html#method-i-then)” documentation which illustrates the idea:

```auto
# meets condition, no-op
1.then.detect(&:odd?) # => 1
# does not meet condition, drop value
2.then.detect(&:odd?) # => nil

```

# CaseStruct

The point that you could have missed here is the dynamic nature of this construct. That dynamism is the justification over plain `case .. when ... end`, obviously, when needed.

One example use case is a simple rule engine, to which you can add or remove rules dynamically, something you can’t achieve with static text (`case...when` expressions).

CaseStrcut could be an alternative to create such Rule Engines with its own API for adding, inspecting, and removing rules. Obviously, this is valid only if the Hash API is good enough for your needs.

If it is not, then one must write a complaint system with whatever API and capabilities are needed. The traditional way would be a class RuleEngine, etc.

# Conclusion

Even so, I know how I justified them, but I still want to find alternative ways to achieve the same with plain Ruby to compare and see the actual value of the gems.

`then_if` is for attaching stages in a pipeline expression. Is it cleaner than `then.<enumerator_method>(...)`, or than `then{ condition(_1) ? process(_1) : _1 }` ?

`CaseStruct`, dynamic `case ... when` expression (for when you need the dynamism): is there something in the Ruby ecosystem doing the same thing?

---

<div class="post-metadata">

**Author:** ![jgomo3](https://www.ruby-forum.com/user_avatar/www.ruby-forum.com/jgomo3/32/950_2.png) [@jgomo3](https://www.ruby-forum.com/u/jgomo3)\
**Post date:** [March 5, 2023, 4:22pm UTC](https://www.ruby-forum.com/t/some-gems-for-some-useful-functions-ive-made/263694/4 "2023-03-05T16:22:22Z")

</div>

### Update

@AndyMaleh, I see you got the dynamism idea before noticing my comments. Good, you get the point.

I also get that you find the style of extending objects directly with modules not pleasant. Indeed it is not something of common use in the Ruby ecosystem.

So now I think it is a good idea to provide the class (like the `RangeHash` of your example) to be used to construct the CaseStrcut objects.

As it is now, the module could be included by any class, all it needs to provide is an implementation of `find`. So my plan is:

1. Rename the module to `AsCaseStruct`
  1. Update the dependent code (this is backward incompatible change, but I like this whole idea).

2. Create a class `CaseStruct`.

I think the implementation would be then:

```auto
require 'forwardable'

class CaseStruct
  include Forewardable
  include AsCaseStruct

  def_delegator :@hash, :find

  def initialize(hash)
    @hash = hash
  end
end

```

Then users could use the class, include the module, or extend the module directly by objects. Similar to what `Forewardable` and `SingleForewardable` do.

Anyways, thank you very much for your feedback and time.

### Update

I missed the point now… what about the Hash API? to get it, I have to make the class extend Hash. I don’t know about that? What are the caveats of having a class that inherits from Hash? Some languages sometimes have this problem, that it is not possible to simply extend base data structures without problems.

---

<div class="post-metadata">

**Author:** ![AndyMaleh](https://www.ruby-forum.com/user_avatar/www.ruby-forum.com/andymaleh/32/2475_2.png) [@AndyMaleh](https://www.ruby-forum.com/u/AndyMaleh)\
**Post date:** [March 5, 2023, 6:41pm UTC](https://www.ruby-forum.com/t/some-gems-for-some-useful-functions-ive-made/263694/5 "2023-03-05T18:41:41Z")

</div>

You are right. There are different options to implement the range hash (or case struct), like inheritance through extending `Hash` or delegation through decorating `Hash` ([Decorator Pattern](https://en.wikipedia.org/wiki/Decorator_pattern) forwarding all methods to `Hash`). Like you said, “it depends” on the requirements. If all what is needed is the `[]` method, then there is no need to extend a `Hash` or even decorate it. But, if there is a need to use the object as a regular `Hash` as well, then you could extend it or decorate it. The benefit of decoration is enabling runtime change of an existing `Hash`’s behavior. But, if the behavior is wanted always, then inheritance in a `CaseStruct` might be good enough.

---

<div class="post-metadata">

**Author:** ![AndyMaleh](https://www.ruby-forum.com/user_avatar/www.ruby-forum.com/andymaleh/32/2475_2.png) [@AndyMaleh](https://www.ruby-forum.com/u/AndyMaleh)\
**Post date:** [March 5, 2023, 6:51pm UTC](https://www.ruby-forum.com/t/some-gems-for-some-useful-functions-ive-made/263694/6 "2023-03-05T18:51:09Z")

</div>

Concerning this code:

> [@jgomo3](#):
>
> `data.predicate? ? process(data) : data`

I am glad you explained it because that reveals an issue with the weird use of `then_if` as I did not understand its purpose correctly upon first seeing it. That is usually a sign of a weak software design and a code smell.

In Object-Oriented Programming, you could easily eliminate the condition you have by using polymorphism, which puts the responsibility of processing data on the data itself, and depending on its type, different things would happen, so the could just becomes this:

```ruby
data.process

```

The data will know itself whether to process itself based on its own hidden predicate or not. To the outside observer, we have fully delegated the responsibility of processing to the data, and do NOT have to worry about the logic anymore. This is proper Object-Oriented Design. As mentioned before, needing something as convoluted as `then_if` is a code smell that was pointing out the bad design of the code. Of course, weaker programmers might try to address the weakness of design by inventing brand new unnecessary concepts when they could have better designed their code following proper OOP.

In any case, there are other options if we do not want to shift the `process` responsibility to the data itself (although that is usually recommended in Object-Oriented Programming). Alternatively, we could shift the responsibility of the predicate to the `process` method like this:

```ruby
def process(data)
  return data unless data.predicate?

  # do work and return processed data
end

```

Now, the main code becomes this:

```ruby
process(data)

```

Again, in this case, we hid the details of the predicate completely, thus resulting in much simpler code than the one doing `then_if`, thus removing the need for that foreign construct completely by relying on standard Ruby code techniques.

---

<div class="post-metadata">

**Author:** ![AndyMaleh](https://www.ruby-forum.com/user_avatar/www.ruby-forum.com/andymaleh/32/2475_2.png) [@AndyMaleh](https://www.ruby-forum.com/u/AndyMaleh)\
**Post date:** [March 5, 2023, 7:01pm UTC](https://www.ruby-forum.com/t/some-gems-for-some-useful-functions-ive-made/263694/7 "2023-03-05T19:01:59Z")

</div>

Regarding your inquiry about what is possible to achieve this:

> [@jgomo3](#):
>
> then{ condition(\_1) ? process(\_1) : \_1 }

The proper Object-Oriented Design for handling this is to do this (no need for `then` at all):

```ruby
object.process

```

The object will handle all the details. You should not have to worry about them. If you are worrying about the details of processing from the outside, then it is a code smell and a sign of a weak software design, as mentioned earlier.

But, if you have an enumerable, like an array, then the code becomes simply this:

```ruby
objects.map(&:process)

```

Again, the details of the predicate are hidden behind the `process` method on the object itself. And, you can chain things if needed (but if you want all objects, then the approach above is sufficient):

```ruby
objects.select(&:odd?).map(&:process)

```

`then` is generally not needed. It is basically an alias for `yield_self`, which is trying to force a functional paradigm on a single element inside a block, which is not truly needed given you could invoke all the functions on the object directly if it is one. The only case I could see it needed is when there is a need to process a single object the same way you would process an enumerable array of objects, and you want to reuse the same code block for the array, but on a single object. Otherwise, there is no need for `then` in general. I would try to avoid it most of the time except for the few rare cases it is needed. Unforutnately, it is over-used and abused by some programmers in the Ruby community who try too hard to force a functional paradigm when it doesn’t apply. Do not copy their example. Always analyze whether a certain approach results in simpler code to read and maintain by others or not.
