├── .ruby-gemset ├── .ruby-version ├── .rspec ├── .travis.yml ├── lib ├── hexpress │ ├── version.rb │ ├── main.rb │ ├── wrapped.rb │ ├── value │ │ ├── with.rb │ │ ├── except.rb │ │ ├── ending.rb │ │ └── starting.rb │ ├── modifier │ │ ├── one.rb │ │ └── many.rb │ ├── values.rb │ ├── nested │ │ ├── find.rb │ │ └── matching.rb │ ├── modifier.rb │ ├── values │ │ ├── either.rb │ │ └── range.rb │ ├── character.rb │ ├── value.rb │ ├── nested.rb │ ├── web.rb │ └── verbal_expressions.rb └── hexpress.rb ├── Gemfile ├── spec ├── spec_helper.rb └── lib │ ├── hexpress │ ├── version_spec.rb │ ├── value │ │ ├── except_spec.rb │ │ ├── ending_spec.rb │ │ ├── starting_spec.rb │ │ └── with_spec.rb │ ├── values │ │ ├── either_spec.rb │ │ └── range_spec.rb │ ├── nested │ │ ├── find_spec.rb │ │ └── matching_spec.rb │ ├── modifier │ │ ├── many_spec.rb │ │ └── one_spec.rb │ ├── values_spec.rb │ ├── main_spec.rb │ ├── value_spec.rb │ ├── modifier_spec.rb │ ├── nested_spec.rb │ └── verbal_expressions_standard.rb │ └── hexpress_spec.rb ├── Rakefile ├── .gitignore ├── LICENSE.txt ├── hexpress.gemspec └── README.md /.ruby-gemset: -------------------------------------------------------------------------------- 1 | hexpress 2 | -------------------------------------------------------------------------------- /.ruby-version: -------------------------------------------------------------------------------- 1 | ruby-2.0.0 2 | -------------------------------------------------------------------------------- /.rspec: -------------------------------------------------------------------------------- 1 | --color 2 | --format documentation 3 | -------------------------------------------------------------------------------- /.travis.yml: -------------------------------------------------------------------------------- 1 | language: ruby 2 | rvm: 3 | - 2.0.0 4 | -------------------------------------------------------------------------------- /lib/hexpress/version.rb: -------------------------------------------------------------------------------- 1 | class Hexpress 2 | VERSION = "1.2.0" 3 | end 4 | -------------------------------------------------------------------------------- /Gemfile: -------------------------------------------------------------------------------- 1 | source "https://rubygems.org/" 2 | 3 | # Specify your gem"s dependencies in hexpress.gemspec 4 | gemspec 5 | -------------------------------------------------------------------------------- /lib/hexpress/main.rb: -------------------------------------------------------------------------------- 1 | require "hexpress" 2 | 3 | class Object 4 | def hex(&block) 5 | Hexpress.new(&block) 6 | end 7 | alias_method :hexp, :hex 8 | end 9 | -------------------------------------------------------------------------------- /spec/spec_helper.rb: -------------------------------------------------------------------------------- 1 | require "coveralls" 2 | Coveralls.wear! do 3 | add_filter "/spec/" 4 | end 5 | 6 | require "pry" 7 | require "rspec" 8 | require "mocha/api" 9 | require "hexpress" 10 | -------------------------------------------------------------------------------- /spec/lib/hexpress/version_spec.rb: -------------------------------------------------------------------------------- 1 | require "spec_helper" 2 | 3 | describe Hexpress::VERSION do 4 | it "should be a string" do 5 | expect(Hexpress::VERSION).to be_kind_of(String) 6 | end 7 | end 8 | -------------------------------------------------------------------------------- /lib/hexpress/wrapped.rb: -------------------------------------------------------------------------------- 1 | class Hexpress 2 | module Wrapped 3 | attr_reader :open, :close 4 | 5 | def wrapping(content) 6 | "#{open}#{content}#{close}" 7 | end 8 | end 9 | end 10 | -------------------------------------------------------------------------------- /spec/lib/hexpress/value/except_spec.rb: -------------------------------------------------------------------------------- 1 | require "spec_helper" 2 | 3 | describe Hexpress::Value::Except do 4 | describe "#to_s" do 5 | it "returns the not marker followed by the value" do 6 | expect(described_class.new("f").to_s).to eq("^f") 7 | end 8 | end 9 | end 10 | -------------------------------------------------------------------------------- /spec/lib/hexpress/values/either_spec.rb: -------------------------------------------------------------------------------- 1 | require "spec_helper" 2 | 3 | describe Hexpress::Values::Either do 4 | describe "#to_s" do 5 | it "returns items delimited by or matcher inside non-capture group" do 6 | expect(described_class.new("foo", "bar").to_s).to eq("(?:foo|bar)") 7 | end 8 | end 9 | end 10 | -------------------------------------------------------------------------------- /spec/lib/hexpress/nested/find_spec.rb: -------------------------------------------------------------------------------- 1 | require "spec_helper" 2 | 3 | describe Hexpress::Nested::Find do 4 | describe "#to_s" do 5 | it "returns a capture of the hexpression" do 6 | expect(described_class.new { word }.to_s ).to eq('(\w)') 7 | end 8 | 9 | it "returns a capture of the string" do 10 | expect(described_class.new("foo").to_s ).to eq('(foo)') 11 | end 12 | end 13 | end 14 | -------------------------------------------------------------------------------- /spec/lib/hexpress/value/ending_spec.rb: -------------------------------------------------------------------------------- 1 | require "spec_helper" 2 | 3 | describe Hexpress::Value::Ending do 4 | describe "#to_s" do 5 | it "returns the end of string pattern" do 6 | expect(described_class.new.to_s).to eq("$") 7 | end 8 | 9 | it "has the given string before the end of string pattern" do 10 | expect(described_class.new("foo").to_s).to eq("foo$") 11 | end 12 | end 13 | end 14 | -------------------------------------------------------------------------------- /lib/hexpress/value/with.rb: -------------------------------------------------------------------------------- 1 | class Hexpress 2 | def with(value) 3 | add_value(Value::With, value) 4 | end 5 | alias_method :has, :with 6 | alias_method :including, :with 7 | 8 | module Value 9 | class With 10 | include Value 11 | 12 | def initialize(value) 13 | @value = value 14 | end 15 | 16 | def to_s 17 | "#{value}" 18 | end 19 | end 20 | end 21 | end 22 | -------------------------------------------------------------------------------- /spec/lib/hexpress/value/starting_spec.rb: -------------------------------------------------------------------------------- 1 | require "spec_helper" 2 | 3 | describe Hexpress::Value::Starting do 4 | describe "#to_s" do 5 | it "returns the start of string pattern" do 6 | expect(described_class.new.to_s).to eq("^") 7 | end 8 | 9 | it "has the given string after the start of string pattern" do 10 | expect(described_class.new("foo").to_s).to eq("^foo") 11 | end 12 | end 13 | end 14 | -------------------------------------------------------------------------------- /lib/hexpress/modifier/one.rb: -------------------------------------------------------------------------------- 1 | class Hexpress 2 | def one(value = nil, &block) 3 | add_value(Modifier::One, value, &block) 4 | end 5 | alias_method :maybe, :one 6 | alias_method :possibly, :one 7 | 8 | module Modifier 9 | class One 10 | include Value 11 | include Modifier 12 | 13 | def initialize(value) 14 | @value = value 15 | @operator = "?" 16 | end 17 | end 18 | end 19 | end 20 | -------------------------------------------------------------------------------- /lib/hexpress/values.rb: -------------------------------------------------------------------------------- 1 | class Hexpress 2 | def add_values(hexpression, *vs) 3 | add(hexpression.new(*vs)) 4 | end 5 | 6 | module Values 7 | attr_reader :delimiter 8 | 9 | def values 10 | @values.map { |v| v.respond_to?(:gsub) ? Regexp.escape(v) : v } 11 | end 12 | 13 | def to_s 14 | "#{values.join(delimiter)}" 15 | end 16 | end 17 | end 18 | 19 | require_relative "values/either" 20 | require_relative "values/range" 21 | -------------------------------------------------------------------------------- /lib/hexpress/value/except.rb: -------------------------------------------------------------------------------- 1 | class Hexpress 2 | def except(value) 3 | add_value(Value::Except, value) 4 | end 5 | alias_method :excluding, :except 6 | alias_method :exclude, :except 7 | alias_method :without, :except 8 | 9 | module Value 10 | class Except 11 | include Value 12 | 13 | def initialize(value) 14 | @value = value 15 | end 16 | 17 | def to_s 18 | "^#{value}" 19 | end 20 | end 21 | end 22 | end 23 | -------------------------------------------------------------------------------- /spec/lib/hexpress/nested/matching_spec.rb: -------------------------------------------------------------------------------- 1 | require "spec_helper" 2 | 3 | describe Hexpress::Nested::Matching do 4 | describe "#hexpression" do 5 | it "escapes strings" do 6 | expect(described_class.new { [word, "-"] }.to_s).to eq('[\w\-]') 7 | end 8 | end 9 | 10 | describe "#to_s" do 11 | it "returns the instance of hexpressions and wraps them in []" do 12 | expect(described_class.new { word }.to_s).to eq('[\w]') 13 | end 14 | end 15 | end 16 | -------------------------------------------------------------------------------- /lib/hexpress/nested/find.rb: -------------------------------------------------------------------------------- 1 | class Hexpress 2 | def find(value = nil, &block) 3 | value ? add_value(Nested::Find, value) : add_nested(Nested::Find, &block) 4 | end 5 | alias_method :capture, :find 6 | 7 | module Nested 8 | class Find 9 | include Nested 10 | 11 | def initialize(value = nil, &block) 12 | @hexpression = value || Hexpress.new.instance_eval(&block) 13 | @open, @close = "(", ")" 14 | end 15 | end 16 | end 17 | end 18 | -------------------------------------------------------------------------------- /lib/hexpress/modifier.rb: -------------------------------------------------------------------------------- 1 | class Hexpress 2 | module Modifier 3 | OPEN = "(?:" 4 | CLOSE = ")" 5 | attr_reader :operator 6 | 7 | def open 8 | OPEN 9 | end 10 | 11 | def close 12 | CLOSE 13 | end 14 | 15 | def to_s 16 | if @value 17 | "#{open}#{value}#{close}#{operator}" 18 | else 19 | operator 20 | end 21 | end 22 | end 23 | end 24 | 25 | require_relative "modifier/many" 26 | require_relative "modifier/one" 27 | -------------------------------------------------------------------------------- /lib/hexpress/value/ending.rb: -------------------------------------------------------------------------------- 1 | class Hexpress 2 | def ending(value = nil) 3 | add_value(Value::Ending, value) 4 | end 5 | alias_method :ending_with, :ending 6 | alias_method :finally, :ending 7 | alias_method :endOfLine, :ending 8 | 9 | module Value 10 | class Ending 11 | include Value 12 | 13 | def initialize(value = nil) 14 | @value = value 15 | end 16 | 17 | def to_s 18 | "#{value}$" 19 | end 20 | end 21 | end 22 | end 23 | -------------------------------------------------------------------------------- /lib/hexpress/value/starting.rb: -------------------------------------------------------------------------------- 1 | class Hexpress 2 | def starting(value = nil) 3 | add_value(Value::Starting, value) 4 | end 5 | alias_method :begins, :starting 6 | alias_method :begin, :starting 7 | alias_method :start, :starting 8 | 9 | module Value 10 | class Starting 11 | include Value 12 | 13 | def initialize(value = nil) 14 | @value = value 15 | end 16 | 17 | def to_s 18 | "^#{value}" 19 | end 20 | end 21 | end 22 | end 23 | -------------------------------------------------------------------------------- /spec/lib/hexpress/modifier/many_spec.rb: -------------------------------------------------------------------------------- 1 | require "spec_helper" 2 | 3 | describe Hexpress::Modifier::Many do 4 | describe "#operator" do 5 | class ExampleWithMany < Hexpress::Modifier::Many 6 | def initialize(minimum) 7 | @minimum = minimum 8 | end 9 | end 10 | 11 | it "returns * if minimum is 0" do 12 | expect(ExampleWithMany.new(0).operator).to eq("*") 13 | end 14 | 15 | it "returns + if minimum is 1" do 16 | expect(ExampleWithMany.new(1).operator).to eq("+") 17 | end 18 | end 19 | end 20 | -------------------------------------------------------------------------------- /spec/lib/hexpress/value/with_spec.rb: -------------------------------------------------------------------------------- 1 | require "spec_helper" 2 | 3 | describe Hexpress::Value::With do 4 | describe "#to_s" do 5 | it "returns the string given" do 6 | expect(described_class.new("foo").to_s).to eq("foo") 7 | end 8 | 9 | it "allows composing of multiple patterns" do 10 | pattern1 = Hexpress.new.starting("foo") 11 | pattern2 = Hexpress.new.ending("bar") 12 | pattern3 = Hexpress.new.including(pattern1).with("1").including(pattern2) 13 | expect(pattern3.to_r).to eq(/^foo1bar$/) 14 | end 15 | end 16 | end 17 | -------------------------------------------------------------------------------- /lib/hexpress/values/either.rb: -------------------------------------------------------------------------------- 1 | class Hexpress 2 | def either(*values) 3 | add_value(Values::Either, values) 4 | end 5 | alias_method :and, :either 6 | alias_method :any_of, :either 7 | 8 | module Values 9 | class Either 10 | include Values 11 | include Wrapped 12 | 13 | def initialize(*values) 14 | @values = values 15 | @delimiter = "|" 16 | @open, @close = "(?:", ")" 17 | end 18 | 19 | def to_s 20 | "#{open}#{values.join(delimiter)}#{close}" 21 | end 22 | end 23 | end 24 | end 25 | -------------------------------------------------------------------------------- /lib/hexpress/modifier/many.rb: -------------------------------------------------------------------------------- 1 | class Hexpress 2 | def many(value = nil, minimum = 1) 3 | add_values(Modifier::Many, value, minimum) 4 | end 5 | alias_method :multiple, :many 6 | 7 | module Modifier 8 | class Many 9 | include Value 10 | include Modifier 11 | 12 | def initialize(value, minimum = 1) 13 | @value = value 14 | @minimum = minimum 15 | end 16 | 17 | def operator 18 | case @minimum 19 | when 0 then "*" 20 | when 1 then "+" 21 | end 22 | end 23 | end 24 | end 25 | end 26 | -------------------------------------------------------------------------------- /lib/hexpress/values/range.rb: -------------------------------------------------------------------------------- 1 | class Hexpress 2 | def letter 3 | lower and upper 4 | end 5 | 6 | def lower(value = "a".."z") 7 | add_value(Values::Range, value) 8 | end 9 | 10 | def upper(value = "A".."Z") 11 | add_value(Values::Range, value) 12 | end 13 | 14 | def number(value = 0..9) 15 | add_value(Values::Range, value) 16 | end 17 | 18 | module Values 19 | class Range 20 | include Values 21 | 22 | def initialize(range) 23 | @values = [range.first, range.last] 24 | @delimiter = "-" 25 | end 26 | end 27 | end 28 | end 29 | -------------------------------------------------------------------------------- /spec/lib/hexpress/values_spec.rb: -------------------------------------------------------------------------------- 1 | require "spec_helper" 2 | 3 | describe Hexpress::Values do 4 | describe "#values" do 5 | class ExampleValues 6 | include Hexpress::Values 7 | 8 | def initialize(*values) 9 | @delimiter = "." 10 | @values = values 11 | end 12 | end 13 | 14 | it "returns the values list" do 15 | expect(ExampleValues.new("foo", "bar").values).to eq(["foo", "bar"]) 16 | end 17 | 18 | it "escapes the regex characters" do 19 | expect(ExampleValues.new("fo.o", "bar").values).to eq(["fo\\.o", "bar"]) 20 | end 21 | end 22 | end 23 | -------------------------------------------------------------------------------- /spec/lib/hexpress/modifier/one_spec.rb: -------------------------------------------------------------------------------- 1 | require "spec_helper" 2 | 3 | describe Hexpress::Modifier::One do 4 | describe "#maybe" do 5 | it 'returns `(?:\w+)?`' do 6 | regex = Hexpress.new.maybe { words }.to_r 7 | expect(regex).to eq(/(?:(?:\w)+)?/) 8 | end 9 | 10 | it 'returns only ? if no value given`' do 11 | regex = Hexpress.new.with("foo").maybe.to_r 12 | expect(regex).to eq(/foo?/) 13 | end 14 | end 15 | 16 | describe "#operator" do 17 | it "returns the ? operator" do 18 | expect(described_class.new("foo").operator).to eq("?") 19 | end 20 | end 21 | end 22 | -------------------------------------------------------------------------------- /Rakefile: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env rake 2 | require "rspec/core/rake_task" 3 | require "yard" 4 | 5 | begin 6 | Bundler.setup :default, :development 7 | Bundler::GemHelper.install_tasks 8 | rescue Bundler::BundlerError => error 9 | $stderr.puts error.message 10 | $stderr.puts "Run `bundle install` to install missing gems" 11 | exit error.status_code 12 | end 13 | 14 | RSpec::Core::RakeTask.new(:spec) 15 | 16 | desc "Generate all of the docs" 17 | YARD::Rake::YardocTask.new do |config| 18 | config.files = Dir["lib/**/*.rb"] 19 | end 20 | 21 | desc "Default: run tests and generate docs" 22 | task default: [ :spec, :yard ] 23 | -------------------------------------------------------------------------------- /lib/hexpress/character.rb: -------------------------------------------------------------------------------- 1 | class Hexpress 2 | class Character 3 | attr_reader :upcase 4 | def initialize(name, upcase = false) 5 | @value = case name 6 | when :word then '\w' 7 | when :digit then '\d' 8 | when :space then '\s' 9 | when :any then '.' 10 | when :tab then '\t' 11 | when :newline then '\n' 12 | when :return then '\r' 13 | else name 14 | end 15 | @upcase = upcase 16 | end 17 | 18 | def value 19 | if upcase then @value.upcase else @value end 20 | end 21 | 22 | def to_s 23 | value 24 | end 25 | end 26 | end 27 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | # See http://help.github.com/ignore-files/ for more about ignoring files. 2 | # 3 | # If you find yourself ignoring temporary files generated by your text editor 4 | # or operating system, you probably want to add a global ignore instead: 5 | # git config --global core.excludesfile ~/.gitignore 6 | 7 | # Ignore all of the generated gem stuff 8 | /pkg 9 | /*.gem 10 | 11 | # Ignore bundler config 12 | /.bundle 13 | /Gemfile.lock 14 | 15 | # Ignore all bundler caching 16 | /vendor/cache 17 | /vendor/ruby 18 | 19 | # Ignore all tempfiles 20 | /tmp 21 | 22 | # Ignores that should be in the global gitignore 23 | /coverage 24 | /doc 25 | -------------------------------------------------------------------------------- /lib/hexpress/value.rb: -------------------------------------------------------------------------------- 1 | class Hexpress 2 | private 3 | 4 | def add_value(hexpression, v, &block) 5 | add(hexpression.new(block_given? ? Hexpress.new.instance_exec(&block) : v)) 6 | end 7 | 8 | module Value 9 | def value 10 | if escapable? then escaped_value else @value end || "" 11 | end 12 | 13 | private 14 | 15 | def escapable? 16 | @value.respond_to?(:gsub) 17 | end 18 | 19 | def escaped_value 20 | Regexp.escape(@value) 21 | end 22 | end 23 | end 24 | 25 | require_relative "value/ending" 26 | require_relative "value/except" 27 | require_relative "value/starting" 28 | require_relative "value/with" 29 | -------------------------------------------------------------------------------- /spec/lib/hexpress/main_spec.rb: -------------------------------------------------------------------------------- 1 | require "spec_helper" 2 | require "hexpress/main" 3 | 4 | describe Hexpress do 5 | describe "hex" do 6 | it "allow for a chain of methods" do 7 | expect(hex.find("foo").to_r).to eq(/(foo)/) 8 | end 9 | 10 | it "allow for a block of methods" do 11 | expect(hex { find("foo") }.to_r).to eq(/(foo)/) 12 | end 13 | end 14 | 15 | describe "hexp" do 16 | it "allow for a chain of methods" do 17 | expect(hexp.find("foo").to_r).to eq(/(foo)/) 18 | end 19 | 20 | it "allow for a block of methods" do 21 | expect(hexp { find("foo") }.to_r).to eq(/(foo)/) 22 | end 23 | end 24 | end 25 | -------------------------------------------------------------------------------- /spec/lib/hexpress/value_spec.rb: -------------------------------------------------------------------------------- 1 | require "spec_helper" 2 | 3 | describe Hexpress::Value do 4 | describe "#value" do 5 | class ExampleValue 6 | include Hexpress::Value 7 | 8 | def initialize(value) 9 | @value = value 10 | end 11 | end 12 | 13 | it "returns the string given" do 14 | expect(ExampleValue.new("foo").value).to eq("foo") 15 | end 16 | 17 | it "returns regex characters escaped" do 18 | expect(ExampleValue.new("fo.o").value).to eq("fo\\.o") 19 | end 20 | 21 | it "returns an empty string if value not given" do 22 | expect(ExampleValue.new(nil).value).to eq("") 23 | end 24 | end 25 | end 26 | -------------------------------------------------------------------------------- /lib/hexpress/nested/matching.rb: -------------------------------------------------------------------------------- 1 | class Hexpress 2 | def matching(&block) 3 | add_nested(Nested::Matching, &block) 4 | end 5 | alias_method :like, :matching 6 | 7 | module Nested 8 | class Matching 9 | include Nested 10 | 11 | def initialize(&block) 12 | @hexpression = Hexpress.new.instance_eval(&block) 13 | @open, @close = "[", "]" 14 | end 15 | 16 | def join_hexpression 17 | @hexpression.map(&method(:escape)).join(delimiter) 18 | end 19 | 20 | private 21 | 22 | def escape(hexpression) 23 | if hexpression.respond_to?(:gsub) 24 | Regexp.escape(hexpression) 25 | else 26 | hexpression 27 | end 28 | end 29 | end 30 | end 31 | end 32 | -------------------------------------------------------------------------------- /lib/hexpress/nested.rb: -------------------------------------------------------------------------------- 1 | class Hexpress 2 | private 3 | 4 | def add_nested(expression, &block) 5 | tap do 6 | @expressions << expression.new(&block) 7 | end 8 | end 9 | 10 | module Nested 11 | include Wrapped 12 | 13 | def delimiter 14 | @delimiter || "" 15 | end 16 | 17 | def hexpression 18 | if joinable? then join_hexpression else @hexpression end 19 | end 20 | 21 | def to_s 22 | wrapping(hexpression) 23 | end 24 | 25 | private 26 | 27 | def join_hexpression 28 | @hexpression.join(delimiter) 29 | end 30 | 31 | def joinable? 32 | @hexpression.respond_to?(:join) 33 | end 34 | end 35 | end 36 | 37 | require_relative "nested/find" 38 | require_relative "nested/matching" 39 | -------------------------------------------------------------------------------- /lib/hexpress/web.rb: -------------------------------------------------------------------------------- 1 | class Hexpress 2 | module Web 3 | def http 4 | start("http").maybe("s").protocol 5 | end 6 | 7 | def http_only 8 | start("http").protocol 9 | end 10 | 11 | def https_only 12 | start("https").protocol 13 | end 14 | 15 | def protocol 16 | with("://") 17 | end 18 | 19 | def domain(name) 20 | maybe { words.with(".") }.find(name) 21 | end 22 | 23 | def tld(name) 24 | has(".").with(name).maybe("/") 25 | end 26 | 27 | def ftp 28 | start("ftp").maybe("s").protocol 29 | end 30 | 31 | def ftp_only 32 | start("ftp").protocol 33 | end 34 | 35 | def ftps_only 36 | start("ftps").protocol 37 | end 38 | 39 | def path 40 | has("/").anything.maybe.maybe("?") 41 | end 42 | end 43 | 44 | include Web 45 | end 46 | -------------------------------------------------------------------------------- /lib/hexpress/verbal_expressions.rb: -------------------------------------------------------------------------------- 1 | require "hexpress" 2 | 3 | class Hexpress 4 | alias_method :startOfLine, :starting 5 | alias_method :then, :with 6 | alias_method :lineBreak, :line 7 | alias_method :br, :line 8 | alias_method :anyOf, :either 9 | 10 | def anythingBut(value) 11 | many(matching { without(value) }.to_s, 0) 12 | end 13 | 14 | def somethingBut(value) 15 | something and without(value) 16 | end 17 | 18 | 19 | module Values 20 | class Either 21 | def open 22 | "(?:[" 23 | end 24 | 25 | def close 26 | "])" 27 | end 28 | end 29 | end 30 | 31 | module Modifier 32 | class Many 33 | def to_s 34 | "#{open}#{value}#{operator}#{close}" 35 | end 36 | end 37 | end 38 | 39 | module Nested 40 | class Find 41 | def open 42 | "(?:" 43 | end 44 | end 45 | end 46 | end 47 | -------------------------------------------------------------------------------- /spec/lib/hexpress/values/range_spec.rb: -------------------------------------------------------------------------------- 1 | require "spec_helper" 2 | 3 | describe Hexpress::Values::Range do 4 | describe "#lower" do 5 | it "returns the lower character matcher" do 6 | expect(Hexpress.new.lower.to_s).to eq("a-z") 7 | end 8 | end 9 | 10 | describe "#upper" do 11 | it "returns the upper character matcher" do 12 | expect(Hexpress.new.upper.to_s).to eq("A-Z") 13 | end 14 | end 15 | 16 | describe "#letter" do 17 | it "returns the upper and lower character matchers" do 18 | expect(Hexpress.new.letter.to_s).to eq("a-zA-Z") 19 | end 20 | end 21 | 22 | describe "#number" do 23 | it "returns the number matcher" do 24 | expect(Hexpress.new.number.to_s).to eq("0-9") 25 | end 26 | end 27 | 28 | describe "#to_s" do 29 | it "returns two values joined by range matcher" do 30 | expect(described_class.new(0..9).to_s).to eq("0-9") 31 | end 32 | end 33 | end 34 | -------------------------------------------------------------------------------- /LICENSE.txt: -------------------------------------------------------------------------------- 1 | Copyright (c) 2013 Kurtis Rainbolt-Greene 2 | 3 | MIT License 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining 6 | a copy of this software and associated documentation files (the 7 | "Software"), to deal in the Software without restriction, including 8 | without limitation the rights to use, copy, modify, merge, publish, 9 | distribute, sublicense, and/or sell copies of the Software, and to 10 | permit persons to whom the Software is furnished to do so, subject to 11 | the following conditions: 12 | 13 | The above copyright notice and this permission notice shall be 14 | included in all copies or substantial portions of the Software. 15 | 16 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, 17 | EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF 18 | MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND 19 | NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE 20 | LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION 21 | OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION 22 | WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. 23 | -------------------------------------------------------------------------------- /spec/lib/hexpress/modifier_spec.rb: -------------------------------------------------------------------------------- 1 | require "spec_helper" 2 | 3 | describe Hexpress::Modifier do 4 | class ExampleModifier 5 | include Hexpress::Modifier 6 | end 7 | 8 | describe "#open" do 9 | it "returns the open operator" do 10 | expect(ExampleModifier.new.open).to eq("(?:") 11 | end 12 | end 13 | 14 | describe "#close" do 15 | it "returns the close operator" do 16 | expect(ExampleModifier.new.close).to eq(")") 17 | end 18 | end 19 | 20 | describe "#to_s" do 21 | class ExampleModifierWithValue 22 | include Hexpress::Modifier 23 | attr_reader :value 24 | 25 | def initialize 26 | @value = "foo" 27 | end 28 | 29 | def operator 30 | "%" 31 | end 32 | end 33 | 34 | it "returns the wrapped value with operator if value exists" do 35 | expect(ExampleModifierWithValue.new.to_s).to eq("(?:foo)%") 36 | end 37 | 38 | class ExampleModifierWithoutValue 39 | include Hexpress::Modifier 40 | def operator 41 | "%" 42 | end 43 | end 44 | 45 | it "returns just the operator" do 46 | expect(ExampleModifierWithoutValue.new.to_s).to eq("%") 47 | end 48 | end 49 | end 50 | -------------------------------------------------------------------------------- /spec/lib/hexpress/nested_spec.rb: -------------------------------------------------------------------------------- 1 | require "spec_helper" 2 | 3 | describe Hexpress::Nested do 4 | describe "#hexpression" do 5 | class ExampleNestedWithDelimiter 6 | include Hexpress::Nested 7 | def initialize 8 | @hexpression = ["foo", "bar"] 9 | @delimiter = "." 10 | end 11 | end 12 | 13 | it "returns the hexpression items with the specified delimiter" do 14 | expect(ExampleNestedWithDelimiter.new.hexpression).to eq("foo.bar") 15 | end 16 | 17 | class ExampleNestedWithoutDelimiter 18 | include Hexpress::Nested 19 | def initialize 20 | @hexpression = ["foo", "bar"] 21 | end 22 | end 23 | 24 | it "returns the hexpression items with the specified delimiter" do 25 | expect(ExampleNestedWithoutDelimiter.new.hexpression).to eq("foobar") 26 | end 27 | end 28 | 29 | describe "#to_s" do 30 | class ExampleNested 31 | include Hexpress::Nested 32 | def initialize 33 | @hexpression = "foo" 34 | @open, @close = "{", "}" 35 | end 36 | end 37 | 38 | it "returns the hexpression wrapped in the open and close" do 39 | expect(ExampleNested.new.to_s).to eq("{foo}") 40 | end 41 | end 42 | end 43 | -------------------------------------------------------------------------------- /hexpress.gemspec: -------------------------------------------------------------------------------- 1 | # coding: utf-8 2 | lib = File.expand_path("../lib", __FILE__) 3 | $LOAD_PATH.unshift(lib) unless $LOAD_PATH.include?(lib) 4 | require "hexpress/version" 5 | 6 | Gem::Specification.new do |spec| 7 | spec.name = "hexpress" 8 | spec.version = Hexpress::VERSION 9 | spec.authors = ["Kurtis Rainbolt-Greene"] 10 | spec.email = ["me@kurtisrainboltgreene.name"] 11 | spec.summary = %q{Hexpress is for Human Expressions, similar to Verbal Expressions} 12 | spec.description = spec.summary 13 | spec.homepage = "http://krainboltgreene.github.com/hexpress" 14 | spec.license = "MIT" 15 | 16 | spec.files = Dir["lib/**/*"] 17 | spec.executables = Dir["bin/**/*"].map! { |f| f.gsub(/bin\//, '') } 18 | spec.test_files = Dir["test/**/*", "spec/**/*"] 19 | spec.require_paths = ["lib"] 20 | 21 | spec.add_development_dependency "bundler" 22 | spec.add_development_dependency "rspec" 23 | spec.add_development_dependency "mocha" 24 | spec.add_development_dependency "rake" 25 | spec.add_development_dependency "yard" 26 | spec.add_development_dependency "kramdown" 27 | spec.add_development_dependency "pry" 28 | spec.add_development_dependency "coveralls" 29 | end 30 | -------------------------------------------------------------------------------- /lib/hexpress.rb: -------------------------------------------------------------------------------- 1 | class Hexpress 2 | CHARACTERS = [:word, :digit, :space] 3 | attr_reader :expressions 4 | 5 | def initialize(*expressions, &block) 6 | @expressions = expressions 7 | instance_exec(&block) if block_given? 8 | end 9 | 10 | # Matches \w 11 | def word 12 | add(Character.new(:word)) 13 | end 14 | 15 | # Matches \d 16 | def digit 17 | add(Character.new(:digit)) 18 | end 19 | 20 | # Matches \s 21 | def space 22 | add(Character.new(:space)) 23 | end 24 | 25 | # Matches . 26 | def any 27 | add(Character.new(:any)) 28 | end 29 | 30 | # Matches (?:.)* 31 | def anything 32 | many(Character.new(:any), 0) 33 | end 34 | 35 | # Matches (?:.)+ 36 | def something 37 | many(Character.new(:any), 1) 38 | end 39 | 40 | # Matches \t 41 | def tab 42 | add(Character.new(:tab)) 43 | end 44 | 45 | # Matches (?:(?:\n)|(?:\r\n)) 46 | def line 47 | either(Character.new('(?:\n)'), Character.new('(?:\r\n)')) 48 | end 49 | 50 | CHARACTERS.each do |character| 51 | define_method(character) do 52 | add(Character.new(character)) 53 | end 54 | 55 | define_method("non#{character}") do 56 | add(Character.new(character, true)) 57 | end 58 | 59 | define_method("#{character}s") do 60 | many(Character.new(character)) 61 | end 62 | 63 | define_method("non#{character}s") do 64 | many(Character.new(character, true)) 65 | end 66 | end 67 | 68 | # This method returns the regular hexpression form of this object. 69 | def to_r 70 | Regexp.new(to_s) 71 | end 72 | 73 | # This method returns the string version of the regexp. 74 | def to_s 75 | @expressions.map(&:to_s).join 76 | end 77 | 78 | # Takes an expression and returns the combination of the two expressions 79 | # in a new Hexpress object. 80 | def +(expression) 81 | Hexpress.new(*expressions + expression.expressions) 82 | end 83 | 84 | private 85 | 86 | # This method takes an hexpression and adds it to the hexpression queue 87 | # while returning the main object. 88 | def add(expression) 89 | tap do 90 | @expressions << expression 91 | end 92 | end 93 | end 94 | 95 | require_relative "hexpress/character" 96 | require_relative "hexpress/version" 97 | require_relative "hexpress/wrapped" 98 | require_relative "hexpress/value" 99 | require_relative "hexpress/values" 100 | require_relative "hexpress/nested" 101 | require_relative "hexpress/modifier" 102 | 103 | if defined?(Rails) 104 | require_relative "hexpress/main" 105 | end 106 | -------------------------------------------------------------------------------- /spec/lib/hexpress_spec.rb: -------------------------------------------------------------------------------- 1 | require "spec_helper" 2 | 3 | describe Hexpress do 4 | it "takes a list of expressions and turns them into a regex" do 5 | expressions = [Hexpress::Value::With.new("foo"), Hexpress::Value::With.new("bar")] 6 | pattern = Hexpress.new(*expressions) 7 | expect(pattern.to_r).to eq(/foobar/) 8 | end 9 | 10 | it "takes a block and turns into a regex" do 11 | pattern = Hexpress.new { find("foo") } 12 | expect(pattern.to_r).to eq(/(foo)/) 13 | end 14 | 15 | it "takes a chain and turns into a regex" do 16 | pattern = Hexpress.new.find("foo") 17 | expect(pattern.to_r).to eq(/(foo)/) 18 | end 19 | 20 | describe "#word" do 21 | it "returns the word matcher" do 22 | expect(Hexpress.new.word.to_s).to eq('\w') 23 | end 24 | end 25 | 26 | describe "#digit" do 27 | it "returns the digit matcher" do 28 | expect(Hexpress.new.digit.to_s).to eq('\d') 29 | end 30 | end 31 | 32 | describe "#space" do 33 | it "returns the whitespace matcher" do 34 | expect(Hexpress.new.space.to_s).to eq('\s') 35 | end 36 | end 37 | 38 | describe "#words" do 39 | it "returns the word and multiple matchers" do 40 | expect(Hexpress.new.words.to_s).to eq('(?:\w)+') 41 | end 42 | end 43 | 44 | describe "#digits" do 45 | it "returns the digit and multiple matchers" do 46 | expect(Hexpress.new.digits.to_s).to eq('(?:\d)+') 47 | end 48 | end 49 | 50 | describe "#spaces" do 51 | it "returns the whitespace and multiple matchers" do 52 | expect(Hexpress.new.spaces.to_s).to eq('(?:\s)+') 53 | end 54 | end 55 | 56 | describe "#nonword" do 57 | it "returns the nonword matcher" do 58 | expect(Hexpress.new.nonword.to_s).to eq('\W') 59 | end 60 | end 61 | 62 | describe "#nondigit" do 63 | it "returns the nondigit matcher" do 64 | expect(Hexpress.new.nondigit.to_s).to eq('\D') 65 | end 66 | end 67 | 68 | describe "#nonspace" do 69 | it "returns the nonwhitespace matcher" do 70 | expect(Hexpress.new.nonspace.to_s).to eq('\S') 71 | end 72 | end 73 | 74 | describe "#anything" do 75 | it "returns any with zero or more pattern wrapped in noncapute" do 76 | expect(Hexpress.new.anything.to_s).to eq('(?:.)*') 77 | end 78 | end 79 | 80 | describe "#+" do 81 | it "returns a combination of any number of expressions" do 82 | pattern1 = Hexpress.new.with("foo") 83 | pattern2 = Hexpress.new.with("bar") 84 | pattern3 = Hexpress.new.with("bang") 85 | pattern4 = pattern1 + pattern2 + pattern3 86 | expect(pattern4.to_r).to eq(/foobarbang/) 87 | end 88 | end 89 | end 90 | -------------------------------------------------------------------------------- /spec/lib/hexpress/verbal_expressions_standard.rb: -------------------------------------------------------------------------------- 1 | require "spec_helper" 2 | require "hexpress/verbal_hexpressions" 3 | 4 | describe "VerbalHexpressions" do 5 | def hexpressed(method, *arguments) 6 | Hexpress.new.send(method, *arguments).to_s 7 | end 8 | 9 | describe "#startOfLine" do 10 | it 'returns `^`' do 11 | expect(hexpressed(:startOfLine)).to eq('^') 12 | end 13 | end 14 | 15 | describe "#endOfLine" do 16 | it 'returns `$`' do 17 | expect(hexpressed(:endOfLine)).to eq('$') 18 | end 19 | end 20 | 21 | describe "#find" do 22 | it 'returns `(?:foo)`' do 23 | expect(hexpressed(:find, "foo")).to eq('(?:foo)') 24 | end 25 | end 26 | 27 | describe "#maybe" do 28 | it 'returns `(?:foo)?`' do 29 | expect(hexpressed(:maybe, "foo")).to eq('(?:foo)?') 30 | end 31 | end 32 | 33 | describe "#anything" do 34 | it 'returns `(?:.*)`' do 35 | expect(hexpressed(:anything)).to eq('(?:.*)') 36 | end 37 | end 38 | 39 | describe "#anythingBut" do 40 | it 'returns `(?:[^f]*)`' do 41 | expect(hexpressed(:anythingBut, "f")).to eq('(?:[^f]*)') 42 | end 43 | end 44 | 45 | describe "#something" do 46 | it 'returns `(?:.+)`' do 47 | expect(hexpressed(:something)).to eq('(?:.+)') 48 | end 49 | end 50 | 51 | describe "#somethingBut" do 52 | it 'returns `(?:[^f]+)`' do 53 | expect(hexpressed(:somethingBut)).to eq('(?:[^f]+)') 54 | end 55 | end 56 | 57 | describe "#lineBreak" do 58 | it 'returns `(?:(?:\n)|(?:\r\n))`' do 59 | expect(hexpressed(:lineBreak)).to eq('(?:(?:\n)|(?:\r\n))') 60 | end 61 | end 62 | 63 | describe "#br" do 64 | it 'returns `(?:(?:\n)|(?:\r\n))`' do 65 | expect(hexpressed(:br)).to eq('(?:(?:\n)|(?:\r\n))') 66 | end 67 | end 68 | 69 | describe "#tab" do 70 | it 'returns `\t`' do 71 | expect(hexpressed(:tab)).to eq('\t') 72 | end 73 | end 74 | 75 | describe "#anyOf" do 76 | it 'returns `(?:[f])`' do 77 | expect(hexpressed(:anyOf, "f")).to eq('(?:[f])') 78 | end 79 | end 80 | 81 | describe "#range" do 82 | it 'to be decided by spec' 83 | end 84 | 85 | describe "#withAnyCase" do 86 | it 'case insensitive search' 87 | end 88 | 89 | describe "#stopAtFirst" do 90 | it 'match first hexpression' 91 | end 92 | 93 | describe "#searchOneLine" do 94 | it 'only look at one line' 95 | end 96 | 97 | describe "#multiple" do 98 | it 'match multiple lines' 99 | end 100 | 101 | describe "#or" do 102 | it 'uses `|` as delimiter for or statements' 103 | end 104 | 105 | describe "#begindCapture" do 106 | it 'begins a capture' 107 | end 108 | 109 | describe "#endCapture" do 110 | it 'ends a capture' 111 | end 112 | end 113 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | Hexpress 2 | ------- 3 | 4 | - [![Gem Version](https://badge.fury.io/rb/hexpress.png)](https://rubygems.org/gems/hexpress) 5 | - [![Code Climate](https://codeclimate.com/github/krainboltgreene/hexpress.png)](https://codeclimate.com/github/krainboltgreene/hexpress) 6 | - [![Build Status](https://travis-ci.org/krainboltgreene/hexpress.png?branch=master)](https://travis-ci.org/krainboltgreene/hexpress) 7 | - [![Dependency Status](https://gemnasium.com/krainboltgreene/hexpress.png)](https://gemnasium.com/krainboltgreene/hexpress) 8 | - [![Coverage Status](https://coveralls.io/repos/krainboltgreene/hexpress/badge.png?branch=master)](https://coveralls.io/r/krainboltgreene/hexpress) 9 | 10 | The hexpress gem is another take at the concept of ["Verbal Hexpressions"]() in Ruby. 11 | 12 | 13 | Using 14 | ===== 15 | 16 | ``` ruby 17 | require "hexpress" 18 | 19 | pattern = Hexpress.new 20 | 21 | pattern = Hexpress.new. 22 | start("http"). 23 | maybe("s"). 24 | with("://"). 25 | maybe { words.with(".") }. 26 | find { matching { [word, "-"] }.multiple }. 27 | has("."). 28 | either("com", "org"). 29 | maybe("/"). 30 | ending 31 | ``` 32 | 33 | After requiring hexpress you'll have access to the Hexpress class, which allows you to chain methods to build up a regex pattern. 34 | 35 | You can see this pattern by calling either `Hexpress#to_s` or `Hexpress#to_r`: 36 | 37 | ``` ruby 38 | pattern.to_s #=> "^http(?:s)?://(?:(?:\\w)+\\.)?([\\w\\-]+)\\.(?:com|org)(?:/)?$" 39 | pattern.to_r #=> /^http(?:s)?:\/\/(?:(?:\w)+\.)?([\w\-]+)\.(?:com|org)(?:\/)?$/ 40 | ``` 41 | 42 | You can also get access to a global method by doing the following (automatically done in any rails application): 43 | 44 | ``` ruby 45 | require_relative "hexpress/main" 46 | 47 | hexp.start("http").maybe("s") 48 | 49 | hexp do 50 | start("http") 51 | maybe("s") 52 | end 53 | ``` 54 | 55 | In addition we've bundled an extra set of helpers for specific use cases like the web: 56 | 57 | ``` ruby 58 | require "hexpress" 59 | require "hexpress/web" 60 | 61 | pattern = hexp.http.domain("amazon").tld("com") 62 | pattern = hexp.ftp # ... 63 | ``` 64 | 65 | You can even do advanced composure of multiple patterns: 66 | 67 | ``` ruby 68 | protocol = exp.start("http").maybe("s").with("://") 69 | tld = exp.then(".").either("org", "com", "net") 70 | link = exp.has(protocol).find { words }.including(tld) 71 | ``` 72 | 73 | It's also entirely feasible to compound two or more patterns together: 74 | 75 | ``` ruby 76 | protocol = exp.start("http").maybe("s").with("://") 77 | domain = exp.find { words } 78 | tld = exp.then(".").either("org", "com", "net") 79 | link = protocol + domain + tld 80 | ``` 81 | 82 | Hexpressions are very flexible. 83 | 84 | Installing 85 | ========== 86 | 87 | Add this line to your application's Gemfile: 88 | 89 | gem "hexpress", "~> 1.0" 90 | 91 | And then execute: 92 | 93 | $ bundle 94 | 95 | Or install it yourself as: 96 | 97 | $ gem install hexpress 98 | 99 | 100 | Contributing 101 | ============ 102 | 103 | 1. Fork it 104 | 2. Create your feature branch (`git checkout -b my-new-feature`) 105 | 3. Commit your changes (`git commit -am 'Add some feature'`) 106 | 4. Push to the branch (`git push origin my-new-feature`) 107 | 5. Create new Pull Request 108 | 109 | 110 | Licensing 111 | ========= 112 | 113 | Copyright (c) 2013 Kurtis Rainbolt-Greene 114 | 115 | MIT License 116 | 117 | Permission is hereby granted, free of charge, to any person obtaining 118 | a copy of this software and associated documentation files (the 119 | "Software"), to deal in the Software without restriction, including 120 | without limitation the rights to use, copy, modify, merge, publish, 121 | distribute, sublicense, and/or sell copies of the Software, and to 122 | permit persons to whom the Software is furnished to do so, subject to 123 | the following conditions: 124 | 125 | The above copyright notice and this permission notice shall be 126 | included in all copies or substantial portions of the Software. 127 | 128 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, 129 | EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF 130 | MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND 131 | NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE 132 | LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION 133 | OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION 134 | WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. 135 | --------------------------------------------------------------------------------