pinstripe 0.1.2

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.
Files changed (64) hide show
  1. checksums.yaml +7 -0
  2. data/.gitignore +5 -0
  3. data/.pinstripe +3 -0
  4. data/Gemfile +4 -0
  5. data/Gemfile.lock +24 -0
  6. data/LICENSE +20 -0
  7. data/README.md +5 -0
  8. data/Rakefile +10 -0
  9. data/exe/pinstripe +4 -0
  10. data/lib/pinstripe/call_handler.rb +12 -0
  11. data/lib/pinstripe/command.rb +20 -0
  12. data/lib/pinstripe/commands/list_commands.rb +16 -0
  13. data/lib/pinstripe/commands/start_console.rb +14 -0
  14. data/lib/pinstripe/concern.rb +19 -0
  15. data/lib/pinstripe/database/row.rb +48 -0
  16. data/lib/pinstripe/database/table.rb +131 -0
  17. data/lib/pinstripe/database/table_alias_manager.rb +20 -0
  18. data/lib/pinstripe/database/union.rb +20 -0
  19. data/lib/pinstripe/database.rb +56 -0
  20. data/lib/pinstripe/helper.rb +20 -0
  21. data/lib/pinstripe/helpers.rb +8 -0
  22. data/lib/pinstripe/inflector/inflections.rb +122 -0
  23. data/lib/pinstripe/inflector.rb +28 -0
  24. data/lib/pinstripe/monkey_patches/pluralize.rb +8 -0
  25. data/lib/pinstripe/monkey_patches/require_all.rb +18 -0
  26. data/lib/pinstripe/monkey_patches/singularize.rb +8 -0
  27. data/lib/pinstripe/monkey_patches/to_params_hash.rb +20 -0
  28. data/lib/pinstripe/params_hash.rb +44 -0
  29. data/lib/pinstripe/project.rb +29 -0
  30. data/lib/pinstripe/registry.rb +46 -0
  31. data/lib/pinstripe/resource_factories/call_handler.rb +8 -0
  32. data/lib/pinstripe/resource_factories/env.rb +9 -0
  33. data/lib/pinstripe/resource_factories/environment.rb +8 -0
  34. data/lib/pinstripe/resource_factories/project.rb +8 -0
  35. data/lib/pinstripe/resource_factory.rb +32 -0
  36. data/lib/pinstripe/resource_provider.rb +50 -0
  37. data/lib/pinstripe/version.rb +3 -0
  38. data/lib/pinstripe/workspace.rb +8 -0
  39. data/lib/pinstripe.rb +24 -0
  40. data/package.json +15 -0
  41. data/pinstripe +4 -0
  42. data/pinstripe.gemspec +38 -0
  43. data/rollup.config.js +19 -0
  44. data/web/javascripts/_pinstripe/event_wrapper.js +29 -0
  45. data/web/javascripts/_pinstripe/index.js +3 -0
  46. data/web/javascripts/_pinstripe/initialize.js +29 -0
  47. data/web/javascripts/_pinstripe/node_wrapper.js +354 -0
  48. data/web/javascripts/_pinstripe/node_wrappers/anchor.js +56 -0
  49. data/web/javascripts/_pinstripe/node_wrappers/document.js +27 -0
  50. data/web/javascripts/_pinstripe/node_wrappers/form.js +52 -0
  51. data/web/javascripts/_pinstripe/node_wrappers/frame.js +72 -0
  52. data/web/javascripts/_pinstripe/node_wrappers/index.js +8 -0
  53. data/web/javascripts/_pinstripe/node_wrappers/input.js +23 -0
  54. data/web/javascripts/_pinstripe/node_wrappers/modal.js +36 -0
  55. data/web/javascripts/_pinstripe/node_wrappers/script.js +17 -0
  56. data/web/javascripts/_pinstripe/string_reader.js +24 -0
  57. data/web/javascripts/_pinstripe/url.js +94 -0
  58. data/web/javascripts/_pinstripe/util/benchmark.js +10 -0
  59. data/web/javascripts/_pinstripe/util/capitalize.js +4 -0
  60. data/web/javascripts/_pinstripe/util/index.js +4 -0
  61. data/web/javascripts/_pinstripe/util/unescape_html.js +13 -0
  62. data/web/javascripts/_pinstripe/virtual_node.js +156 -0
  63. data/yarn.lock +72 -0
  64. metadata +162 -0
