pinstripe 0.1.2

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