gemmyrb 0.0.0

Sign up to get free protection for your applications and to get access to all the features.
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: []