orange-core 0.5.3
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.
- data/README.markdown +145 -0
- data/lib/orange-core.rb +8 -0
- data/lib/orange-core/application.rb +132 -0
- data/lib/orange-core/assets/css/exceptions.css +50 -0
- data/lib/orange-core/assets/js/exceptions.js +44 -0
- data/lib/orange-core/carton.rb +178 -0
- data/lib/orange-core/core.rb +266 -0
- data/lib/orange-core/magick.rb +270 -0
- data/lib/orange-core/middleware/base.rb +96 -0
- data/lib/orange-core/middleware/database.rb +45 -0
- data/lib/orange-core/middleware/four_oh_four.rb +45 -0
- data/lib/orange-core/middleware/globals.rb +17 -0
- data/lib/orange-core/middleware/loader.rb +13 -0
- data/lib/orange-core/middleware/rerouter.rb +53 -0
- data/lib/orange-core/middleware/restful_router.rb +99 -0
- data/lib/orange-core/middleware/route_context.rb +39 -0
- data/lib/orange-core/middleware/route_site.rb +51 -0
- data/lib/orange-core/middleware/show_exceptions.rb +80 -0
- data/lib/orange-core/middleware/static.rb +67 -0
- data/lib/orange-core/middleware/static_file.rb +32 -0
- data/lib/orange-core/middleware/template.rb +60 -0
- data/lib/orange-core/packet.rb +232 -0
- data/lib/orange-core/plugin.rb +172 -0
- data/lib/orange-core/resource.rb +96 -0
- data/lib/orange-core/resources/mapper.rb +36 -0
- data/lib/orange-core/resources/model_resource.rb +228 -0
- data/lib/orange-core/resources/not_found.rb +10 -0
- data/lib/orange-core/resources/page_parts.rb +68 -0
- data/lib/orange-core/resources/parser.rb +113 -0
- data/lib/orange-core/resources/routable_resource.rb +16 -0
- data/lib/orange-core/resources/scaffold.rb +106 -0
- data/lib/orange-core/stack.rb +226 -0
- data/lib/orange-core/templates/exceptions.haml +111 -0
- data/lib/orange-core/views/default_resource/create.haml +4 -0
- data/lib/orange-core/views/default_resource/edit.haml +9 -0
- data/lib/orange-core/views/default_resource/list.haml +10 -0
- data/lib/orange-core/views/default_resource/show.haml +4 -0
- data/lib/orange-core/views/default_resource/table_row.haml +7 -0
- data/lib/orange-core/views/not_found/404.haml +2 -0
- data/spec/orange-core/application_spec.rb +183 -0
- data/spec/orange-core/carton_spec.rb +136 -0
- data/spec/orange-core/core_spec.rb +248 -0
- data/spec/orange-core/magick_spec.rb +96 -0
- data/spec/orange-core/middleware/base_spec.rb +38 -0
- data/spec/orange-core/middleware/globals_spec.rb +3 -0
- data/spec/orange-core/middleware/rerouter_spec.rb +3 -0
- data/spec/orange-core/middleware/restful_router_spec.rb +3 -0
- data/spec/orange-core/middleware/route_context_spec.rb +3 -0
- data/spec/orange-core/middleware/route_site_spec.rb +3 -0
- data/spec/orange-core/middleware/show_exceptions_spec.rb +3 -0
- data/spec/orange-core/middleware/static_file_spec.rb +3 -0
- data/spec/orange-core/middleware/static_spec.rb +3 -0
- data/spec/orange-core/mock/mock_app.rb +16 -0
- data/spec/orange-core/mock/mock_carton.rb +43 -0
- data/spec/orange-core/mock/mock_core.rb +2 -0
- data/spec/orange-core/mock/mock_middleware.rb +25 -0
- data/spec/orange-core/mock/mock_mixins.rb +19 -0
- data/spec/orange-core/mock/mock_model_resource.rb +47 -0
- data/spec/orange-core/mock/mock_pulp.rb +24 -0
- data/spec/orange-core/mock/mock_resource.rb +26 -0
- data/spec/orange-core/mock/mock_router.rb +10 -0
- data/spec/orange-core/orange_spec.rb +19 -0
- data/spec/orange-core/packet_spec.rb +203 -0
- data/spec/orange-core/resource_spec.rb +96 -0
- data/spec/orange-core/resources/mapper_spec.rb +5 -0
- data/spec/orange-core/resources/model_resource_spec.rb +246 -0
- data/spec/orange-core/resources/parser_spec.rb +5 -0
- data/spec/orange-core/resources/routable_resource_spec.rb +5 -0
- data/spec/orange-core/spec_helper.rb +53 -0
- data/spec/orange-core/stack_spec.rb +232 -0
- data/spec/stats.rb +182 -0
- metadata +227 -0
@@ -0,0 +1,266 @@
|
|
1
|
+
require 'dm-core'
|
2
|
+
require 'rack'
|
3
|
+
require 'rack/builder'
|
4
|
+
|
5
|
+
module Orange
|
6
|
+
# Declare submodules for later use
|
7
|
+
module Pulp; end
|
8
|
+
module Mixins; end
|
9
|
+
module Plugins; end
|
10
|
+
|
11
|
+
attr_accessor :plugins
|
12
|
+
|
13
|
+
# Support for plugins
|
14
|
+
def self.plugins
|
15
|
+
@plugins ||= []
|
16
|
+
end
|
17
|
+
|
18
|
+
# Allows adding plugins
|
19
|
+
def self.plugin(plugin)
|
20
|
+
self.plugins << plugin if plugin.kind_of?(Orange::Plugins::Base)
|
21
|
+
end
|
22
|
+
|
23
|
+
# Allow mixins directly from Orange
|
24
|
+
def self.mixin(inc)
|
25
|
+
Core.mixin inc
|
26
|
+
end
|
27
|
+
|
28
|
+
# Allow pulp directly from Orange
|
29
|
+
def self.add_pulp(inc)
|
30
|
+
Packet.mixin inc
|
31
|
+
end
|
32
|
+
|
33
|
+
# Core is one of two main sources of interaction for Orange Applications
|
34
|
+
#
|
35
|
+
# All portions of Orange based code have access to the Core upon
|
36
|
+
# initialization. Orange allows access to individual resources,
|
37
|
+
# and also allows single point for event registration and firing.
|
38
|
+
#
|
39
|
+
# Functionality of the core can be extended by loading resources,
|
40
|
+
# or by mixins that directly affect the Core. Generally, resources
|
41
|
+
# are the less convoluted (easier to debug) way to do it.
|
42
|
+
class Core
|
43
|
+
# Sets the default options for Orange Applications
|
44
|
+
DEFAULT_CORE_OPTIONS =
|
45
|
+
{
|
46
|
+
:contexts => [:live, :admin, :orange],
|
47
|
+
:default_context => :live,
|
48
|
+
:default_resource => :not_found,
|
49
|
+
:default_database => 'sqlite3::memory:'
|
50
|
+
} unless defined?(DEFAULT_CORE_OPTIONS)
|
51
|
+
|
52
|
+
# Args will be set to the @options array.
|
53
|
+
# Block DSL style option setting also available:
|
54
|
+
#
|
55
|
+
# orange = Orange::Core.new(:optional_option => 'foo') do
|
56
|
+
# haml true
|
57
|
+
# site_name "Banana"
|
58
|
+
# custom_router MyRouterClass.new
|
59
|
+
# end
|
60
|
+
#
|
61
|
+
# orange.options[:site_name] #=> "Banana"
|
62
|
+
#
|
63
|
+
# This method calls afterLoad when it is done. Subclasses can override
|
64
|
+
# the afterLoad method for initialization needs.
|
65
|
+
def initialize(*args, &block)
|
66
|
+
@options = Options.new(*args, &block).hash.with_defaults(DEFAULT_CORE_OPTIONS)
|
67
|
+
@resources = {}
|
68
|
+
@application = false
|
69
|
+
@stack = false
|
70
|
+
@middleware = []
|
71
|
+
@events = {}
|
72
|
+
@file = __FILE__
|
73
|
+
load(Orange::Parser.new, :parser)
|
74
|
+
load(Orange::Mapper.new, :mapper)
|
75
|
+
load(Orange::Scaffold.new, :scaffold)
|
76
|
+
load(Orange::PageParts.new, :page_parts)
|
77
|
+
Orange.plugins.each{|p| p.resources.each{|args| load(*args)} if p.has_resources?}
|
78
|
+
self.register(:stack_loaded) do |s|
|
79
|
+
@middleware.each{|m| m.stack_init if m.respond_to?(:stack_init)}
|
80
|
+
@application.stack_init if @application
|
81
|
+
end
|
82
|
+
self.register(:stack_reloading){|s| @middleware = []} # Dump middleware on stack reload
|
83
|
+
# load(Orange::AdminResource.new, :admin)
|
84
|
+
afterLoad
|
85
|
+
self
|
86
|
+
end
|
87
|
+
|
88
|
+
# Returns the orange library directory
|
89
|
+
# @return [String] the directory name indicating where the core file is
|
90
|
+
# located
|
91
|
+
def core_dir(*args)
|
92
|
+
if args
|
93
|
+
File.join((options[:core_dir] ||= File.dirname(__FILE__)), *args)
|
94
|
+
else
|
95
|
+
options[:core_dir] ||= File.dirname(__FILE__)
|
96
|
+
end
|
97
|
+
end
|
98
|
+
|
99
|
+
# Returns the directory of the currently executing file (using Dir.pwd),
|
100
|
+
# can be overriden using the option :app_dir in initialization
|
101
|
+
#
|
102
|
+
# @return [String] the directory name of the currently running application
|
103
|
+
def app_dir(*args)
|
104
|
+
if args
|
105
|
+
File.join((options[:app_dir] ||= Dir.pwd), *args)
|
106
|
+
else
|
107
|
+
options[:app_dir] ||= Dir.pwd
|
108
|
+
end
|
109
|
+
end
|
110
|
+
|
111
|
+
# Called by initialize after finished loading
|
112
|
+
def afterLoad
|
113
|
+
true
|
114
|
+
end
|
115
|
+
|
116
|
+
# Returns status of a given resource by short name
|
117
|
+
# @param [Symbol] resource_name The short name of the resource
|
118
|
+
# @return [Boolean] result of has_key? in the resources list
|
119
|
+
def loaded?(resource_name)
|
120
|
+
@resources.has_key?(resource_name)
|
121
|
+
end
|
122
|
+
|
123
|
+
# Takes an instance of a Orange::Resource subclass, sets orange
|
124
|
+
# then adds it to the orange resources
|
125
|
+
#
|
126
|
+
# It can be assigned a short name to be used for accessing later
|
127
|
+
# on. If no short name is assigned, one will be generated by downcasing
|
128
|
+
# the class name and changing it to a symbol
|
129
|
+
#
|
130
|
+
# Resources must respond to set_orange, which is automatically used to
|
131
|
+
# create a link back to the Core, and to notify the resource of its assigned
|
132
|
+
# short name.
|
133
|
+
#
|
134
|
+
# @param [Orange::Resource] resource An instance of Orange::Resource subclass
|
135
|
+
# @param [optional, Symbol, String] name A short name to assign as key in Hash
|
136
|
+
# list of resources.
|
137
|
+
# Doesn't necessarily need to be a symbol, but generally is.
|
138
|
+
# Set to the class name lowercase as a symbol by default.
|
139
|
+
def load(resource, name = false)
|
140
|
+
name = resource.orange_name if(!name)
|
141
|
+
name = resource.class.to_s.gsub(/::/, '_').downcase.to_sym if(!name)
|
142
|
+
@resources[name] = resource.set_orange(self, name)
|
143
|
+
end
|
144
|
+
|
145
|
+
# Takes an instance of Orange::Middleware::Base subclass and
|
146
|
+
# keeps it for later. This way we can provide introspection into the
|
147
|
+
# middleware instances (useful for calling stack_init on them)
|
148
|
+
def middleware(middle = false)
|
149
|
+
@middleware << middle if middle
|
150
|
+
@middleware
|
151
|
+
end
|
152
|
+
|
153
|
+
# Takes an instance of Orange::Application and saves it.
|
154
|
+
def application(app = false)
|
155
|
+
@application = app if app
|
156
|
+
@application
|
157
|
+
end
|
158
|
+
|
159
|
+
# Takes an instance of Orange::Stack and saves it.
|
160
|
+
def stack(new_stack = false)
|
161
|
+
@stack = new_stack if new_stack
|
162
|
+
@stack
|
163
|
+
end
|
164
|
+
|
165
|
+
# Takes an instance of Orange::Stack and saves it.
|
166
|
+
def stack=(new_stack)
|
167
|
+
@stack = new_stack
|
168
|
+
end
|
169
|
+
|
170
|
+
# Convenience self for consistent naming across middleware
|
171
|
+
# @return [Orange::Core] self
|
172
|
+
def orange; self; end
|
173
|
+
|
174
|
+
# Registers interest in a callback for a named event.
|
175
|
+
#
|
176
|
+
# Event registration is stored as a hash list of events and arrays
|
177
|
+
# of procs to be executed on each event.
|
178
|
+
#
|
179
|
+
# @param [Symbol] event the name of the event registered for
|
180
|
+
# @param [optional, Integer] position the position to place the event in,
|
181
|
+
# by default goes to the front of the list. Doesn't necessarily need
|
182
|
+
# to be exact count, empty spaces in array are taken out. Forcing the
|
183
|
+
# event to be at 99 or some such position will typically make sure it
|
184
|
+
# happens last in the firing process.
|
185
|
+
# @param [Block] block The code to be executed upon event firing.
|
186
|
+
# Saved to an array of procs that are called when #fire is called.
|
187
|
+
# Block must accept one param, which is the intended to be the packet
|
188
|
+
# causing the block to fire, unless the event happens in setup.
|
189
|
+
def register(event, position = 0, &block)
|
190
|
+
if block_given?
|
191
|
+
if @events[event]
|
192
|
+
@events[event].insert(position, Proc.new)
|
193
|
+
else
|
194
|
+
@events[event] = Array.new.insert(position, Proc.new)
|
195
|
+
end
|
196
|
+
end
|
197
|
+
end
|
198
|
+
|
199
|
+
# Fires a callback for a given packet (or other object)
|
200
|
+
#
|
201
|
+
# @param [Symbol] event name of event something has registered for
|
202
|
+
# @param [Orange::Packet, object] packet Object, generally Orange::Packet,
|
203
|
+
# causing the fire. This is passed to each Proc registered.
|
204
|
+
# @return [Boolean] returns false if nothing has been registered for the
|
205
|
+
# event, otherwise true.
|
206
|
+
def fire(event, packet, *args)
|
207
|
+
return false unless @events[event]
|
208
|
+
@events[event].compact!
|
209
|
+
for callback in @events[event]
|
210
|
+
callback.call(packet, *args)
|
211
|
+
end
|
212
|
+
true
|
213
|
+
end
|
214
|
+
|
215
|
+
# Returns options of the orange core
|
216
|
+
#
|
217
|
+
# @return [Hash] Hash of options
|
218
|
+
def options
|
219
|
+
@options
|
220
|
+
end
|
221
|
+
|
222
|
+
|
223
|
+
# Accesses resources array, stored as a hash {:short_name => Resource instance,...}
|
224
|
+
#
|
225
|
+
# @param [Symbol] name the short name for the requested resource
|
226
|
+
# @param [optional, Boolean] ignore Whether to ignore any calls to resource if not found
|
227
|
+
# (false is default). This will allow method calls to non-existent resources. Should be
|
228
|
+
# used with caution.
|
229
|
+
# @return [Orange::Resource] the resource for the given short name
|
230
|
+
def [](name, ignore = false)
|
231
|
+
if ignore && !loaded?(name)
|
232
|
+
Ignore.new
|
233
|
+
else
|
234
|
+
@resources[name]
|
235
|
+
end
|
236
|
+
end
|
237
|
+
|
238
|
+
# Includes module in the Packet class
|
239
|
+
# @param [Module] inc module to be included
|
240
|
+
def add_pulp(inc)
|
241
|
+
self.class.add_pulp inc
|
242
|
+
end
|
243
|
+
|
244
|
+
# Includes module in this class
|
245
|
+
# @param [Module] inc module to be included
|
246
|
+
def mixin(inc)
|
247
|
+
self.class.mixin inc
|
248
|
+
end
|
249
|
+
|
250
|
+
# Includes module in this class
|
251
|
+
# @param [Module] inc module to be included
|
252
|
+
def self.mixin(inc)
|
253
|
+
include inc
|
254
|
+
end
|
255
|
+
|
256
|
+
# Includes module in the Packet class
|
257
|
+
# @param [Module] inc module to be included
|
258
|
+
def self.add_pulp(inc)
|
259
|
+
Packet.mixin inc
|
260
|
+
end
|
261
|
+
|
262
|
+
def inspect
|
263
|
+
"#<Orange::Core:0x#{self.object_id.to_s(16)}>"
|
264
|
+
end
|
265
|
+
end
|
266
|
+
end
|
@@ -0,0 +1,270 @@
|
|
1
|
+
# Monkey Patch the extract_options! stolen from ActiveSupport
|
2
|
+
# @private
|
3
|
+
class ::Array #:nodoc:
|
4
|
+
def extract_options!
|
5
|
+
last.is_a?(::Hash) ? pop : {}
|
6
|
+
end
|
7
|
+
def extract_with_defaults(defaults)
|
8
|
+
extract_options!.with_defaults(defaults)
|
9
|
+
end
|
10
|
+
end
|
11
|
+
|
12
|
+
# Monkey Patch for merging defaults into a hash
|
13
|
+
# @private
|
14
|
+
class ::Hash #:nodoc:
|
15
|
+
def with_defaults(defaults)
|
16
|
+
self.merge(defaults){ |key, old, new| old.nil? ? new : old }
|
17
|
+
end
|
18
|
+
def with_defaults!(defaults)
|
19
|
+
self.merge!(defaults){ |key, old, new| old.nil? ? new : old }
|
20
|
+
end
|
21
|
+
end
|
22
|
+
|
23
|
+
|
24
|
+
|
25
|
+
# Monkey patch for awesome array -> hash conversions
|
26
|
+
# use:
|
27
|
+
#
|
28
|
+
# [:x, :y, :z].inject_hash do |results, letter|
|
29
|
+
# results[letter] = rand(100)
|
30
|
+
# end
|
31
|
+
#
|
32
|
+
# # => {:x => 32, :y => 63, :z => 91}
|
33
|
+
# @private
|
34
|
+
module Enumerable #:nodoc:
|
35
|
+
def inject_hash(hash = {})
|
36
|
+
inject(hash) {|(h,item)| yield(h,item); h}
|
37
|
+
end
|
38
|
+
end
|
39
|
+
|
40
|
+
# Borrowed from Roman2k:
|
41
|
+
# http://github.com/Roman2K/class-inheritable-attributes/
|
42
|
+
# @private
|
43
|
+
module ClassInheritableAttributes
|
44
|
+
class << self
|
45
|
+
def eval_in_accessor_module(klass, code)
|
46
|
+
mod = klass.instance_eval { @_inheritable_attribute_accessors ||= Module.new }
|
47
|
+
klass.extend(mod)
|
48
|
+
mod.module_eval(code)
|
49
|
+
end
|
50
|
+
|
51
|
+
def fetch_value(klass, attribute)
|
52
|
+
each_parent(klass) do |parent|
|
53
|
+
if values = registry[parent] and values.key?(attribute)
|
54
|
+
return values[attribute]
|
55
|
+
elsif parent.instance_variable_defined?("@#{attribute}")
|
56
|
+
return parent.instance_variable_get("@#{attribute}")
|
57
|
+
end
|
58
|
+
end
|
59
|
+
return nil
|
60
|
+
end
|
61
|
+
|
62
|
+
def store_value(klass, attribute, value)
|
63
|
+
if Thread.current == Thread.main
|
64
|
+
if value.nil?
|
65
|
+
klass.instance_eval do
|
66
|
+
remove_instance_variable("@#{attribute}") if instance_variable_defined?("@#{attribute}")
|
67
|
+
end
|
68
|
+
else
|
69
|
+
klass.instance_variable_set("@#{attribute}", value)
|
70
|
+
end
|
71
|
+
else
|
72
|
+
registry[klass] ||= {}
|
73
|
+
if value.nil?
|
74
|
+
registry[klass].delete(attribute)
|
75
|
+
registry.delete(klass) if registry[klass].empty?
|
76
|
+
else
|
77
|
+
registry[klass][attribute] = value
|
78
|
+
end
|
79
|
+
end
|
80
|
+
end
|
81
|
+
|
82
|
+
private
|
83
|
+
|
84
|
+
def each_parent(klass)
|
85
|
+
while klass < Object
|
86
|
+
yield klass
|
87
|
+
klass = klass.superclass
|
88
|
+
end
|
89
|
+
end
|
90
|
+
|
91
|
+
def registry
|
92
|
+
Thread.current[ClassInheritableAttributes.name] ||= {}
|
93
|
+
end
|
94
|
+
end
|
95
|
+
|
96
|
+
def cattr_reader(*attributes)
|
97
|
+
attributes.each do |attribute|
|
98
|
+
ClassInheritableAttributes.eval_in_accessor_module(self, <<-EOS)
|
99
|
+
def #{attribute}
|
100
|
+
#{ClassInheritableAttributes}.fetch_value(self, :#{attribute})
|
101
|
+
end
|
102
|
+
EOS
|
103
|
+
end
|
104
|
+
end
|
105
|
+
|
106
|
+
def cattr_writer(*attributes)
|
107
|
+
attributes.each do |attribute|
|
108
|
+
ClassInheritableAttributes.eval_in_accessor_module(self, <<-EOS)
|
109
|
+
def #{attribute}=(value)
|
110
|
+
#{ClassInheritableAttributes}.store_value(self, :#{attribute}, value)
|
111
|
+
end
|
112
|
+
EOS
|
113
|
+
end
|
114
|
+
end
|
115
|
+
|
116
|
+
def cattr_accessor(*attributes)
|
117
|
+
cattr_reader(*attributes)
|
118
|
+
cattr_writer(*attributes)
|
119
|
+
end
|
120
|
+
end
|
121
|
+
|
122
|
+
module Orange
|
123
|
+
|
124
|
+
# Class that extends hash so that [] can have an optional second attribute
|
125
|
+
class DefaultHash < ::Hash
|
126
|
+
def [](key, my_default = nil)
|
127
|
+
my_default = self.default if my_default.nil?
|
128
|
+
self.has_key?(key) ? super(key) : my_default
|
129
|
+
end
|
130
|
+
end
|
131
|
+
|
132
|
+
# This class acts as a simple sink for ignoring messages, it will return itself
|
133
|
+
# for any message call. Orange::Core can optionally return this when trying
|
134
|
+
# to access resources so that you can make method calls to a resource that
|
135
|
+
# might not be really there. It will silently swallow any errors that might arrise,
|
136
|
+
# so this should be used with caution.
|
137
|
+
class Ignore
|
138
|
+
def blank?; true; end
|
139
|
+
def method_missing(name, *args, &block)
|
140
|
+
return self
|
141
|
+
end
|
142
|
+
end
|
143
|
+
|
144
|
+
# Simple class for evaluating options and allowing us to access them.
|
145
|
+
class Options
|
146
|
+
|
147
|
+
def initialize(*options, &block)
|
148
|
+
@options = options.extract_options!
|
149
|
+
@options ||= {}
|
150
|
+
instance_eval(&block) if block_given?
|
151
|
+
end
|
152
|
+
|
153
|
+
def hash
|
154
|
+
@options
|
155
|
+
end
|
156
|
+
|
157
|
+
def method_missing(key, *args)
|
158
|
+
return (@options[key.to_s.gsub(/\?$/, '').to_sym].eql?(true)) if key.to_s.match(/\?$/)
|
159
|
+
if args.empty?
|
160
|
+
@options[key.to_sym]
|
161
|
+
elsif(key.to_s.match(/\=$/))
|
162
|
+
@options[key.to_s.gsub(/\=$/, '').to_sym] = (args.size == 1 ? args.first : args)
|
163
|
+
else
|
164
|
+
@options[key.to_sym] = (args.size == 1 ? args.first : args)
|
165
|
+
end
|
166
|
+
end
|
167
|
+
end
|
168
|
+
|
169
|
+
# @private
|
170
|
+
module Inflector
|
171
|
+
extend self
|
172
|
+
# @private
|
173
|
+
def camelize(lower_case_and_underscored_word, first_letter_in_uppercase = true)
|
174
|
+
if first_letter_in_uppercase
|
175
|
+
lower_case_and_underscored_word.to_s.gsub(/\/(.?)/) { "::#{$1.upcase}" }.gsub(/(?:^|_)(.)/) { $1.upcase }
|
176
|
+
else
|
177
|
+
lower_case_and_underscored_word.to_s[0].chr.downcase + camelize(lower_case_and_underscored_word)[1..-1]
|
178
|
+
end
|
179
|
+
end
|
180
|
+
|
181
|
+
if Module.method(:const_get).arity == 1
|
182
|
+
def constantize(camel_cased_word)
|
183
|
+
names = camel_cased_word.split('::')
|
184
|
+
names.shift if names.empty? || names.first.empty?
|
185
|
+
|
186
|
+
constant = Object
|
187
|
+
names.each do |name|
|
188
|
+
constant = constant.const_defined?(name) ? constant.const_get(name) : constant.const_missing(name)
|
189
|
+
end
|
190
|
+
constant
|
191
|
+
end
|
192
|
+
else
|
193
|
+
def constantize(camel_cased_word) #:nodoc:
|
194
|
+
names = camel_cased_word.split('::')
|
195
|
+
names.shift if names.empty? || names.first.empty?
|
196
|
+
|
197
|
+
constant = Object
|
198
|
+
names.each do |name|
|
199
|
+
constant = constant.const_get(name, false) || constant.const_missing(name)
|
200
|
+
end
|
201
|
+
constant
|
202
|
+
end
|
203
|
+
end
|
204
|
+
|
205
|
+
end
|
206
|
+
|
207
|
+
end
|
208
|
+
|
209
|
+
# @private
|
210
|
+
class Object #:nodoc:
|
211
|
+
# An object is blank if it's false, empty, or a whitespace string.
|
212
|
+
# For example, "", " ", +nil+, [], and {} are blank.
|
213
|
+
#
|
214
|
+
# This simplifies
|
215
|
+
#
|
216
|
+
# if !address.nil? && !address.empty?
|
217
|
+
#
|
218
|
+
# to
|
219
|
+
#
|
220
|
+
# if !address.blank?
|
221
|
+
def blank?
|
222
|
+
respond_to?(:empty?) ? empty? : !self
|
223
|
+
end
|
224
|
+
end
|
225
|
+
|
226
|
+
# @private
|
227
|
+
class NilClass #:nodoc:
|
228
|
+
def blank?
|
229
|
+
true
|
230
|
+
end
|
231
|
+
end
|
232
|
+
|
233
|
+
# @private
|
234
|
+
class FalseClass #:nodoc:
|
235
|
+
def blank?
|
236
|
+
true
|
237
|
+
end
|
238
|
+
end
|
239
|
+
|
240
|
+
# @private
|
241
|
+
class TrueClass #:nodoc:
|
242
|
+
def blank?
|
243
|
+
false
|
244
|
+
end
|
245
|
+
end
|
246
|
+
|
247
|
+
# @private
|
248
|
+
class Array #:nodoc:
|
249
|
+
alias_method :blank?, :empty?
|
250
|
+
end
|
251
|
+
|
252
|
+
# @private
|
253
|
+
class Hash #:nodoc:
|
254
|
+
alias_method :blank?, :empty?
|
255
|
+
end
|
256
|
+
|
257
|
+
# @private
|
258
|
+
class String #:nodoc:
|
259
|
+
def blank?
|
260
|
+
self !~ /\S/
|
261
|
+
end
|
262
|
+
end
|
263
|
+
|
264
|
+
# @private
|
265
|
+
class Numeric #:nodoc:
|
266
|
+
def blank?
|
267
|
+
false
|
268
|
+
end
|
269
|
+
end
|
270
|
+
|