# CreativeRailsUtilities Defines extensions, useful and convenient methods on Ruby and Rails base classes. Requires `Ruby >=2.1` for keyword arguments. Intended for `Rails >= 4.0.0`, but will probably work for older projects with robust Activesupport inclusion. ## Installation `gem 'creative_rails_utilities', "~> 0.4.0"` and `bundle` ## Usage Feel free to read the source under `/lib/creative_rails_utilities/` and peruse the method "it" lines under `/spec` ##### Array ```ruby # Array#histogram # returns a histogram hash of elements sorted by most value first %i|c a b a a b|.histogram # => {a: 3, b: 2, c: 1} ``` ```ruby # Array#by_popularity # returns a semi-histogram array of unique elements sorted by most numerous element first %i|c a b a a b|.by_popularity # => [:a, :b, :c]} ``` ##### Date ```ruby # get an array of date objects Date.build_date_array("2015-10-11", "2015-10-13") #=> ["2015-10-11".to_date, "2015-10-12".to_date "2015-10-13".to_date] ``` ```ruby # get an array of dates between one date object and another "2015-10-11".to_date.build_date_array("2015-10-09") #=> ["2015-10-09".to_date, "2015-10-10".to_date, "2015-10-11".to_date] ``` ```ruby # build a range of dates that will never go beyond a limit and/or yesterday # used for accessing pre-churned data for strictly past days # Called on "2015-10-13" "2015-10-11".to_date.array_with_pre_churn_limit(30) #=> ["2015-10-11".to_date, "2015-10-12".to_date] # Called on "2015-12-25" "2015-10-11".to_date.array_with_pre_churn_limit(30) #=> ["2015-10-11".to_date, .. 28 .. , "2015-11-10".to_date ] ``` ##### Hash ```ruby # dig for deep nested values in a hash safely a = {a: {b: {c: {d: 1}}}} a.dig(:a, :b, :c, :d) #=> 1 a.dig(:a, :b, :c, :x) #=> nil ``` ```ruby # build a hash from variables and methods, more at https://github.com/Epigene/hashrush @key1 = "a" def key2; return "b"; end Hash.rush(binding, :@key1, :key2) #=> {key1: "a", key2: "b"} ``` ```ruby # fast sort by key some_hash.fast_sort_keys #=> some_sorted_hash ``` ```ruby # ensure a hash has a range of integer keys (useful for graphs) {}.populate_with_keys(min: 1, max: 3) #=> {1 => 0, 2 => 0, 3 => 0} ``` ##### Numeric ```ruby # integer into letters 1.to_s26 #=> "a" 28.to_s26 #=> "ab" # NB, not to be confused with 28.to_s(26) which operates as base (n) interpretation and starts with numbers. ``` ```ruby # get a part even when deleting with zero # whole.safe_part(part) 100.safe_part(50) #=> 0.5 1.safe_part(2) #=> 2 0.safe_part(0) #=> 0 ``` ```ruby # get a percent even when deleting with zero # whole.safe_percent(part) 100.safe_percent(50) #=> 50 1.safe_percent(2) #=> 200 3.0.safe_percent(1) #=> 33.333333333333336 # you should .round when calling this on floats 3.0.safe_percent(1, precision: 2) #=> 33.33 # As of 0.3.4 you can have precision 0.safe_percent(0) #=> 0 ``` ```ruby # get a portion even when deleting with zero # whole.safe_per(part) 100.safe_per(50) #=> 2.0 3.12.safe_per(2.6) #=> 1.2 1.safe_per(3) #=> 0.3333333333333333 0.safe_per(0) #=> 0 ``` ```ruby # transform a Numeric that denotes seconds into a hash of how many :days, :hours, :minutes ad :seconds that is 61.5.to_time_hash #=> {days: 0, hours: 0, minutes: 1, seconds: 1.5} 2775721.to_time_hash #=> {days: 32, hours: 3, minutes: 2, seconds: 1} (Time.zone.now.to_i - 3.minutes.ago.to_i).to_time_hash #=> {days: 0, hours: 0, minutes: 3, seconds: 0} ``` ##### Object __Object.chain_if(switch, operator, *args)__ __Object#chain_if(switch, operator, *args)__ ```rb # Allows smart conditional chaining of method calls or Procs # Works on Object and its descendants, class or instance String.chain_if(true, :new, "abc") #=> "abc" String.chain_if(false, :new, "abc") #=> String String.chain_if(true, Proc.new{|a| a.new("abc") }) #=> "abc" String.chain_if(false, Proc.new{|a| a.new("abc") }) #=> String 1.chain_if(false, Proc.new{raise}, 0).chain_if("yes", :*, 2).chain_if(!nil, Proc.new{|a| a.to_s}) #=> "2" # Here `chain_if(false, Proc.new{raise}, 0)` would raise, but it is set to false, so it is skipped. # `chain_if("yes", :*, 2)` multiplies by 2, and is ON (since "yes" is truthy) # Finally, `chain_if(!nil, Proc.new{|a| a.to_s})` turns result to a string and also is ON. ``` ##### String ```ruby # letters into integer "z".to_i26 #=> 26 "apple".to_i26 #=> 749325 ``` ```ruby # removes leading, trailing and repeated middle whitespace " z z ".clean_whitespace #=> "z z" ``` ```ruby # Unindent here doc type strings to the indentation level of first line string = <<-ENDBAR.unindent 1 2 3 1 ENDBAR string #=> "1..." instead of " 1..." ``` ```ruby # Convert string to boolean. "true".to_bool #=> true "faLSE".to_bool #=> false # yup, case insensitive "0".to_bool #=> false "".to_bool #=> false "y".to_bool #=> true ``` ##### URI ```ruby # URI#build(scheme: "http", domain:, path: "/", query: "", fragment: "") # builds an uri from components URI.build(domain: "www.example.com") #=> "http://www.example.com/" URI.build(scheme: "https", domain: "example.com", path: "/some/path", query: "test=true&fake=false", fragment: "#fragment") #=> "https://example.com/some/path?test=true&fake=false#fragment" URI.build(scheme: "https", domain: "example.com", path: "/some/path", query: {test: "true", fake: false}) #=> "https://example.com/some/path?fake=false&test=true" ``` #### View Helpers Rails-only (via railtie) view helpers have been added, they are automagically loaded and usable upon gem inclusion. ##### relative_time_parse(earlier_time, later_time=optional) turns a timestamp into "time ago" format. Examples: ```ruby # If used with only one argument, will default the second argument to Time.now # at "2015-01-15 12:00".to_datetime relative_time_parse("2015-01-15 12:00".to_datetime) #=> {key: "now", count: nil} relative_time_parse("2015-01-15 11:59:59".to_datetime) #=> {key: "second", count: nil} relative_time_parse("2015-01-15 11:59:58".to_datetime) #=> {key: "seconds", count: "1"} relative_time_parse("2015-01-15 11:59:00".to_datetime) #=> {key: "minute", count: nil} relative_time_parse("2015-01-15 11:58:00".to_datetime) #=> {key: "minutes", count: "2"} relative_time_parse("2015-01-15 11:00".to_datetime) #=> {key: "hour", count: nil} relative_time_parse("2015-01-15 09:00".to_datetime) #=> {key: "hours", count: "3"} relative_time_parse("2015-01-14 11:00".to_datetime) #=> {key: "day", count: nil} relative_time_parse("2015-01-10 09:00".to_datetime) #=> {key: "days", count: 5} # if used with both arguments, expects the second to be later than the first and will calculate relative time between them relative_time_parse("2015-01-01 12:00".to_datetime, "2015-01-01 12:02".to_datetime) #=> {key: "minutes", count: "2"} ``` The keys are intended to be used together with `I18n.t` Sample yaml for English ```yml en: time: now: "just now" second: "a second ago" seconds: "%{count} seconds ago" minute: "a minute ago" minutes: "%{count} minutes ago" hour: "an hour ago" hours: "%{count} hours ago" day: "a day ago" days: "%{count} days ago" ``` Then you can write a simple helper that specifies localization key location and returns the correct value based on the hash returned by `relative_time_parse` ```ruby def relative_short_time(t1, t2=nil) hash = relative_time_parse(t1, t2) count_hash = hash.count.present? ? hash.except(:key): {} return I18n.t("time.#{hash[:key]}", count_hash) end ``` ## Development Use ruby >=2.1 1. Write specs 2. Make specs fail 3. Write feature 4. Make tests pass 5. Update README.md with examples 6. Update CHANGELOG.md with changes/fixes/additions. 7. Bump version and `rake release` ## Contributing Bug reports and pull requests are welcome on [GitHub](https://github.com/CreativePublisher/rails_utilities) ## License The gem is available as open source under the terms of the [MIT License](http://opensource.org/licenses/MIT).