├── .gitignore ├── .travis.yml ├── CODE_OF_CONDUCT.md ├── Gemfile ├── LICENSE.txt ├── README.md ├── Rakefile ├── bin ├── console ├── ms-striphtml └── setup ├── lib ├── mightystring.rb └── mightystring │ ├── html.rb │ ├── string.rb │ └── version.rb ├── mightystring.gemspec └── test ├── mightystring_test.rb └── minitest_helper.rb /.gitignore: -------------------------------------------------------------------------------- 1 | *.gem 2 | *.swp 3 | /.idea/ 4 | Gemfile.lock 5 | -------------------------------------------------------------------------------- /.travis.yml: -------------------------------------------------------------------------------- 1 | language: ruby 2 | rvm: 3 | - 2.2.5 4 | - 2.3.1 5 | before_install: gem install bundler -v 1.11.2 6 | script: bundle exec rake test 7 | -------------------------------------------------------------------------------- /CODE_OF_CONDUCT.md: -------------------------------------------------------------------------------- 1 | # Contributor Code of Conduct 2 | 3 | As contributors and maintainers of this project, we pledge to respect all people who contribute through reporting issues, posting feature requests, updating documentation, submitting pull requests or patches, and other activities. 4 | 5 | We are committed to making participation in this project a harassment-free experience for everyone, regardless of level of experience, gender, gender identity and expression, sexual orientation, disability, personal appearance, body size, race, age, or religion. 6 | 7 | Examples of unacceptable behavior by participants include the use of sexual language or imagery, derogatory comments or personal attacks, trolling, public or private harassment, insults, or other unprofessional conduct. 8 | 9 | Project maintainers have the right and responsibility to remove, edit, or reject comments, commits, code, wiki edits, issues, and other contributions that are not aligned to this Code of Conduct. Project maintainers who do not follow the Code of Conduct may be removed from the project team. 10 | 11 | Instances of abusive, harassing, or otherwise unacceptable behavior may be reported by opening an issue or contacting one or more of the project maintainers. 12 | 13 | This Code of Conduct is adapted from the [Contributor Covenant](http:contributor-covenant.org), version 1.0.0, available at [http://contributor-covenant.org/version/1/0/0/](http://contributor-covenant.org/version/1/0/0/) 14 | -------------------------------------------------------------------------------- /Gemfile: -------------------------------------------------------------------------------- 1 | source 'https://rubygems.org' 2 | 3 | # Specify your gem's dependencies in MightyString.gemspec 4 | gemspec 5 | -------------------------------------------------------------------------------- /LICENSE.txt: -------------------------------------------------------------------------------- 1 | The MIT License (MIT) 2 | 3 | Copyright (c) 2012-2016 Daniel P. Clark & 6ft Dan(TM) 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in 13 | all copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN 21 | THE SOFTWARE. 22 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | ## MightyString 2 | [](http://badge.fury.io/rb/mightystring) 3 | [](https://travis-ci.org/danielpclark/MightyString) 4 | 5 | Powerful methods for your strings. 6 | 7 | ## Install 8 | 9 | Add this to your Gemfile and then run `bundle install`. 10 | ```ruby 11 | gem 'mightystring', '~> 1.0' 12 | ``` 13 | 14 | Or you can just install and use it manually. 15 | ```ruby 16 | gem install mightystring 17 | ``` 18 | ## Usage 19 | 20 | ```ruby 21 | # String#at 22 | "abc".at(0) 23 | # => "a" 24 | "0123456789".at(-1) 25 | # => "9" 26 | "vwq".at(5) 27 | # => nil 28 | 29 | # String#del 30 | "asdfasdf".del(0..2) 31 | # => "fasdf" 32 | "asdfasdf".del(1) 33 | # => "adfasdf" 34 | "asdfasdf".del([1,3]) 35 | # => "adasdf" 36 | "asdfasdf".del("a") 37 | # => "sdfsdf" 38 | 39 | # String#del! 40 | str = "asdfasdf" 41 | str.del!("sd") 42 | str 43 | # => "afaf" 44 | str.del!(1..2) 45 | str 46 | # => "af" 47 | 48 | # String#first 49 | "asdf".first 50 | # => "a" 51 | 52 | # String#last 53 | "asdf".last 54 | # => "f" 55 | 56 | # String#pop 57 | "asdf".pop 58 | # => "f" 59 | 60 | # String#push 61 | "asdf".push("r") 62 | # => "asdfr" 63 | 64 | # String#shift 65 | "asdf".shift 66 | # => "a" 67 | 68 | # String#sort 69 | str = "asdf" 70 | str.sort 71 | # => "adfs" 72 | str 73 | # => "asdf" 74 | 75 | # String#sort! 76 | str = "asdf" 77 | str.sort! 78 | # => "adfs" 79 | str 80 | # => "adfs" 81 | 82 | # String#unshift 83 | "asdf".unshift("r") 84 | # => "rasdf" 85 | 86 | # String#values_at 87 | "asdfasdfasdf".values_at(0,5,-1) 88 | # => ["a", "s", "f"] 89 | 90 | # String#index_all 91 | "012324507654301243".index_all( "0" ) 92 | # => [0,7,13] 93 | "the apple is the best fruit in the world".index_all( "the" ) 94 | # => [0, 13, 31] 95 | "asdfasdfasdf".index_all( /sd/ ) 96 | # => [1,5,9] 97 | 98 | # String#sift 99 | "qwertyuiop".sift( "aeiou" ) 100 | # => "euio" 101 | "qa2ws3ed4rf5tg6yh7uj8ik9ol".sift( Range.new( "0", "9" ) ) 102 | # => "23456789" 103 | 104 | # String#head 105 | "asdf".head 106 | # => "a" 107 | "asdf".head(3) 108 | # => "asd" 109 | 110 | # String#tail 111 | "asdf".tail 112 | # => "sdf" 113 | "asdf".tail(3) 114 | # => "f" 115 | 116 | # String#bhead and String#bisect_head 117 | "asdf".bhead 118 | # => ["a", "sdf"] 119 | "asdf".bhead(3) 120 | # => ["asd", "f"] 121 | 122 | # String#btail and String#bisect_tail 123 | "asdf".btail 124 | # => ["sdf", "a"] 125 | "asdf".btail(3) 126 | # => ["f", "asd"] 127 | ``` 128 | 129 | ## Extras 130 | * MightyString::HTML.text provides a more ideal HTML to ASCII formatting output. This is an advanced block "filtering" module. It works very well with, currently, extremely rare cases that fall through it's fingers. Regardless it's beautiful, and will strive to be more so. 131 | 132 | Look at the test/mightystring_test.rb for case usages of each feature. 133 | -------------------------------------------------------------------------------- /Rakefile: -------------------------------------------------------------------------------- 1 | require "bundler/gem_tasks" 2 | 3 | begin 4 | require 'bundler/setup' 5 | rescue LoadError 6 | puts 'You must `gem install bundler` and `bundle install` to run rake tasks' 7 | end 8 | 9 | #require 'rdoc/task' 10 | # 11 | #RDoc::Task.new(:rdoc) do |rdoc| 12 | # rdoc.rdoc_dir = 'rdoc' 13 | # rdoc.title = 'MightyString' 14 | # rdoc.options << '--line-numbers' 15 | # rdoc.rdoc_files.include('README.rdoc') 16 | # rdoc.rdoc_files.include('lib/**/*.rb') 17 | #end 18 | 19 | Bundler::GemHelper.install_tasks 20 | 21 | require 'rake/testtask' 22 | 23 | Rake::TestTask.new(:test) do |t| 24 | t.libs << 'lib' 25 | t.libs << 'test' 26 | t.pattern = 'test/**/*_test.rb' 27 | t.verbose = false 28 | end 29 | 30 | 31 | task :default => :test 32 | -------------------------------------------------------------------------------- /bin/console: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env ruby 2 | 3 | require "bundler/setup" 4 | require "MightyString" 5 | 6 | # You can add fixtures and/or initialization code here to make experimenting 7 | # with your gem easier. You can also use a different console, if you like. 8 | 9 | # (If you use this, don't forget to add pry to your Gemfile!) 10 | # require "pry" 11 | # Pry.start 12 | 13 | require "irb" 14 | IRB.start 15 | -------------------------------------------------------------------------------- /bin/ms-striphtml: -------------------------------------------------------------------------------- 1 | #!/usr/bin/ruby 2 | 3 | require 'getoptlong' 4 | begin 5 | require 'mightystring' 6 | rescue LoadError 7 | require 'rubygems' 8 | require 'mightystring' 9 | end 10 | 11 | include MightyString 12 | opts = GetoptLong.new( 13 | ["--license", "-l", GetoptLong::NO_ARGUMENT], 14 | ["--test", "-t", GetoptLong::NO_ARGUMENT], 15 | ["--help", "-h", GetoptLong::NO_ARGUMENT] 16 | ) 17 | opts.each do |opt, arg| 18 | case opt 19 | when "--help" 20 | puts "Yes. You do indeed need help. Go seek it." 21 | #RDoc::usage 22 | when "--test" 23 | #Strip_HTML.test = true 24 | HTML.testCase 25 | when "--license" 26 | #Strip_HTML.license = true 27 | HTML.license 28 | end 29 | end 30 | if ARGV.any? { |s| s.downcase.include?('.htm') } then 31 | ARGV.each do |s| 32 | if s.downcase.include?('.htm') and File.exists?(s) 33 | puts HTML.text( File.open(s, "rb").read ) 34 | end 35 | end 36 | end 37 | -------------------------------------------------------------------------------- /bin/setup: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | set -euo pipefail 3 | IFS=$'\n\t' 4 | 5 | bundle install 6 | 7 | # Do any other automated setup that you need to do here 8 | -------------------------------------------------------------------------------- /lib/mightystring.rb: -------------------------------------------------------------------------------- 1 | require 'mightystring/string' 2 | require 'mightystring/html' 3 | require 'mightystring/version' 4 | 5 | module MightyString 6 | ::String.include String 7 | end 8 | -------------------------------------------------------------------------------- /lib/mightystring/html.rb: -------------------------------------------------------------------------------- 1 | module MightyString 2 | module HTML 3 | def self.text(htmlstr, options = {}) 4 | options[:mappings] = default_options[:mappings]. 5 | merge(options.delete(:mappings)) if options.has_key?(:mappings) 6 | options = default_options.merge(options) 7 | options[:tag_markers].each { |g| 8 | sh_endpoints = htmlstr.index_all(g[1]) 9 | if sh_endpoints.nil? 10 | break 11 | end 12 | sh_end = htmlstr.rindex(g[1]) 13 | sh_start = htmlstr.rindex(g[0]) 14 | while !!sh_end and !!sh_start do 15 | if sh_end > sh_start 16 | sh_seq = htmlstr[sh_start,sh_end - sh_start + 1] 17 | until sh_seq.count(g[1]) == 1 do # until we've selected only the inner block 18 | sh_end = htmlstr[0,sh_end-1].rindex(g[1]) 19 | sh_seq = htmlstr[sh_start,sh_end - sh_start + 1] 20 | end 21 | if not (options[:math_by_space] and not html_math_exceptions(htmlstr[sh_start,sh_end - sh_start + 1]) == 0) 22 | htmlstr = strip_first_seq( htmlstr, htmlstr[sh_start,sh_end - sh_start + 1], options[:mappings]) 23 | else 24 | sh_end = sh_end - 1 25 | end 26 | else 27 | sh_start = sh_start - 1 28 | end 29 | sh_end = htmlstr[0..sh_end].rindex(g[1]) 30 | sh_start = htmlstr[0..sh_start].rindex(g[0]) 31 | end 32 | } 33 | return htmlstr 34 | end 35 | 36 | def self.html_math_exceptions(in_str = "") 37 | if in_str["< "] or in_str["& "] 38 | return 1 # Execption found at beginning 39 | elsif in_str["&"] and in_str[";"] and (in_str[" "] or in_str.length > 7) # Shouldn't have spaces in html &code;s or be greater than 7 in length 40 | return 2 # Exception found for both 41 | else 42 | return 0 43 | end 44 | 45 | end 46 | 47 | # strip sequence out ( master string, sequence to remove, any characters to swap inplace this for that ) 48 | def self.strip_first_seq( mstr = "", mseq = "", cmpchar = default_options[:mappings] ) 49 | if not cmpchar.empty? and cmpchar.keys.any? {|mkey| mseq.downcase[mkey.downcase] } # keys exist and one of the keys match 50 | cmpchar.each_key { |mkey| 51 | if mseq.downcase[mkey.downcase] 52 | mstr = mstr[0,mstr.index(mseq)] + cmpchar[mkey] + mstr[(mstr.index(mseq)+mseq.length)..-1] 53 | end 54 | } 55 | elsif mstr.index(mseq) 56 | mstr = mstr[0,mstr.index(mseq)] + mstr[(mstr.index(mseq)+mseq.length)..-1] 57 | end 58 | return mstr 59 | end 60 | 61 | def self.default_options 62 | { 63 | :tag_markers => [["<",">"],["&",";"]], 64 | :mappings => { 65 | """=>"'","br"=>"\n","'" => "'", " " => " ", "™" => "(TM)", "©" => "(c)" 66 | }, 67 | :math_by_space => false # FIXME certain (long) A href tags slip through 68 | } 69 | end 70 | 71 | end # module Strip_HTML 72 | end # MightyString 73 | -------------------------------------------------------------------------------- /lib/mightystring/string.rb: -------------------------------------------------------------------------------- 1 | require'forwardable' 2 | 3 | module MightyString 4 | module String 5 | def self.included(base) 6 | base.class_eval { 7 | alias_method :at, :[] 8 | alias_method :bhead, :bisect_head 9 | alias_method :btail, :bisect_tail 10 | 11 | extend Forwardable 12 | def_delegators :chars, :first, :last, :values_at 13 | } 14 | end 15 | 16 | def del indexes 17 | case indexes 18 | when String then split(indexes).join 19 | else 20 | each_char.with_index. 21 | reduce([]) {|arr,(c,i)| 22 | arr << c unless Array(indexes).include?(i) 23 | arr 24 | }.join 25 | end 26 | end 27 | 28 | def del! indexes 29 | replace del(indexes) 30 | end 31 | 32 | def head offset = 1 33 | self[0...offset] 34 | end 35 | 36 | def bisect_head offset = 1 37 | [head(offset), tail(offset)] 38 | end 39 | 40 | def index_all matcher 41 | arr_indexes = [] 42 | srch_index = rindex(matcher) 43 | while srch_index do 44 | tmpStr = self[0..srch_index-1] 45 | arr_indexes << srch_index 46 | srch_index = srch_index.zero? ? nil : tmpStr.rindex(matcher) 47 | end 48 | arr_indexes.reverse 49 | end 50 | 51 | def pop 52 | chr = self[-1] 53 | replace chop 54 | chr 55 | end 56 | 57 | def push str 58 | replace self.+(str) 59 | end 60 | 61 | def shift 62 | chr = self[0] 63 | replace self[1..-1] 64 | chr 65 | end 66 | 67 | def sift chars_to_keep 68 | chars_to_keep = case chars_to_keep 69 | when String then chars_to_keep.chars 70 | when Array then chars_to_keep 71 | when Range then chars_to_keep.to_a 72 | else 73 | raise TypeError, "Invalid parameter" 74 | end 75 | chars.keep_if {|chr| 76 | chars_to_keep.include? chr 77 | }.join 78 | end 79 | 80 | def sort 81 | chars.sort.join 82 | end 83 | 84 | def sort! 85 | replace sort 86 | end 87 | 88 | def tail offset = 1 89 | self[offset..-1] 90 | end 91 | 92 | def bisect_tail offset = 1 93 | [tail(offset), head(offset)] 94 | end 95 | 96 | def unshift str 97 | replace str.+(self) 98 | end 99 | end 100 | end 101 | -------------------------------------------------------------------------------- /lib/mightystring/version.rb: -------------------------------------------------------------------------------- 1 | module MightyString 2 | VERSION = "1.0.2" 3 | end 4 | -------------------------------------------------------------------------------- /mightystring.gemspec: -------------------------------------------------------------------------------- 1 | # coding: utf-8 2 | lib = File.expand_path('../lib', __FILE__) 3 | $LOAD_PATH.unshift(lib) unless $LOAD_PATH.include?(lib) 4 | require "mightystring/version" 5 | 6 | Gem::Specification.new do |s| 7 | s.name = 'mightystring' 8 | s.version = MightyString::VERSION 9 | s.license = 'MIT' 10 | s.summary = "Strings (are) Arrays with Matching, Indexing, Substitution, Deletion, and more." 11 | s.description = "Array functionality in Strings as well as Matching, Indexing, Substitution, Deletion, and more." 12 | s.authors = ["Daniel P. Clark / 6ftDan(TM)"] 13 | s.email = ["6ftdan@gmail.com"] 14 | s.files = `git ls-files -z`.split("\x0").reject { |f| f.match(%r{^(test|spec|features)/}) } 15 | s.homepage = 'http://www.github.com/danielpclark/mightystring' 16 | s.platform = 'ruby' 17 | s.bindir = 'bin' 18 | s.executables = 'ms-striphtml' 19 | s.require_paths = ['lib'] 20 | s.test_files = ["test/mightystring_test.rb"] 21 | s.required_ruby_version = '>= 1.8' 22 | 23 | s.add_development_dependency "bundler", "~> 1.11" 24 | s.add_development_dependency "rake", "~> 10.5" 25 | s.add_development_dependency "minitest", "~> 5.8" 26 | s.add_development_dependency "minitest-reporters", "~> 1.1" 27 | s.add_development_dependency "color_pound_spec_reporter", "~> 0.0.5" 28 | end 29 | -------------------------------------------------------------------------------- /test/mightystring_test.rb: -------------------------------------------------------------------------------- 1 | require 'minitest_helper' 2 | 3 | describe MightyString do 4 | it "String#at returns character at position" do 5 | _("abc".at(0)).must_equal "a" 6 | _("0123456789".at(-1)).must_equal "9" 7 | _("vwq".at(5)).must_be_nil 8 | _("vwq".at(-5)).must_be_nil 9 | end 10 | 11 | it "String#del deletes characters at positions" do 12 | _("asdfasdf".del(0..2)).must_equal "fasdf" 13 | _("asdfasdf".del(1)).must_equal "adfasdf" 14 | _("asdfasdf".del([1,3])).must_equal "adasdf" 15 | _("asdfasdf".del("a")).must_equal "sdfsdf" 16 | str = "asdfasdf" 17 | str.del! "sd" 18 | _(str).must_equal "afaf" 19 | str.del! (1..2) 20 | _(str).must_equal "af" 21 | end 22 | 23 | it "Can get the first and last character" do 24 | _("asdf".first).must_equal "a" 25 | _("asdf".last).must_equal "f" 26 | end 27 | 28 | it "String#pop pops it like it's hot" do 29 | _("asdf".pop).must_equal "f" 30 | end 31 | 32 | it "String#push pushes like Sisyphus" do 33 | _("asdf".push("r")).must_equal "asdfr" 34 | end 35 | 36 | it "String#shift" do 37 | _("asdf".shift).must_equal "a" 38 | end 39 | 40 | it "String#head" do 41 | _("asdf".head).must_equal "a" 42 | _("asdf".head(3)).must_equal "asd" 43 | end 44 | 45 | it "String#tail" do 46 | _("asdf".tail).must_equal "sdf" 47 | _("asdf".tail(3)).must_equal "f" 48 | end 49 | 50 | it "String#bisect_head and String#head_" do 51 | _("asdf".bisect_head).must_equal ["a", "sdf"] 52 | _("asdf".bisect_head(3)).must_equal ["asd", "f"] 53 | _("asdf".bhead).must_equal ["a", "sdf"] 54 | _("asdf".bhead(3)).must_equal ["asd", "f"] 55 | end 56 | 57 | it "String#bisect_tail and String#tail_" do 58 | _("asdf".bisect_tail).must_equal ["sdf", "a"] 59 | _("asdf".bisect_tail(3)).must_equal ["f", "asd"] 60 | _("asdf".btail).must_equal ["sdf", "a"] 61 | _("asdf".btail(3)).must_equal ["f", "asd"] 62 | end 63 | 64 | it "sorts" do 65 | str = "asdf" 66 | _(str.sort).must_equal "adfs" 67 | _(str).must_equal "asdf" 68 | str.sort! 69 | _(str).must_equal "adfs" 70 | end 71 | 72 | it "String#unshift" do 73 | _("asdf".unshift("r")).must_equal "rasdf" 74 | end 75 | 76 | it "String#values_at will show values at individual locations" do 77 | _("asdfasdfasdf".values_at(0,5,-1)).must_equal ["a", "s", "f"] 78 | end 79 | 80 | it "String#index_all returns all indexes of given string" do 81 | _("012324507654301243".index_all("0")).must_equal [0,7,13] 82 | _("the apple is the best fruit in the world".index_all("the")).must_equal [0, 13, 31] 83 | _("asdfasdfasdf".index_all(/sd/)).must_equal [1,5,9] 84 | _("asdfasdfasdf".index_all('a')).must_equal [0,4,8] 85 | _("asdfasdfasdf".index_all('f')).must_equal [3,7,11] 86 | end 87 | 88 | it "sifts out bad characters" do 89 | _("qa2ws3ed4rf5tg6yh7uj8ik9ol".sift(Range.new( "0", "9" ))).must_equal "23456789" 90 | 91 | custRange = (Range.new('a','z').to_a + Range.new('A','Z').to_a + [" "]).flatten 92 | _("
Content ".sift(custRange)).must_equal "htmlbody Content bodyhtml" 93 | end 94 | 95 | it "strips/substitues custom sequences" do 96 | _(MightyString::HTML.strip_first_seq("APPLES n APPLES ARE Yummy!","APPLES",{"APPLES" => "COWS"})).must_equal "COWS n APPLES ARE Yummy!" 97 | _(MightyString::HTML.strip_first_seq("Cows Cows and more Cows!","Cows", {"Cows" => "Winner"})).must_equal "Winner Cows and more Cows!" 98 | _(MightyString::HTML.strip_first_seq(" ---- APPLES &nbps;", " " )).must_equal " ---- APPLES &nbps;" 99 | _(MightyString::HTML.strip_first_seq("™ ---- ™ TradeMark ™", "™" )).must_equal "(TM) ---- ™ TradeMark ™" 100 | end 101 | 102 | it "strips HTML and substitues new mappings" do 103 | _(MightyString::HTML.text("")).must_equal "" 104 | _(MightyString::HTML.text("Piped sides. |
"Quoted" Copyright © TradeMark ™
")).must_equal "'Quoted' Copyright (c) TradeMark (TM)" 107 | end 108 | end 109 | 110 | -------------------------------------------------------------------------------- /test/minitest_helper.rb: -------------------------------------------------------------------------------- 1 | $LOAD_PATH.unshift File.expand_path('../../lib', __FILE__) 2 | require 'mightystring' 3 | 4 | require 'minitest/autorun' 5 | require 'color_pound_spec_reporter' 6 | 7 | Dir["#{File.dirname(__FILE__)}/support/**/*.rb"].each { |f| require f } 8 | 9 | Minitest::Reporters.use! [ColorPoundSpecReporter.new] 10 | --------------------------------------------------------------------------------