gemmyrb 0.0.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
checksums.yaml ADDED
@@ -0,0 +1,7 @@
1
+ ---
2
+ SHA1:
3
+ metadata.gz: 18532f58f02f659faa596f0ed22dd3f85acfa1a4
4
+ data.tar.gz: 17ee68de85cf655f22bb5190e33a27378ecee904
5
+ SHA512:
6
+ metadata.gz: 32ef4734ff5168c54883208469f150927eb09053786e8dc6f5721f6d449061f509a60b0a1028eca628d973ef042339d4e55ee9be8d9c3edba1aea3ecf2e60d7c
7
+ data.tar.gz: cee902db7fa77e8e555f6473bf4bd1191353206df35697d0ade31a5bcb0a13322db9335ebbb59a330810cb4aacc3c1ad3e04c0f035b5dabf99d06b0799360c85
data/README.md ADDED
@@ -0,0 +1,18 @@
1
+ ### Gemmy gem
2
+
3
+ This is a general purpose gem.
4
+
5
+ I'm continually adding small core patches and utility classes.
6
+
7
+ It is on RubyGems and can be installed with `gem install gemmyrb`
8
+
9
+ **note**
10
+
11
+ because there is an existing gem named `gemmy`, the name passed
12
+ to `gem install` is `gemmyrb`. But `require 'gemmy'` is still used.
13
+
14
+ For more information, see the following documents:
15
+
16
+ - [examples/01_using_as_refinement.rb](.examples/01_using_as_refinement.rb)
17
+ - [examples/02_using_globally.rb](.examples/02_using_globally.rb)
18
+ - [examples/03_full_api.rb](.examples/03_full_api.rb)
data/bin/gemmy ADDED
@@ -0,0 +1,5 @@
1
+ #!/usr/bin/env ruby
2
+
3
+ require 'gemmy'
4
+
5
+ Gemmy::CLI.run
data/lib/gemmy/cli.rb ADDED
@@ -0,0 +1,41 @@
1
+ # Command line interface
2
+ #
3
+ # Run from command line:
4
+ # gemmy <arguments>
5
+ # e.g. gemmy help
6
+ #
7
+ # Start from code with Gemmy::CLI.run
8
+ #
9
+ class Gemmy::CLI < Thor
10
+
11
+ # Start the CLI
12
+ # @param arguments [Array<String>] passed to thor, defaults to ARGV
13
+ #
14
+ def self.run(arguments: nil)
15
+ # Store a copy of the arguments.
16
+ # The originals are shifted so they don't intefere with gets
17
+ arguments = ARGV.clone
18
+ ARGV.clear
19
+
20
+ # Can't make this conditional on "__FILE__ == $0"
21
+ # Because of the way gem executables are run
22
+ start arguments
23
+ end
24
+
25
+ # Task to make a gem
26
+ # @param name [String] the name of the gem
27
+ # Other options are requested via gets
28
+ #
29
+ desc "make_gem NAME", "make a skeleton ruby gem project"
30
+ def make_gem(name)
31
+ Gemmy::Tasks::MakeGem.run(name)
32
+ end
33
+
34
+ # Test the gem.
35
+ #
36
+ desc "test", "test the gem"
37
+ def test
38
+ Gemmy::Tests.run
39
+ end
40
+
41
+ end
@@ -0,0 +1,92 @@
1
+ # Mimics the Cucumber API:
2
+ # {DynamicSteps#step} runs a step
3
+ # {DynamicSteps#define_step} defines a step
4
+ #
5
+ # The usage is the same as Cucumber:
6
+ #
7
+ # define_step /I print (.+) (.+) times/ do |string, n|
8
+ # n.times { print string }
9
+ # end
10
+ #
11
+ # step "I print hello 2 times"
12
+ # => 'hellohello'
13
+ #
14
+ # Like Cucumber, it will raise an error if there is > 1 matching step
15
+ # {DynamicSteps::AmbiguousMatchError} can be rescued if desired.
16
+ #
17
+ # It also raises an error if no matcher was found
18
+ #
19
+ module Gemmy::Components::DynamicSteps
20
+
21
+ Gemmy::Patches.refinements.each { |r| using r }
22
+
23
+ # A hash mapping regex to proc
24
+ #
25
+ attr_reader :steps
26
+
27
+ # Error raised when a string matches multiple step regexes.
28
+ # It's frequently accidental to come into this situation,
29
+ # and having this check prevents surprise errors.
30
+ #
31
+ class AmbiguousMatchError < StandardError; end
32
+
33
+ # Error raised when no matcher is found for a string
34
+ #
35
+ class NoMatchFoundError < StandardError; end
36
+
37
+ def steps
38
+ @steps ||= {}
39
+ end
40
+
41
+ # Defines a regex => proc mapping
42
+ # A good pattern for regex is to use (.+) as match groups, and
43
+ # mirror those as sequential named parameters in the block.
44
+ #
45
+ # Match groups are left-greedy, for example:
46
+ # "1 2 3 4 5".match(/(.+) (.+)/).tap &:shift
47
+ # # => ['1 2 3 4', '5']
48
+ #
49
+ def define_step(regex, &blk)
50
+ steps[regex] = blk
51
+ end
52
+
53
+ # run a step.
54
+ # searches @step keys for regex which matches the string
55
+ # then runs the associated proc, passing the regex match results
56
+ #
57
+ def step(string)
58
+ matching_steps = find_matching_steps(string)
59
+ if matching_steps.keys.length > 1
60
+ # Failure, multiple matching steps
61
+ raise(
62
+ AmbiguousMatchError,
63
+ "step #{string} matched: #{matching_steps.keys.join(", ")}"
64
+ )
65
+ elsif matching_steps.keys.length == 0
66
+ # Failure, no matching step
67
+ raise(
68
+ NoMatchFoundError,
69
+ "step #{string} had no match"
70
+ )
71
+ else
72
+ # Success, run the proc
73
+ matching_step = matching_steps.values[0]
74
+ matching_step[:proc].call(*(matching_step[:matches]))
75
+ end
76
+ end
77
+
78
+ # Searches the keys in @steps for a regex the matches the string.
79
+ # If one is found, it adds the regex as a key in the results hash.
80
+ # The value is a hash with two keys: :matches (an array) and :proc
81
+ #
82
+ def find_matching_steps(string)
83
+ matching_steps = steps.reduce({}) do |matching_steps, (regex, proc)|
84
+ match_results = string.match(regex).to_a.tap &:shift
85
+ if match_results.any_not? &:blank?
86
+ matching_steps[regex] = { matches: match_results, proc: proc }
87
+ end
88
+ matching_steps
89
+ end
90
+ end
91
+
92
+ end
@@ -0,0 +1,17 @@
1
+ # Different collections of functionality
2
+ #
3
+ module Gemmy::Components
4
+ def self.included(base)
5
+ list.each do |klass|
6
+ base.include klass
7
+ base.extend klass
8
+ end
9
+ end
10
+
11
+ def self.list
12
+ [
13
+ Gemmy::Components::DynamicSteps
14
+ ]
15
+ end
16
+
17
+ end
@@ -0,0 +1,19 @@
1
+ # Global patches. Could be applied to Object or Kernel
2
+ #
3
+ module Gemmy::Patches::ArrayPatch
4
+ # checks if any of the results of an array do not respond truthily
5
+ # to a block
6
+ #
7
+ # For example, to check if any items of an array are truthy:
8
+ # [false, nil, ''].any_not? &:blank?
9
+ # => false
10
+ #
11
+ def any_not?(&blk)
12
+ any? { |item| ! blk.call(item) }
13
+ end
14
+
15
+ refine Array do
16
+ include Gemmy::Patches::ArrayPatch
17
+ end
18
+
19
+ end
@@ -0,0 +1,104 @@
1
+ # Hash patches
2
+ #
3
+ module Gemmy::Patches::HashPatch
4
+
5
+ # The opposite of Hash#dig
6
+ # Takes a list of keys followed by a value to set
7
+ #
8
+ # Example:
9
+ #
10
+ # a = {a: {b: {}} }
11
+ # a.bury(:a, :b, :c, 0)
12
+ # puts a[:a][:b][:c]
13
+ # => 0
14
+ #
15
+ # Source: https://github.com/dam13n/ruby-bury/blob/master/hash.rb
16
+ #
17
+ def bury *args
18
+ Gemmy::Patches::HashPatch._bury(self, *args)
19
+ end
20
+
21
+ # The bury method, taking the input hash as a parameter
22
+ # Used by the Hash#bury instance method
23
+ def self._bury(caller_hash, *args)
24
+ if args.count < 2
25
+ raise ArgumentError.new("2 or more arguments required")
26
+ elsif args.count == 2
27
+ caller_hash[args[0]] = args[1]
28
+ else
29
+ arg = args.shift
30
+ caller_hash[arg] = {} unless caller_hash[arg]
31
+ _bury(caller_hash[arg], *args) unless args.empty?
32
+ end
33
+ caller_hash
34
+ end
35
+
36
+ # Turns a hash into one that's "autovivified"
37
+ # meaning it's default values for keys is an empty hash.
38
+ # The result is that you can set nested keys without initializing
39
+ # more than one hash layer.
40
+ #
41
+ # Usage:
42
+ # hash = {}.autovivified
43
+ # hash[:a][:b] = 0
44
+ # puts hash[:a][:b]
45
+ # => 0
46
+ #
47
+ def autovivified
48
+ Gemmy::Patches::HashPatch._autovivified(self)
49
+ end
50
+
51
+ def self._autovivified(caller_hash)
52
+ result = Hash.new { |hash,key| hash[key] = Hash.new(&hash.default_proc) }
53
+ result.deep_merge caller_hash
54
+ end
55
+
56
+ # Sets up a hash to mirror all changes to a database file
57
+ # All nested gets & sets require a list of keys, passed as subsequent args.
58
+ # Instead of [] and []=, use get and set
59
+ #
60
+ # Everything in the db is contained in a hash with one predefined
61
+ # key, :data. The value is an empty, autovivified hash.
62
+ #
63
+ # This also makes the caller hash autovivified
64
+ #
65
+ # Example:
66
+ #
67
+ # hash = {}.persisted("db.yaml")
68
+ # hash.set(:a, :b, 0) # => this writes to disk and memory
69
+ # hash.get(:a, :b) # => reads from memory
70
+ # hash.get(:a, :b, disk: true) # => reads from disk
71
+ #
72
+ def persisted(path)
73
+ require 'yaml/store'
74
+ Gemmy::Patches::HashPatch._autovivified(self).tap do |hash|
75
+ hash.instance_exec do
76
+ @db = YAML::Store.new path
77
+ @db.transaction do
78
+ @db[:data] = Gemmy::Patches::HashPatch._autovivified({})
79
+ end
80
+ end
81
+ hash.extend Gemmy::Patches::HashPatch::PersistedHash
82
+ end
83
+ end
84
+
85
+ # Helper methods for the persistence patch
86
+ #
87
+ module PersistedHash
88
+ def get(*keys, disk: false)
89
+ disk ? @db.transaction { @db[:data].dig(*keys) } : dig(*keys)
90
+ end
91
+ def set(*keys, val)
92
+ Gemmy::Patches::HashPatch._bury(self, *keys, val)
93
+ @db.transaction do
94
+ Gemmy::Patches::HashPatch._bury(@db[:data], *(keys + [val]))
95
+ end
96
+ val
97
+ end
98
+ end
99
+
100
+ refine Hash do
101
+ include Gemmy::Patches::HashPatch
102
+ end
103
+
104
+ end
@@ -0,0 +1,22 @@
1
+ # Method patches. To the 'Method' class i.e. `method(:puts)`
2
+ #
3
+ module Gemmy::Patches::MethodPatch
4
+
5
+ # Bind an argument to a method.
6
+ # Very useful for the proc shorthand.
7
+ # For example say there's a method "def add(a,b); print a + b; end"
8
+ # You can run it for each number in a list:
9
+ # [1,2,3].each &method(:add).bind(1)
10
+ # => 234
11
+ #
12
+ def bind *args
13
+ Proc.new do |*more|
14
+ self.call *(args + more)
15
+ end
16
+ end
17
+
18
+ refine Method do
19
+ include Gemmy::Patches::MethodPatch
20
+ end
21
+
22
+ end
@@ -0,0 +1,62 @@
1
+ # Object patches. Can be called with implicit receiver
2
+ #
3
+ module Gemmy::Patches::ObjectPatch
4
+
5
+ # Turns on verbose mode, showing warnings
6
+ #
7
+ def verbose_mode
8
+ $VERBOSE = true
9
+ end
10
+
11
+ # Generic error. Raises RuntimeError
12
+ # @param msg [String] optional
13
+ #
14
+ def error(msg='')
15
+ raise RuntimeError, msg
16
+ end
17
+
18
+ # Prints a string then gets input
19
+ # @param txt [String]
20
+ #
21
+ def _prompt(txt)
22
+ puts txt
23
+ gets.chomp
24
+ end
25
+
26
+ # Shifts one ARGV and raises a message if it's undefined.
27
+ # @param msg [String]
28
+ #
29
+ def get_arg_or_error(msg)
30
+ ([ARGV.shift, msg].tap &method(:error_if_blank)).shift
31
+ end
32
+
33
+ # Writes a string to a file
34
+ # @param file [String] path to write to
35
+ # @param text [String] text to write
36
+ #
37
+ def write(file:, text:)
38
+ File.open(file, 'w', &:write.with(text))
39
+ end
40
+
41
+ # if args[0] (object) is blank, raises args[1] (message)
42
+ # @param args [Array] - value 1 is obj, value 2 is msg
43
+ #
44
+ def error_if_blank(args)
45
+ obj, msg = args
46
+ obj.blank? && error(msg)
47
+ end
48
+
49
+ # shorter proc shorthands
50
+ #
51
+ alias m method
52
+
53
+ # method which does absolutely nothing, ignoring all arguments
54
+ #
55
+ def nothing(*args)
56
+ end
57
+
58
+ refine Object do
59
+ include Gemmy::Patches::ObjectPatch
60
+ end
61
+
62
+ end
@@ -0,0 +1,21 @@
1
+ # String patches
2
+ #
3
+ module Gemmy::Patches::StringPatch
4
+
5
+ # reference 'strip_heredoc' (provided by active support) by 'unindent'
6
+ #
7
+ # this takes an indented heredoc and treats it as if the first line is not
8
+ # indented.
9
+ #
10
+ # Instead of using alias, a method is defined here to enable modular
11
+ # patches
12
+ #
13
+ def unindent
14
+ strip_heredoc
15
+ end
16
+
17
+ refine String do
18
+ include Gemmy::Patches::StringPatch
19
+ end
20
+
21
+ end
@@ -0,0 +1,19 @@
1
+ # Symbol patches
2
+ #
3
+ module Gemmy::Patches::SymbolPatch
4
+
5
+ # Patch symbol so the proc shorthand can take extra arguments
6
+ # http://stackoverflow.com/a/23711606/2981429
7
+ #
8
+ # Example: [1,2,3].map &:*.with(2)
9
+ # => [2,4,6]
10
+ #
11
+ def with(*args, &block)
12
+ ->(caller, *rest) { caller.send(self, *rest, *args, &block) }
13
+ end
14
+
15
+ refine Symbol do
16
+ include Gemmy::Patches::SymbolPatch
17
+ end
18
+
19
+ end
@@ -0,0 +1,18 @@
1
+ # Thread patches
2
+ # This does some core configuration to Thread as soon as the patches are
3
+ # included, so functionality will be changed even without calling any of these
4
+ # methods
5
+ #
6
+ module Gemmy::Patches::ThreadPatch
7
+
8
+ # Ensure that threads bubble up their errors
9
+ #
10
+ def self.included(base)
11
+ Thread.abort_on_exception = true
12
+ end
13
+
14
+ refine Thread do
15
+ include Gemmy::Patches::ThreadPatch
16
+ end
17
+
18
+ end
@@ -0,0 +1,25 @@
1
+ # Gemmy provides patches for a few of the core classes.
2
+ #
3
+ # For example:
4
+ # Gemmy::Patches.load(only: [:string])
5
+ # Gemmy::Patches.load(except: [:symbol, :global])
6
+ #
7
+ module Gemmy::Patches
8
+
9
+ def self.refinements
10
+ core_patches.values
11
+ end
12
+
13
+ def self.core_patches
14
+ @@core_patches ||= {
15
+ String: Gemmy::Patches::StringPatch,
16
+ Symbol: Gemmy::Patches::SymbolPatch,
17
+ Object: Gemmy::Patches::ObjectPatch,
18
+ Array: Gemmy::Patches::ArrayPatch,
19
+ Method: Gemmy::Patches::MethodPatch,
20
+ Hash: Gemmy::Patches::HashPatch,
21
+ Thread: Gemmy::Patches::ThreadPatch,
22
+ }
23
+ end
24
+
25
+ end
@@ -0,0 +1,189 @@
1
+ # A task to create skeleton structure for a ruby gem
2
+ #
3
+ # Only one method is intended for public use, {Tasks::MakeGem#run}.
4
+ #
5
+ # It takes one argument - the name of the ruby gem.
6
+ #
7
+ # It then prompts for a few more defails (summary, email, and name).
8
+ #
9
+ # Here's the structure it creates:
10
+ #
11
+ # ├── <name>.gemspec
12
+ # ├── Gemfile
13
+ # └── lib
14
+ # ├── <name>.rb
15
+ # └── version.rb
16
+ #
17
+ # At which point you can run "gem build" then "gem install"
18
+ #
19
+ class Gemmy::Tasks::MakeGem
20
+
21
+ Gemmy::Patches.refinements.each { |r| using r }
22
+
23
+ # Builds a skeleton ruby gem.
24
+ # Prompts for some input using gets.chomp
25
+ #
26
+ def self.run(name)
27
+ new.make_gem(name)
28
+ end
29
+
30
+ # calls a sequence of commands to build out the gem directory
31
+ #
32
+ def make_gem(name)
33
+ @name = name
34
+ @class_name = name.capitalize
35
+
36
+ usage_io
37
+ create_root_dir
38
+ create_lib_dir
39
+ create_version_file
40
+ create_main_file
41
+ gemspec_info_io
42
+ create_gemspec_file
43
+ create_gemfile
44
+ create_reinstall_file
45
+ end
46
+
47
+ attr_reader :name, :root_dir, :lib, :version_file, :main_file, :summary,
48
+ :author, :email, :gemspec_file, :class_name, :rubygems_version,
49
+ :gemfile
50
+
51
+ private
52
+
53
+ # A reinstall file is very helpful for development
54
+ def create_reinstall_file
55
+ file_txt = <<-TXT.unindent
56
+ #!/usr/bin/env ruby
57
+ puts `gem uninstall -x #{name}`
58
+ puts `gem build #{name}.gemspec`
59
+ Dir.glob("./*.gem").each { |path| puts `gem install #{path}` }
60
+ TXT
61
+ file_path = "#{root_dir}/reinstall"
62
+ File.open(file_path, 'w') do |file|
63
+ file.write file_txt
64
+ end
65
+ `chmod a+x #{file_path}`
66
+ end
67
+
68
+ # prints usage instructions unless the gem name was specified
69
+ #
70
+ def usage_io
71
+ usage = <<-TXT.unindent
72
+ \nUsage: make_gem <name>
73
+ TXT
74
+ raise usage if @name.blank?
75
+ self
76
+ end
77
+
78
+ # creates a root directory for the gem
79
+ #
80
+ def create_root_dir
81
+ @root_dir = name
82
+ `mkdir #{root_dir}`
83
+ puts "created root dir".green
84
+ self
85
+ end
86
+
87
+ # creates a lib directory for the gme
88
+ #
89
+ def create_lib_dir
90
+ @lib = "#{root_dir}/lib"
91
+ `mkdir #{lib}`
92
+ puts "created lib dir".green
93
+ self
94
+ end
95
+
96
+ # creates a version file for the gem
97
+ #
98
+ def create_version_file
99
+ @version_file = "#{lib}/version.rb"
100
+ version_text = <<-TXT.unindent
101
+ module #{class_name}
102
+ VERSION = '0.0.0'
103
+ end
104
+ TXT
105
+ write file: version_file, text: version_text
106
+ puts "wrote version file".green
107
+ self
108
+ end
109
+
110
+ # creates a main file for the gem.
111
+ # this file explicitly requires each of the gem's dependencies.
112
+ #
113
+ def create_main_file
114
+ @main_file = "#{lib}/#{name}.rb"
115
+ main_text = <<-TXT.unindent
116
+ require 'colored'
117
+ require 'pry'
118
+ require 'active_support/all'
119
+ require 'thor'
120
+ module #{class_name}
121
+ end
122
+ Gem.find_files("#{name}/**/*.rb").each &method(:require)
123
+ TXT
124
+ write file: main_file, text: main_text
125
+ puts "wrote main file".green
126
+ self
127
+ end
128
+
129
+ # prompts for some additional info required for the gemspec
130
+ #
131
+ def gemspec_info_io
132
+ puts "Some info is needed for the gemspec:".red
133
+ @summary = _prompt "add a one-line summary."
134
+ @author = _prompt "enter the author's name"
135
+ @email = _prompt "enter the author's email"
136
+ @rubygems_version = `gem -v`.chomp
137
+ self
138
+ end
139
+
140
+ # creates a gemspec file
141
+ # add gem dependencies here, not the Gemfile
142
+ # the Gemfile contains a reference to this file
143
+ #
144
+ def create_gemspec_file
145
+ @gemspec_file = "./#{root_dir}/#{name}.gemspec"
146
+ gemspec_text = <<-TXT.unindent
147
+ require_relative './lib/version.rb'
148
+ Gem::Specification.new do |s|
149
+ s.name = name
150
+ s.version = #{class_name}::VERSION
151
+ s.date = "#{Time.now.strftime("%Y-%m-%d")}"
152
+ s.summary = "#{summary}"
153
+ s.description = ""
154
+ s.platform = Gem::Platform::RUBY
155
+ s.authors = ["#{author}"]
156
+ s.email = '#{email}'
157
+ s.homepage = "http://github.com/maxpleaner/gemmy"
158
+ s.files = Dir["lib/**/*.rb", "bin/*", "**/*.md", "LICENSE"]
159
+ s.require_path = 'lib'
160
+ s.required_rubygems_version = ">= #{rubygems_version}"
161
+ s.executables = Dir["bin/*"].map &File.method(:basename)
162
+ s.add_dependency "colored", '~> 1.2'
163
+ s.add_dependency 'activesupport', '~> 4.2', '>= 4.2.7'
164
+ s.add_dependency 'pry', '~> 0.10.4'
165
+ s.add_dependency 'thor', '~> 0.19.4'
166
+ s.license = 'MIT'
167
+ end
168
+ TXT
169
+ write file: gemspec_file, text: gemspec_text
170
+ puts "wrote gemspec".green
171
+ self
172
+ end
173
+
174
+ # creates a gemfile which contains a reference to the gemspec
175
+ # in a ruby gem, gems are listed in the gemspec, not the gemfile.
176
+ #
177
+ def create_gemfile
178
+ @gemfile = "./#{root_dir}/Gemfile"
179
+ gemfile_text = <<-TXT.unindent
180
+ source "http://www.rubygems.org"
181
+ gemspec
182
+ TXT
183
+ write file: gemfile, text: gemfile_text
184
+ puts "wrote gemfile".green
185
+ self
186
+ end
187
+
188
+ end
189
+
@@ -0,0 +1,4 @@
1
+ # Tasks which are accessible through the CLI
2
+ # They can also be programatically run in Ruby
3
+ #
4
+ module Gemmy::Tasks; end
@@ -0,0 +1,21 @@
1
+ module Gemmy::Tests::ComponentTests::DynamicStepsTests
2
+
3
+ Gemmy::Patches.refinements.each { |r| using r }
4
+
5
+ def self.run
6
+ runner_class = Class.new
7
+ runner_class.include Gemmy::Components::DynamicSteps
8
+ runner = runner_class.new
9
+ runner.define_step(/(.+) case (.+)/) do |a,b|
10
+ error("failed") unless (a=='test') && (b=='pass')
11
+ end
12
+ puts " define_step".blue
13
+ runner.step "test case pass"
14
+ begin
15
+ runner.step "test case fail"
16
+ rescue RuntimeError => e
17
+ error("unexpected fail") unless e.message==("failed")
18
+ puts " step".blue
19
+ end
20
+ end
21
+ end
@@ -0,0 +1,11 @@
1
+ module Gemmy::Tests
2
+
3
+ module ComponentTests
4
+ def self.run
5
+ Gemmy::Tests.run_test_class DynamicStepsTests
6
+ end
7
+ end
8
+
9
+ TestSets << ComponentTests
10
+
11
+ end
@@ -0,0 +1,127 @@
1
+ module Gemmy::Tests
2
+
3
+ module PatchTests
4
+
5
+ def self.run
6
+ # call each of the patch_class methods
7
+ %i{
8
+ array_test symbol_test method_test thread_test
9
+ global_test string_test hash_test
10
+ }.each { |method_name| PatchedClass.send method_name }
11
+ end
12
+
13
+ module Error
14
+ # Allow using raise with one argument only
15
+ # raises RuntimeError
16
+ #
17
+ # this is included in the global patches,
18
+ # but the test suite doesn't depend on it.
19
+ #
20
+ # @param msg [String]
21
+ #
22
+ def error(msg='')
23
+ raise(RuntimeError, msg)
24
+ end
25
+ end
26
+
27
+ class PatchedClass
28
+
29
+ Gemmy::Patches.refinements.each { |r| using r }
30
+
31
+ extend Gemmy::Tests::PatchTests::Error
32
+
33
+ def self.thread_test
34
+ # Threads abort on exception
35
+ Thread.new { fail }
36
+ sleep 0.25
37
+ error "thread didn't bubble up error"
38
+ rescue
39
+ puts " Threads abort on exception".blue
40
+ end
41
+
42
+ def self.hash_test
43
+ # Hash#autovivified
44
+ hash = {}.autovivified
45
+ hash[:a][:b] = 0
46
+ error unless hash[:a][:b] == 0
47
+ puts " Hash#autovivified".blue
48
+
49
+ # Hash#bury
50
+ hash = { a: { b: { } } }
51
+ hash.bury(:a, :b, :c, 0)
52
+ error unless hash[:a][:b][:c] == 0
53
+ puts " Hash#bury".blue
54
+
55
+ # Hash#persisted
56
+ db = "test_db.yaml"
57
+ hash = {}.persisted db
58
+ hash.set(:a, :b, 0)
59
+ error unless hash.get(:a, :b) == 0
60
+ error unless hash.get(:a, :b, disk: true) == 0
61
+ error unless YAML.load(File.read db)[:data][:a][:b] == 0
62
+ File.delete(db)
63
+ puts " Hash#persisted".blue
64
+ end
65
+
66
+ def self.array_test
67
+ # Array#any_not?
68
+ false_case = [[]].any_not? &:empty?
69
+ true_case = [[1]].any_not? &:empty?
70
+ error unless true_case && !false_case
71
+ puts " Array#any_not?".blue
72
+ end
73
+
74
+ def self.symbol_test
75
+ # Symbol#with
76
+ result = [[]].map &:concat.with([1])
77
+ error unless result == [[1]]
78
+ puts " Symbol#with".blue
79
+ end
80
+
81
+ def self.method_test
82
+ # Method#bind
83
+ def self.add(a,b)
84
+ a + b
85
+ end
86
+ result = [0].map &method(:add).bind(1)
87
+ error unless result == [1]
88
+ puts " Method#bind".blue
89
+ end
90
+
91
+ def self.global_test
92
+ # m is an alias for method
93
+ def self.sample_method(x)
94
+ x
95
+ end
96
+ result = [0].map &m(:sample_method)
97
+ error unless result == [0]
98
+ puts " Object#m".blue
99
+
100
+ # error_if_blank
101
+ # error an error if an object is blank
102
+ blank = nil
103
+ not_blank = 0
104
+ error_if_blank not_blank
105
+ begin
106
+ error_if_blank blank
107
+ error("this won't be raised")
108
+ rescue RuntimeError => e
109
+ error if e.message == "this won't be raised"
110
+ puts " Object#error_if_blank".blue
111
+ end
112
+ end
113
+
114
+ def self.string_test
115
+ # String#unindent
116
+ result = " 0".unindent
117
+ error unless result == "0"
118
+ puts " String#unindent".blue
119
+ end
120
+
121
+ end
122
+
123
+ end
124
+
125
+ TestSets << PatchTests
126
+
127
+ end
@@ -0,0 +1,16 @@
1
+ # Test out the gem
2
+ # Can be called from the command line: gemmy test
3
+ #
4
+ module Gemmy::Tests
5
+ TestSets = []
6
+
7
+ def self.run
8
+ TestSets.each &method(:run_test_class)
9
+ end
10
+
11
+ def self.run_test_class(klass)
12
+ klass.run
13
+ puts "#{klass} passed".green
14
+ end
15
+
16
+ end
@@ -0,0 +1,3 @@
1
+ class Gemmy
2
+ VERSION = '0.0.0'
3
+ end
data/lib/gemmy.rb ADDED
@@ -0,0 +1,34 @@
1
+ require 'thor'
2
+ require 'colored'
3
+ require 'active_support/all'
4
+ require 'pry'
5
+ require 'colored'
6
+
7
+ # Container class for all the functionality.
8
+ #
9
+ class Gemmy
10
+
11
+ # There are two main usages:
12
+ # - namespaced (using refinements and explicit include/extend calls)
13
+ # - global
14
+ #
15
+ # This is the method handling the global case
16
+ #
17
+ def self.load_globally
18
+ Patches.core_patches.each do |core_klass_name, patch_klass|
19
+ core_klass_name.to_s.constantize.include patch_klass
20
+ end
21
+ Components.list.each do |patch_klass|
22
+ Object.include patch_klass
23
+ Object.extend patch_klass
24
+ end
25
+ end
26
+ end
27
+
28
+ # Load files in order of their "/" count
29
+ # to preserve constant hierarchies
30
+ #
31
+ Gem.find_files("gemmy/**/*.rb").sort_by do |x|
32
+ x.split("/").length
33
+ end.each &method(:require)
34
+
metadata ADDED
@@ -0,0 +1,141 @@
1
+ --- !ruby/object:Gem::Specification
2
+ name: gemmyrb
3
+ version: !ruby/object:Gem::Version
4
+ version: 0.0.0
5
+ platform: ruby
6
+ authors:
7
+ - max pleaner
8
+ autorequire:
9
+ bindir: bin
10
+ cert_chain: []
11
+ date: 2016-12-04 00:00:00.000000000 Z
12
+ dependencies:
13
+ - !ruby/object:Gem::Dependency
14
+ name: colored
15
+ requirement: !ruby/object:Gem::Requirement
16
+ requirements:
17
+ - - "~>"
18
+ - !ruby/object:Gem::Version
19
+ version: '1.2'
20
+ type: :runtime
21
+ prerelease: false
22
+ version_requirements: !ruby/object:Gem::Requirement
23
+ requirements:
24
+ - - "~>"
25
+ - !ruby/object:Gem::Version
26
+ version: '1.2'
27
+ - !ruby/object:Gem::Dependency
28
+ name: activesupport
29
+ requirement: !ruby/object:Gem::Requirement
30
+ requirements:
31
+ - - "~>"
32
+ - !ruby/object:Gem::Version
33
+ version: '4.2'
34
+ - - ">="
35
+ - !ruby/object:Gem::Version
36
+ version: 4.2.7
37
+ type: :runtime
38
+ prerelease: false
39
+ version_requirements: !ruby/object:Gem::Requirement
40
+ requirements:
41
+ - - "~>"
42
+ - !ruby/object:Gem::Version
43
+ version: '4.2'
44
+ - - ">="
45
+ - !ruby/object:Gem::Version
46
+ version: 4.2.7
47
+ - !ruby/object:Gem::Dependency
48
+ name: pry
49
+ requirement: !ruby/object:Gem::Requirement
50
+ requirements:
51
+ - - "~>"
52
+ - !ruby/object:Gem::Version
53
+ version: 0.10.4
54
+ type: :runtime
55
+ prerelease: false
56
+ version_requirements: !ruby/object:Gem::Requirement
57
+ requirements:
58
+ - - "~>"
59
+ - !ruby/object:Gem::Version
60
+ version: 0.10.4
61
+ - !ruby/object:Gem::Dependency
62
+ name: byebug
63
+ requirement: !ruby/object:Gem::Requirement
64
+ requirements:
65
+ - - "~>"
66
+ - !ruby/object:Gem::Version
67
+ version: 9.0.6
68
+ type: :runtime
69
+ prerelease: false
70
+ version_requirements: !ruby/object:Gem::Requirement
71
+ requirements:
72
+ - - "~>"
73
+ - !ruby/object:Gem::Version
74
+ version: 9.0.6
75
+ - !ruby/object:Gem::Dependency
76
+ name: thor
77
+ requirement: !ruby/object:Gem::Requirement
78
+ requirements:
79
+ - - "~>"
80
+ - !ruby/object:Gem::Version
81
+ version: 0.19.4
82
+ type: :runtime
83
+ prerelease: false
84
+ version_requirements: !ruby/object:Gem::Requirement
85
+ requirements:
86
+ - - "~>"
87
+ - !ruby/object:Gem::Version
88
+ version: 0.19.4
89
+ description: ''
90
+ email: maxpleaner@gmail.com
91
+ executables:
92
+ - gemmy
93
+ extensions: []
94
+ extra_rdoc_files: []
95
+ files:
96
+ - README.md
97
+ - bin/gemmy
98
+ - lib/gemmy.rb
99
+ - lib/gemmy/cli.rb
100
+ - lib/gemmy/components.rb
101
+ - lib/gemmy/components/dynamic_steps.rb
102
+ - lib/gemmy/patches.rb
103
+ - lib/gemmy/patches/array_patch.rb
104
+ - lib/gemmy/patches/hash_patch.rb
105
+ - lib/gemmy/patches/method_patch.rb
106
+ - lib/gemmy/patches/object_patch.rb
107
+ - lib/gemmy/patches/string_patch.rb
108
+ - lib/gemmy/patches/symbol_patch.rb
109
+ - lib/gemmy/patches/thread_patch.rb
110
+ - lib/gemmy/tasks.rb
111
+ - lib/gemmy/tasks/make_gem.rb
112
+ - lib/gemmy/tests.rb
113
+ - lib/gemmy/tests/component_tests.rb
114
+ - lib/gemmy/tests/component_tests/dynamic_steps_tests.rb
115
+ - lib/gemmy/tests/patch_test.rb
116
+ - lib/gemmy/version.rb
117
+ homepage: http://github.com/maxpleaner/gemmy
118
+ licenses:
119
+ - MIT
120
+ metadata: {}
121
+ post_install_message:
122
+ rdoc_options: []
123
+ require_paths:
124
+ - lib
125
+ required_ruby_version: !ruby/object:Gem::Requirement
126
+ requirements:
127
+ - - ">="
128
+ - !ruby/object:Gem::Version
129
+ version: '0'
130
+ required_rubygems_version: !ruby/object:Gem::Requirement
131
+ requirements:
132
+ - - ">="
133
+ - !ruby/object:Gem::Version
134
+ version: 1.3.6
135
+ requirements: []
136
+ rubyforge_project:
137
+ rubygems_version: 2.5.1
138
+ signing_key:
139
+ specification_version: 4
140
+ summary: general utils
141
+ test_files: []