checksums.yaml ADDED
@@ -0,0 +1,7 @@
1
+ ---
2
+ SHA256:
3
+ metadata.gz: f2cb947e0311ba670442c2bc3863299bf54c9d918420b44b1ebc52f8fe58b5d9
4
+ data.tar.gz: 50acc4e1919747618d584a0866e5226d58493d3e512fbd10828687c24213b884
5
+ SHA512:
6
+ metadata.gz: fed3bc212e47bc4a7eab9ffc39f4474d15511edb44216da14196801615b059be9e6993edb67e90f593c1b5b9e8610487b63ee996312460132feeee3673c26e9f
7
+ data.tar.gz: e84b52f0d8d53235a21452eda448c87639b9b22d38898a971ca94fccd251e09771e53faefbb4611feb642bb430e29c4052ac5a6fea5a1de01741852da29e5c98
data/.gitignore ADDED
@@ -0,0 +1,5 @@
1
+
2
+
3
+ node_modules/
4
+ pkg/
5
+ web/javascripts/bundle.*
data/.pinstripe ADDED
@@ -0,0 +1,3 @@
1
+ {
2
+ "name" => "pinstripe"
3
+ }
data/Gemfile ADDED
@@ -0,0 +1,4 @@
1
+ source "https://rubygems.org"
2
+
3
+ # Specify your gem's dependencies in pinstripe.gemspec
4
+ gemspec
data/Gemfile.lock ADDED
@@ -0,0 +1,24 @@
1
+ PATH
2
+ remote: .
3
+ specs:
4
+ pinstripe (0.1.2)
5
+
6
+ GEM
7
+ remote: https://rubygems.org/
8
+ specs:
9
+ minitest (5.14.0)
10
+ mysql2 (0.5.3)
11
+ rake (13.0.1)
12
+
13
+ PLATFORMS
14
+ ruby
15
+
16
+ DEPENDENCIES
17
+ bundler (~> 2.0)
18
+ minitest (~> 5.0)
19
+ mysql2 (~> 0.5)
20
+ pinstripe!
21
+ rake (~> 13.0)
22
+
23
+ BUNDLED WITH
24
+ 2.0.2
data/LICENSE ADDED
@@ -0,0 +1,20 @@
1
+
2
+ Copyright (c) 2020 Jody Salt
3
+
4
+ Permission is hereby granted, free of charge, to any person obtaining a copy
5
+ of this software and associated documentation files (the "Software"), to deal
6
+ in the Software without restriction, including without limitation the rights
7
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
8
+ copies of the Software, and to permit persons to whom the Software is
9
+ furnished to do so, subject to the following conditions:
10
+
11
+ The above copyright notice and this permission notice shall be included in all
12
+ copies or substantial portions of the Software.
13
+
14
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
15
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
16
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
17
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
18
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
19
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
20
+ SOFTWARE.
data/README.md ADDED
@@ -0,0 +1,5 @@
1
+
2
+ # Pinstripe Suit
3
+
4
+ Welcome to Pinstripe. An entrepreneurial full stack web framework!
5
+
data/Rakefile ADDED
@@ -0,0 +1,10 @@
1
+ require "bundler/gem_tasks"
2
+ require "rake/testtask"
3
+
4
+ Rake::TestTask.new(:test) do |t|
5
+ t.libs << "test"
6
+ t.libs << "lib"
7
+ t.test_files = FileList["test/**/*_test.rb"]
8
+ end
9
+
10
+ task :default => :test
data/exe/pinstripe ADDED
@@ -0,0 +1,4 @@
1
+
2
+ require("pinstripe")
3
+
4
+ Pinstripe::Command.run(*ARGV)
@@ -0,0 +1,12 @@
1
+
2
+ require "pinstripe/helpers"
3
+
4
+ class Pinstripe::CallHandler
5
+
6
+ include Pinstripe::Helpers
7
+
8
+ def handle_call
9
+ [200, {"Content-Type" => "text/plain"}, ["Hello World!"]]
10
+ end
11
+
12
+ end
@@ -0,0 +1,20 @@
1
+
2
+ require "pinstripe/registry"
3
+
4
+ class Pinstripe::Command
5
+
6
+ extend Pinstripe::Registry
7
+
8
+ def initialize(*args)
9
+
10
+ end
11
+
12
+ def self.run(name = 'list-commands', *args)
13
+ create(name, *args).run
14
+ end
15
+
16
+ def run
17
+ puts "No such command \"#{self.class.name}\""
18
+ end
19
+
20
+ end
@@ -0,0 +1,16 @@
1
+
2
+ require "pinstripe/command"
3
+
4
+ Pinstripe::Command.define "list-commands" do
5
+
6
+ def run
7
+ puts ""
8
+ puts "The following commands are available:"
9
+ puts ""
10
+ Pinstripe::Command.registered_classes.keys.sort.each do |name|
11
+ puts " * #{name}"
12
+ end
13
+ puts ""
14
+ end
15
+
16
+ end
@@ -0,0 +1,14 @@
1
+
2
+ require "pinstripe/command"
3
+
4
+ Pinstripe::Command.define "start-console" do
5
+
6
+ def run
7
+ require "irb"
8
+ IRB.setup nil
9
+ IRB.conf[:MAIN_CONTEXT] = IRB::Irb.new.context
10
+ require "irb/ext/multi-irb"
11
+ IRB.irb nil, Pinstripe::Workspace.new
12
+ end
13
+
14
+ end
@@ -0,0 +1,19 @@
1
+
2
+ module Pinstripe::Concern
3
+
4
+ def missing_method_calls
5
+ @missing_method_calls ||= []
6
+ end
7
+
8
+ def included(_module)
9
+ missing_method_calls.each do |missing_method_call|
10
+ args, block = missing_method_call
11
+ _module.send *args, &block
12
+ end
13
+ end
14
+
15
+ def method_missing(*args, &block)
16
+ missing_method_calls << [args, block]
17
+ end
18
+
19
+ end
@@ -0,0 +1,48 @@
1
+
2
+ class Pinstripe::Database::Row
3
+
4
+ extend Pinstripe::Registry
5
+
6
+ class << self
7
+
8
+ attr_accessor :table_class
9
+
10
+ def register(name, options = {})
11
+ if options[:abstract]
12
+ union_class = Pinstripe::Database::Union.for(name.pluralize, :register_if_not_exists => true)
13
+ union_class.table_classes << table_class if !union_class.table_classes.include?(table_class)
14
+ else
15
+ super(name)
16
+ @table_class = Pinstripe::Database::Table.for(name.pluralize, :register_if_not_exists => true)
17
+ field "id", "type" => "integer"
18
+ end
19
+ end
20
+
21
+ def has_many(name, *args, &block)
22
+ table_class.has_many(name, *args, &block)
23
+ class_eval("def #{name}; self.class.table_class.new(@database).id_eq(id).#{name}; end")
24
+ end
25
+
26
+ def has_one(name, *args, &block)
27
+ table_class.has_one(name, *args, &block)
28
+ class_eval("def #{name}; self.class.table_class.new(@database).id_eq(id).#{name}.first; end")
29
+ end
30
+
31
+ def belongs_to(name, *args, &block)
32
+ table_class.belongs_to(name, *args, &block)
33
+ class_eval("def #{name}; self.class.table_class.new(@database).id_eq(id).#{name}.first; end")
34
+ end
35
+
36
+ def field(*args, &block)
37
+ table_class.column(*args, &block)
38
+ end
39
+
40
+ end
41
+
42
+ def initialize(database, data = {})
43
+ @database = database
44
+ @data = data
45
+ @altered_data = {}
46
+ end
47
+
48
+ end
@@ -0,0 +1,131 @@
1
+
2
+ require "pinstripe/registry"
3
+
4
+ class Pinstripe::Database::Table
5
+
6
+ extend Pinstripe::Registry
7
+
8
+ class << self
9
+
10
+ def relationships
11
+ @relationships ||= {}.to_params_hash
12
+ end
13
+
14
+ def has_many(name, options = {})
15
+ relationships[name] = {
16
+ "name" => name.to_s,
17
+ "collection_name" => name.to_s,
18
+ "from_key" => "id",
19
+ "to_key" => "#{self.name.singularize}_id",
20
+ "cascade_delete" => "true"
21
+ }
22
+ relationships[name].merge!(options)
23
+ class_eval("def #{name}; join('#{name}'); end")
24
+ end
25
+
26
+ def has_one(name, options = {})
27
+ relationships[name] = {
28
+ "name" => name.to_s,
29
+ "collection_name" => name.pluralize,
30
+ "from_key" => "id",
31
+ "to_key" => "#{self.name.singularize}_id",
32
+ "cascade_delete" => "true"
33
+ }
34
+ relationships[name].merge!(options)
35
+ class_eval("def #{name}; join('#{name}'); end")
36
+ end
37
+
38
+ def belongs_to(name, options = {})
39
+ relationships[name] = {
40
+ "name" => name.to_s,
41
+ "collection_name" => name.pluralize,
42
+ "from_key" => "#{name}_id",
43
+ "to_key" => "id",
44
+ "cascade_delete" => "false"
45
+ }
46
+ relationships[name].merge!(options)
47
+ class_eval("def #{name}; join('#{name}'); end")
48
+ end
49
+
50
+ def columns
51
+ @columns ||= {}.to_params_hash
52
+ end
53
+
54
+ def column(name, options = {})
55
+ columns[name] = {
56
+ "name" => name.to_s,
57
+ "type" => "string"
58
+ }
59
+ columns[name].merge!(options)
60
+ class_eval("def #{name}_eq(value); where('?.`#{name}` = ?', self, value); end")
61
+ end
62
+
63
+ def scope(name, &block)
64
+ #call block with join parents as arguments
65
+ end
66
+
67
+ end
68
+
69
+ def initialize(database, join_parent = nil, join_relationship_name = nil, table_alias_manager = Pinstripe::Database::TableAliasManager.new)
70
+ @database = database
71
+ @join_parent = join_parent
72
+ @join_relationship_name = join_relationship_name
73
+ @table_alias_manager = table_alias_manager
74
+ @joins = []
75
+ @predicates = []
76
+ @alias = table_alias_manager.generate_alias(self.class.name)
77
+ end
78
+
79
+ def clone
80
+ out = super
81
+ out.instance_eval do
82
+ @joins = @joins.clone
83
+ @predicates = @predicates.clone
84
+ end
85
+ out
86
+ end
87
+
88
+ def back(count = 1)
89
+ current = self
90
+ count.times do
91
+ previous = current
92
+ current = current.instance_eval{ @join_parent }.clone
93
+ current.instance_eval{ @joins << previous }
94
+ end
95
+ current
96
+ end
97
+
98
+ def join(relationship_name)
99
+ collection_name = self.class.relationships[relationship_name].collection_name
100
+ Pinstripe::Database::Table.create(collection_name, @database, self, relationship_name, @table_alias_manager)
101
+ end
102
+
103
+ def where(*predicate)
104
+ out = clone
105
+ out.instance_eval{ @predicates << predicate.flatten }
106
+ out
107
+ end
108
+
109
+ def first
110
+
111
+ end
112
+
113
+ def each
114
+
115
+ end
116
+
117
+ def all
118
+
119
+ end
120
+
121
+ def generate_predicate_sql
122
+ out = []
123
+
124
+ out.join(' and ')
125
+ end
126
+
127
+ def generate_select_sql
128
+
129
+ end
130
+
131
+ end
@@ -0,0 +1,20 @@
1
+
2
+ class Pinstripe::Database::TableAliasManager
3
+
4
+ def initialize
5
+ @alias_counters = {}
6
+ end
7
+
8
+ def generate_alias(name)
9
+ name = name.to_s
10
+ @alias_counters[name] = 0 if @alias_counters[name].nil?
11
+ @alias_counters[name] += 1
12
+
13
+ if @alias_counters[name] == 1
14
+ name
15
+ else
16
+ "#{name}#{@alias_counters[name]}"
17
+ end
18
+ end
19
+
20
+ end
@@ -0,0 +1,20 @@
1
+
2
+ require "pinstripe/registry"
3
+
4
+ class Pinstripe::Database::Union
5
+
6
+ extend Pinstripe::Registry
7
+
8
+ class << self
9
+
10
+ def table_classes
11
+ @table_classes ||= []
12
+ end
13
+
14
+ end
15
+
16
+ def initialize(database)
17
+ @database = database
18
+ end
19
+
20
+ end
@@ -0,0 +1,56 @@
1
+
2
+ require "mysql2"
3
+
4
+ class Pinstripe::Database
5
+
6
+ attr_reader :config
7
+
8
+ def initialize(config = {})
9
+ @config = config.to_params_hash
10
+ end
11
+
12
+ def client
13
+ @client ||= Mysql2::Client.new({
14
+ :host => @config.host || '127.0.0.1',
15
+ :port => @config.port || 3306,
16
+ :username => @config.username || 'root',
17
+ :password => @config.password || '',
18
+ :database => @config.name
19
+ })
20
+ end
21
+
22
+ def query(*query)
23
+ out = []
24
+ query = query.flatten
25
+ while(query.length > 0)
26
+ sql_fragment = query.shift.to_s
27
+ sql_fragment.gsub! /\?/ do |match|
28
+ value = query.shift
29
+ if value.kind_of?(Pinstripe::Database::Table)
30
+ value.instance_eval{ @alias }
31
+ else
32
+ sanitize(value)
33
+ end
34
+ end
35
+ out << sql_fragment
36
+ end
37
+ out.join(' ')
38
+ end
39
+
40
+ def sanitize(value)
41
+ value.kind_of?(Numeric) ? value : "'#{client.escape(value)}'"
42
+ end
43
+
44
+ def method_missing(name, *args, &block)
45
+ if args.length == 0
46
+ if Union.registered_classes[name]
47
+ Union.create(name, self)
48
+ else
49
+ Table.create(name, self)
50
+ end
51
+ else
52
+ super
53
+ end
54
+ end
55
+
56
+ end
@@ -0,0 +1,20 @@
1
+
2
+ require "pinstripe/registry"
3
+ require "pinstripe/helpers"
4
+
5
+ class Pinstripe::Helper
6
+
7
+ extend Pinstripe::Registry
8
+
9
+ include Pinstripe::Helpers
10
+
11
+ def self.register(name)
12
+ super
13
+ Pinstripe::Helpers.class_eval("def #{name}(*args, &block); ::Pinstripe::Helper.create(\"#{name}\").call(*args, &block); end")
14
+ end
15
+
16
+ def call(*args, &block)
17
+
18
+ end
19
+
20
+ end
@@ -0,0 +1,8 @@
1
+
2
+ module Pinstripe::Helpers
3
+
4
+ def database
5
+ @database ||= Pinstripe::Database.new
6
+ end
7
+
8
+ end
@@ -0,0 +1,122 @@
1
+
2
+ module Pinstripe::Inflector::Inflections
3
+
4
+ instance_eval do
5
+ @pluralize_rules = []
6
+ @singularize_rules = []
7
+ end
8
+
9
+ class << self
10
+
11
+ attr_accessor :pluralize_rules
12
+ attr_accessor :singularize_rules
13
+
14
+ def plural(*args)
15
+ pluralize_rules.prepend(args)
16
+ end
17
+
18
+ def singular(*args)
19
+ singularize_rules.prepend(args)
20
+ end
21
+
22
+ def irregular(singular, plural)
23
+ s0 = singular[0]
24
+ srest = singular[1..-1]
25
+
26
+ p0 = plural[0]
27
+ prest = plural[1..-1]
28
+
29
+ if s0.upcase == p0.upcase
30
+ plural(/(#{s0})#{srest}$/i, '\1' + prest)
31
+ plural(/(#{p0})#{prest}$/i, '\1' + prest)
32
+
33
+ singular(/(#{s0})#{srest}$/i, '\1' + srest)
34
+ singular(/(#{p0})#{prest}$/i, '\1' + srest)
35
+ else
36
+ plural(/#{s0.upcase}(?i)#{srest}$/, p0.upcase + prest)
37
+ plural(/#{s0.downcase}(?i)#{srest}$/, p0.downcase + prest)
38
+ plural(/#{p0.upcase}(?i)#{prest}$/, p0.upcase + prest)
39
+ plural(/#{p0.downcase}(?i)#{prest}$/, p0.downcase + prest)
40
+
41
+ singular(/#{s0.upcase}(?i)#{srest}$/, s0.upcase + srest)
42
+ singular(/#{s0.downcase}(?i)#{srest}$/, s0.downcase + srest)
43
+ singular(/#{p0.upcase}(?i)#{prest}$/, s0.upcase + srest)
44
+ singular(/#{p0.downcase}(?i)#{prest}$/, s0.downcase + srest)
45
+ end
46
+ end
47
+
48
+ def uncountable(singular_and_plural)
49
+ irregular(singular_and_plural, singular_and_plural)
50
+ end
51
+
52
+ end
53
+
54
+ plural(/$/, "s")
55
+ plural(/s$/i, "s")
56
+ plural(/^(ax|test)is$/i, '\1es')
57
+ plural(/(octop|vir)us$/i, '\1i')
58
+ plural(/(octop|vir)i$/i, '\1i')
59
+ plural(/(alias|status)$/i, '\1es')
60
+ plural(/(bu)s$/i, '\1ses')
61
+ plural(/(buffal|tomat)o$/i, '\1oes')
62
+ plural(/([ti])um$/i, '\1a')
63
+ plural(/([ti])a$/i, '\1a')
64
+ plural(/sis$/i, "ses")
65
+ plural(/(?:([^f])fe|([lr])f)$/i, '\1\2ves')
66
+ plural(/(hive)$/i, '\1s')
67
+ plural(/([^aeiouy]|qu)y$/i, '\1ies')
68
+ plural(/(x|ch|ss|sh)$/i, '\1es')
69
+ plural(/(matr|vert|ind)(?:ix|ex)$/i, '\1ices')
70
+ plural(/^(m|l)ouse$/i, '\1ice')
71
+ plural(/^(m|l)ice$/i, '\1ice')
72
+ plural(/^(ox)$/i, '\1en')
73
+ plural(/^(oxen)$/i, '\1')
74
+ plural(/(quiz)$/i, '\1zes')
75
+
76
+ singular(/s$/i, "")
77
+ singular(/(ss)$/i, '\1')
78
+ singular(/(n)ews$/i, '\1ews')
79
+ singular(/([ti])a$/i, '\1um')
80
+ singular(/((a)naly|(b)a|(d)iagno|(p)arenthe|(p)rogno|(s)ynop|(t)he)(sis|ses)$/i, '\1sis')
81
+ singular(/(^analy)(sis|ses)$/i, '\1sis')
82
+ singular(/([^f])ves$/i, '\1fe')
83
+ singular(/(hive)s$/i, '\1')
84
+ singular(/(tive)s$/i, '\1')
85
+ singular(/([lr])ves$/i, '\1f')
86
+ singular(/([^aeiouy]|qu)ies$/i, '\1y')
87
+ singular(/(s)eries$/i, '\1eries')
88
+ singular(/(m)ovies$/i, '\1ovie')
89
+ singular(/(x|ch|ss|sh)es$/i, '\1')
90
+ singular(/^(m|l)ice$/i, '\1ouse')
91
+ singular(/(bus)(es)?$/i, '\1')
92
+ singular(/(o)es$/i, '\1')
93
+ singular(/(shoe)s$/i, '\1')
94
+ singular(/(cris|test)(is|es)$/i, '\1is')
95
+ singular(/^(a)x[ie]s$/i, '\1xis')
96
+ singular(/(octop|vir)(us|i)$/i, '\1us')
97
+ singular(/(alias|status)(es)?$/i, '\1')
98
+ singular(/^(ox)en/i, '\1')
99
+ singular(/(vert|ind)ices$/i, '\1ex')
100
+ singular(/(matr)ices$/i, '\1ix')
101
+ singular(/(quiz)zes$/i, '\1')
102
+ singular(/(database)s$/i, '\1')
103
+
104
+ irregular("person", "people")
105
+ irregular("man", "men")
106
+ irregular("child", "children")
107
+ irregular("sex", "sexes")
108
+ irregular("move", "moves")
109
+ irregular("zombie", "zombies")
110
+
111
+ uncountable "equipment"
112
+ uncountable "information"
113
+ uncountable "rice"
114
+ uncountable "money"
115
+ uncountable "species"
116
+ uncountable "series"
117
+ uncountable "fish"
118
+ uncountable "sheep"
119
+ uncountable "jeans"
120
+ uncountable "police"
121
+
122
+ end
@@ -0,0 +1,28 @@
1
+
2
+
3
+ module Pinstripe::Inflector
4
+
5
+ require "pinstripe/inflector/inflections"
6
+
7
+ class << self
8
+
9
+ def pluralize(word)
10
+ word = word.to_s
11
+ Inflections.pluralize_rules.each do |rule|
12
+ pattern, replacement = rule
13
+ return word.sub(pattern, replacement) if word.match?(pattern)
14
+ end
15
+ end
16
+
17
+ def singularize(word)
18
+ word = word.to_s
19
+ Inflections.singularize_rules.each do |rule|
20
+ pattern, replacement = rule
21
+ return word.sub(pattern, replacement) if word.match?(pattern)
22
+ end
23
+ end
24
+
25
+ end
26
+
27
+
28
+ end
@@ -0,0 +1,8 @@
1
+
2
+ class Object
3
+
4
+ def pluralize
5
+ Pinstripe::Inflector.pluralize(to_s)
6
+ end
7
+
8
+ end
@@ -0,0 +1,18 @@
1
+
2
+ module Kernel
3
+
4
+ def require_all(path)
5
+ $LOAD_PATH.each do |current_load_path|
6
+ candidate_path = "#{current_load_path}/#{path}"
7
+ require path if File.exists?("#{candidate_path}.rb") || File.exists?("#{candidate_path}.ruby")
8
+
9
+ if Dir.exists?(candidate_path)
10
+ Dir.open(candidate_path).each do |item|
11
+ item = item.sub(/(\.[^\.]+)$/, '')
12
+ require_all("#{path}/#{item}") if item != '..' && item != '.'
13
+ end
14
+ end
15
+ end
16
+ end
17
+
18
+ end
@@ -0,0 +1,8 @@
1
+
2
+ class Object
3
+
4
+ def singularize
5
+ Pinstripe::Inflector.singularize(to_s)
6
+ end
7
+
8
+ end