flex 0.4.2 → 1.0.1
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/LICENSE +1 -1
- data/README.md +46 -7
- data/VERSION +1 -1
- data/flex.gemspec +13 -19
- data/lib/flex.rb +66 -392
- data/lib/flex/api_stubs.rb +1268 -0
- data/lib/flex/api_templates/cluster_api.yml +94 -0
- data/lib/flex/api_templates/core_api.yml +202 -0
- data/lib/flex/api_templates/indices_api.yml +304 -0
- data/lib/flex/class_proxy/base.rb +20 -6
- data/lib/flex/class_proxy/templates.rb +97 -0
- data/lib/flex/class_proxy/templates/doc.rb +91 -0
- data/lib/flex/class_proxy/templates/search.rb +72 -0
- data/lib/flex/configuration.rb +17 -49
- data/lib/flex/deprecation.rb +153 -0
- data/lib/flex/errors.rb +2 -1
- data/lib/flex/http_clients/base.rb +15 -0
- data/lib/flex/http_clients/loader.rb +51 -0
- data/lib/flex/http_clients/patron.rb +9 -7
- data/lib/flex/http_clients/rest_client.rb +6 -8
- data/lib/flex/logger.rb +24 -3
- data/lib/flex/prog_bar.rb +11 -6
- data/lib/flex/rails.rb +1 -13
- data/lib/flex/result.rb +8 -2
- data/lib/flex/result/document.rb +42 -13
- data/lib/flex/result/multi_get.rb +24 -0
- data/lib/flex/result/search.rb +1 -24
- data/lib/flex/struct/array.rb +25 -0
- data/lib/flex/struct/hash.rb +105 -0
- data/lib/flex/{result/collection.rb → struct/paginable.rb} +16 -9
- data/lib/flex/struct/prunable.rb +60 -0
- data/lib/flex/struct/symbolize.rb +27 -0
- data/lib/flex/tasks.rb +26 -86
- data/lib/flex/template.rb +60 -120
- data/lib/flex/template/common.rb +42 -0
- data/lib/flex/template/logger.rb +66 -0
- data/lib/flex/template/partial.rb +12 -15
- data/lib/flex/template/search.rb +6 -6
- data/lib/flex/template/slim_search.rb +1 -1
- data/lib/flex/template/tags.rb +19 -9
- data/lib/flex/templates.rb +20 -0
- data/lib/flex/utility_methods.rb +80 -89
- data/lib/flex/utils.rb +68 -25
- data/lib/flex/variables.rb +55 -4
- data/lib/tasks.rake +28 -0
- metadata +61 -85
- data/bin/flexes +0 -174
- data/lib/flex/api_methods.yml +0 -108
- data/lib/flex/class_proxy/loader.rb +0 -102
- data/lib/flex/class_proxy/model.rb +0 -45
- data/lib/flex/class_proxy/model_sync.rb +0 -23
- data/lib/flex/class_proxy/related_model.rb +0 -23
- data/lib/flex/instance_proxy/base.rb +0 -29
- data/lib/flex/instance_proxy/model.rb +0 -102
- data/lib/flex/instance_proxy/related_model.rb +0 -7
- data/lib/flex/loader.rb +0 -18
- data/lib/flex/manager.rb +0 -61
- data/lib/flex/model.rb +0 -24
- data/lib/flex/rails/engine.rb +0 -23
- data/lib/flex/rails/helper.rb +0 -16
- data/lib/flex/related_model.rb +0 -16
- data/lib/flex/result/indifferent_access.rb +0 -11
- data/lib/flex/result/source_document.rb +0 -63
- data/lib/flex/result/source_search.rb +0 -32
- data/lib/flex/structure/indifferent_access.rb +0 -44
- data/lib/flex/structure/mergeable.rb +0 -21
- data/lib/flex/template/base.rb +0 -41
- data/lib/flex/template/info.rb +0 -68
- data/lib/generators/flex/setup/setup_generator.rb +0 -48
- data/lib/generators/flex/setup/templates/flex_config.yml +0 -16
- data/lib/generators/flex/setup/templates/flex_dir/es.rb.erb +0 -18
- data/lib/generators/flex/setup/templates/flex_dir/es.yml.erb +0 -19
- data/lib/generators/flex/setup/templates/flex_dir/es_extender.rb.erb +0 -17
- data/lib/generators/flex/setup/templates/flex_initializer.rb.erb +0 -44
- data/lib/tasks/index.rake +0 -17
- data/test/flex.irt +0 -143
- data/test/flex/configuration.irt +0 -53
- data/test/irt_helper.rb +0 -12
data/lib/flex/errors.rb
CHANGED
@@ -8,6 +8,7 @@ module Flex
|
|
8
8
|
class ExistingIndexError < StandardError; end
|
9
9
|
class MissingHttpClientError < StandardError; end
|
10
10
|
class MissingParentError < StandardError; end
|
11
|
+
class MissingVariableError < StandardError; end
|
11
12
|
|
12
13
|
class HttpError < StandardError
|
13
14
|
|
@@ -27,7 +28,7 @@ module Flex
|
|
27
28
|
end
|
28
29
|
|
29
30
|
def to_s
|
30
|
-
log = "
|
31
|
+
log = "#@caller_line\n" if @caller_line
|
31
32
|
"#{log}#{status}: #{body}"
|
32
33
|
end
|
33
34
|
|
@@ -0,0 +1,15 @@
|
|
1
|
+
module Flex
|
2
|
+
module HttpClients
|
3
|
+
class Base
|
4
|
+
|
5
|
+
attr_accessor :options, :base_uri, :raise_proc
|
6
|
+
|
7
|
+
def initialize(base_uri='http://localhost:9200', options={})
|
8
|
+
@options = options
|
9
|
+
@base_uri = base_uri
|
10
|
+
@raise_proc = proc{|response| response.status >= 400}
|
11
|
+
end
|
12
|
+
|
13
|
+
end
|
14
|
+
end
|
15
|
+
end
|
@@ -0,0 +1,51 @@
|
|
1
|
+
module Flex
|
2
|
+
module HttpClients
|
3
|
+
|
4
|
+
class Dummy
|
5
|
+
def request(*)
|
6
|
+
raise MissingHttpClientError,
|
7
|
+
'you should install the gem "patron" (recommended for performances) or "rest-client", ' +
|
8
|
+
'or provide your own http-client interface and set Flex::Configuration.http_client'
|
9
|
+
end
|
10
|
+
end
|
11
|
+
|
12
|
+
module Loader
|
13
|
+
|
14
|
+
extend self
|
15
|
+
|
16
|
+
def new_http_client
|
17
|
+
if Gem::Specification.respond_to?(:find_all_by_name)
|
18
|
+
case
|
19
|
+
# terrible way to check whether a gem is available.
|
20
|
+
# Gem.available? was just perfect: that's probably the reason it has been deprecated!
|
21
|
+
# https://github.com/rubygems/rubygems/issues/176
|
22
|
+
when Gem::Specification::find_all_by_name('patron').any? then require_patron
|
23
|
+
when Gem::Specification::find_all_by_name('rest-client').any? then require_rest_client
|
24
|
+
else Dummy.new
|
25
|
+
end
|
26
|
+
else
|
27
|
+
case
|
28
|
+
when Gem.available?('patron') then require_patron
|
29
|
+
when Gem.available?('rest-client') then require_rest_client
|
30
|
+
else Dummy.new
|
31
|
+
end
|
32
|
+
end
|
33
|
+
end
|
34
|
+
|
35
|
+
private
|
36
|
+
|
37
|
+
def require_patron
|
38
|
+
require 'patron'
|
39
|
+
require 'flex/http_clients/patron'
|
40
|
+
Patron.new
|
41
|
+
end
|
42
|
+
|
43
|
+
def require_rest_client
|
44
|
+
require 'rest-client'
|
45
|
+
require 'flex/http_clients/rest_client'
|
46
|
+
RestClient.new
|
47
|
+
end
|
48
|
+
|
49
|
+
end
|
50
|
+
end
|
51
|
+
end
|
@@ -1,14 +1,16 @@
|
|
1
1
|
module Flex
|
2
2
|
module HttpClients
|
3
|
-
|
4
|
-
extend self
|
3
|
+
class Patron < Base
|
5
4
|
|
6
5
|
def request(method, path, data=nil)
|
7
|
-
|
8
|
-
|
9
|
-
|
6
|
+
# patron would raise an error for :post and :put requests with no data
|
7
|
+
# and elasticsearch ignores the data when it expects no data,
|
8
|
+
# so we silence patron by adding some dummy data
|
9
|
+
data = {} if (method == 'POST' || method == 'PUT') && data.nil?
|
10
|
+
opts = options.merge(:data => data)
|
11
|
+
session.request method.to_s.downcase.to_sym, path, {}, opts
|
10
12
|
rescue ::Patron::TimeoutError
|
11
|
-
session.request method.to_s.downcase.to_sym, path, {},
|
13
|
+
session.request method.to_s.downcase.to_sym, path, {}, opts
|
12
14
|
end
|
13
15
|
|
14
16
|
private
|
@@ -17,7 +19,7 @@ module Flex
|
|
17
19
|
Thread.current[:flex_patron_session] ||= begin
|
18
20
|
sess = ::Patron::Session.new
|
19
21
|
sess.headers['User-Agent'] = "flex-#{Flex::VERSION}"
|
20
|
-
sess.base_url =
|
22
|
+
sess.base_url = base_uri
|
21
23
|
sess
|
22
24
|
end
|
23
25
|
end
|
@@ -1,15 +1,13 @@
|
|
1
1
|
module Flex
|
2
2
|
module HttpClients
|
3
|
-
|
4
|
-
extend self
|
3
|
+
class RestClient < Base
|
5
4
|
|
6
5
|
def request(method, path, data=nil)
|
7
|
-
|
8
|
-
|
9
|
-
|
10
|
-
|
11
|
-
|
12
|
-
response = ::RestClient::Request.new( args ).execute
|
6
|
+
url = "#{base_uri}#{path}"
|
7
|
+
opts = options.merge( :method => method.to_s.downcase.to_sym,
|
8
|
+
:url => url,
|
9
|
+
:payload => data )
|
10
|
+
response = ::RestClient::Request.new( opts ).execute
|
13
11
|
extend_response(response, url)
|
14
12
|
|
15
13
|
rescue ::RestClient::ExceptionWithResponse => e
|
data/lib/flex/logger.rb
CHANGED
@@ -3,13 +3,34 @@ require 'logger'
|
|
3
3
|
module Flex
|
4
4
|
class Logger < ::Logger
|
5
5
|
|
6
|
+
|
7
|
+
attr_accessor :debug_variables, :debug_request, :debug_result, :curl_format
|
8
|
+
|
6
9
|
def initialize(*)
|
7
10
|
super
|
8
|
-
self.level = ::Logger::
|
9
|
-
self.progname =
|
11
|
+
self.level = ::Logger::WARN
|
12
|
+
self.progname = 'FLEX'
|
10
13
|
self.formatter = proc do |severity, datetime, progname, msg|
|
11
|
-
|
14
|
+
flex_format(severity, msg)
|
12
15
|
end
|
16
|
+
@debug_variables = true
|
17
|
+
@debug_request = true
|
18
|
+
@debug_result = true
|
19
|
+
@curl_format = false
|
20
|
+
end
|
21
|
+
|
22
|
+
def flex_format(severity, msg)
|
23
|
+
prefix = Dye.dye(" FLEX-#{severity} ", "FLEX-#{severity}:", :blue, :bold, :reversed) + ' '
|
24
|
+
msg.split("\n").map{|l| prefix + l}.join("\n") + "\n"
|
25
|
+
end
|
26
|
+
|
27
|
+
# force color in console (used with jruby)
|
28
|
+
def color=(bool)
|
29
|
+
Dye.color = bool
|
30
|
+
end
|
31
|
+
|
32
|
+
def color
|
33
|
+
Dye.color?
|
13
34
|
end
|
14
35
|
|
15
36
|
end
|
data/lib/flex/prog_bar.rb
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
module Flex
|
2
2
|
class ProgBar
|
3
3
|
|
4
|
-
attr_reader :pbar
|
4
|
+
attr_reader :pbar, :total_count
|
5
5
|
|
6
6
|
def initialize(total_count, batch_size=nil, prefix_message=nil)
|
7
7
|
@total_count = total_count
|
@@ -18,16 +18,21 @@ module Flex
|
|
18
18
|
end
|
19
19
|
|
20
20
|
def process_result(result, inc)
|
21
|
-
|
22
|
-
|
23
|
-
|
21
|
+
unless result.nil? || result.empty?
|
22
|
+
unless result.failed.size == 0
|
23
|
+
Conf.logger.error "Failed load:\n#{result.failed.to_yaml}"
|
24
|
+
@pbar.bar_mark = 'F'
|
25
|
+
end
|
26
|
+
@failed_count += result.failed.size
|
27
|
+
@successful_count += result.successful.size
|
28
|
+
end
|
24
29
|
@pbar.inc(inc)
|
25
30
|
end
|
26
31
|
|
27
32
|
def finish
|
28
33
|
@pbar.finish
|
29
|
-
puts "Processed
|
30
|
-
puts
|
34
|
+
puts "Processed #@total_count. Successful #@successful_count. Skipped #{@total_count - @successful_count - @failed_count}. Failed #@failed_count."
|
35
|
+
puts 'See the log for the details about the failures.' unless @failed_count == 0
|
31
36
|
end
|
32
37
|
|
33
38
|
end
|
data/lib/flex/rails.rb
CHANGED
@@ -1,13 +1 @@
|
|
1
|
-
require 'flex'
|
2
|
-
require 'rails'
|
3
|
-
require 'flex/rails/helper'
|
4
|
-
|
5
|
-
if ::Rails.respond_to?(:version) && ::Rails.version.to_i >= 3
|
6
|
-
require 'flex/rails/engine'
|
7
|
-
else
|
8
|
-
Flex::Configuration.configure do |c|
|
9
|
-
c.config_file = ::Rails.root.join('config', 'flex.yml').to_s
|
10
|
-
c.flex_dir = ::Rails.root.join('app', 'flex').to_s
|
11
|
-
c.debug = ::Rails.env.development?
|
12
|
-
end
|
13
|
-
end
|
1
|
+
raise NotImplementedError, %(The "flex/rails" file has been replaced by the "flex-rails" gem. Please, use "flex-rails" in place of "flex" in Rails applications (change "gem 'flex', require => 'flex/rails'" to "gem 'flex-rails'" in your Gemfile))
|
data/lib/flex/result.rb
CHANGED
@@ -1,18 +1,24 @@
|
|
1
1
|
module Flex
|
2
2
|
class Result < ::Hash
|
3
3
|
|
4
|
-
attr_reader :template, :
|
4
|
+
attr_reader :template, :response
|
5
|
+
attr_accessor :variables
|
5
6
|
|
6
7
|
def initialize(template, variables, response, result=nil)
|
7
8
|
@template = template
|
8
9
|
@variables = variables
|
9
10
|
@response = response
|
10
11
|
replace result || !response.body.empty? && MultiJson.decode(response.body) || return
|
11
|
-
|
12
|
+
Conf.result_extenders.each do |ext|
|
12
13
|
next if ext.respond_to?(:should_extend?) && !ext.should_extend?(self)
|
13
14
|
extend ext
|
14
15
|
end
|
15
16
|
end
|
16
17
|
|
18
|
+
def to_flex_result(force=false)
|
19
|
+
return self if variables[:context].nil? || variables[:raw_result] &&! force
|
20
|
+
variables[:context].respond_to?(:flex_result) ? variables[:context].flex_result(self) : self
|
21
|
+
end
|
22
|
+
|
17
23
|
end
|
18
24
|
end
|
data/lib/flex/result/document.rb
CHANGED
@@ -1,36 +1,65 @@
|
|
1
1
|
module Flex
|
2
2
|
class Result
|
3
3
|
|
4
|
-
# adds sugar to documents with the following structure:
|
4
|
+
# adds sugar to documents with the following structure (_source is optional):
|
5
5
|
#
|
6
6
|
# {
|
7
7
|
# "_index" : "twitter",
|
8
8
|
# "_type" : "tweet",
|
9
9
|
# "_id" : "1",
|
10
|
+
# "_source" : {
|
11
|
+
# "user" : "kimchy",
|
12
|
+
# "postDate" : "2009-11-15T14:12:12",
|
13
|
+
# "message" : "trying out Elastic Search"
|
14
|
+
# }
|
10
15
|
# }
|
11
16
|
|
12
17
|
module Document
|
13
18
|
|
14
|
-
META = %w[_index _type _id]
|
15
|
-
|
16
19
|
# extend if result has a structure like a document
|
17
20
|
def self.should_extend?(obj)
|
18
|
-
|
21
|
+
%w[_index _type _id].all? {|k| obj.has_key?(k)}
|
22
|
+
end
|
23
|
+
|
24
|
+
def respond_to?(meth, private=false)
|
25
|
+
smeth = meth.to_s
|
26
|
+
readers.has_key?(smeth) || has_key?(smeth) || has_key?("_#{smeth}") || super
|
19
27
|
end
|
20
28
|
|
21
|
-
|
22
|
-
|
29
|
+
# exposes _source and readers: automatically supply object-like reader access
|
30
|
+
# also expose meta readers like _id, _source, etc, also callable without the leading '_'
|
31
|
+
def method_missing(meth, *args, &block)
|
32
|
+
smeth = meth.to_s
|
33
|
+
case
|
34
|
+
# field name
|
35
|
+
when readers.has_key?(smeth)
|
36
|
+
readers[smeth]
|
37
|
+
# result item
|
38
|
+
when has_key?(smeth)
|
39
|
+
self[smeth]
|
40
|
+
# result item called without the '_' prefix
|
41
|
+
when has_key?("_#{smeth}")
|
42
|
+
self["_#{smeth}"]
|
43
|
+
else
|
44
|
+
super
|
45
|
+
end
|
23
46
|
end
|
24
47
|
|
25
|
-
|
26
|
-
|
27
|
-
|
28
|
-
raise DocumentMappingError, "the '#{_index}/#{_type}' document cannot be mapped to any class." \
|
29
|
-
if should_raise
|
48
|
+
# used to get the unprefixed (by live-reindex) index name
|
49
|
+
def index_basename
|
50
|
+
@index_basename ||= self['_index'].sub(/^\d{14}_/, '')
|
30
51
|
end
|
31
52
|
|
32
|
-
|
33
|
-
|
53
|
+
|
54
|
+
private
|
55
|
+
|
56
|
+
def readers
|
57
|
+
@readers ||= begin
|
58
|
+
readers = (self['_source']||{}).merge(self['fields']||{})
|
59
|
+
# flattened reader for multi_readers or attachment readers
|
60
|
+
readers.keys.each{|k| readers[k.gsub('.','_')] = readers.delete(k) if k.include?('.')}
|
61
|
+
readers
|
62
|
+
end
|
34
63
|
end
|
35
64
|
|
36
65
|
end
|
@@ -0,0 +1,24 @@
|
|
1
|
+
module Flex
|
2
|
+
class Result
|
3
|
+
module MultiGet
|
4
|
+
|
5
|
+
# extend if result comes from a search url
|
6
|
+
def self.should_extend?(result)
|
7
|
+
result.response.url =~ /\b_mget\b/ && result['docs']
|
8
|
+
end
|
9
|
+
|
10
|
+
# extend the hits results on extended
|
11
|
+
def self.extended(result)
|
12
|
+
result['docs'].each { |h| h.extend(Document) }
|
13
|
+
result['docs'].extend Struct::Paginable
|
14
|
+
result['docs'].setup(result['docs'].size, result.variables)
|
15
|
+
end
|
16
|
+
|
17
|
+
def docs
|
18
|
+
self['docs']
|
19
|
+
end
|
20
|
+
alias_method :collection, :docs
|
21
|
+
|
22
|
+
end
|
23
|
+
end
|
24
|
+
end
|
data/lib/flex/result/search.rb
CHANGED
@@ -10,7 +10,7 @@ module Flex
|
|
10
10
|
# extend the hits results on extended
|
11
11
|
def self.extended(result)
|
12
12
|
result['hits']['hits'].each { |h| h.extend(Document) }
|
13
|
-
result['hits']['hits'].extend
|
13
|
+
result['hits']['hits'].extend Struct::Paginable
|
14
14
|
result['hits']['hits'].setup(result['hits']['total'], result.variables)
|
15
15
|
end
|
16
16
|
|
@@ -23,29 +23,6 @@ module Flex
|
|
23
23
|
self['facets']
|
24
24
|
end
|
25
25
|
|
26
|
-
def loaded_collection
|
27
|
-
@loaded_collection ||= begin
|
28
|
-
records = []
|
29
|
-
# returns a structure like {Comment=>[{"_id"=>"123", ...}, {...}], BlogPost=>[...]}
|
30
|
-
h = Utils.group_array_by(collection) do |d|
|
31
|
-
d.mapped_class(should_raise=true)
|
32
|
-
end
|
33
|
-
h.each do |klass, docs|
|
34
|
-
records |= klass.find(docs.map(&:_id))
|
35
|
-
end
|
36
|
-
class_ids = collection.map { |d| [d.mapped_class.to_s, d._id] }
|
37
|
-
# Reorder records to preserve order from search results
|
38
|
-
records = class_ids.map do |class_str, id|
|
39
|
-
records.detect do |record|
|
40
|
-
record.class.to_s == class_str && record.id.to_s == id.to_s
|
41
|
-
end
|
42
|
-
end
|
43
|
-
records.extend Collection
|
44
|
-
records.setup(self['hits']['total'], variables)
|
45
|
-
records
|
46
|
-
end
|
47
|
-
end
|
48
|
-
|
49
26
|
end
|
50
27
|
end
|
51
28
|
end
|
@@ -0,0 +1,25 @@
|
|
1
|
+
module Flex
|
2
|
+
module Struct
|
3
|
+
class Array < ::Array
|
4
|
+
|
5
|
+
include Symbolize
|
6
|
+
|
7
|
+
def push(*vals)
|
8
|
+
super *symbolize(vals)
|
9
|
+
end
|
10
|
+
|
11
|
+
def <<(val)
|
12
|
+
super symbolize(val)
|
13
|
+
end
|
14
|
+
|
15
|
+
def insert(*vals)
|
16
|
+
super *symbolize(vals)
|
17
|
+
end
|
18
|
+
|
19
|
+
def unshift(*vals)
|
20
|
+
super *symbolize(vals)
|
21
|
+
end
|
22
|
+
|
23
|
+
end
|
24
|
+
end
|
25
|
+
end
|
@@ -0,0 +1,105 @@
|
|
1
|
+
module Flex
|
2
|
+
module Struct
|
3
|
+
class Hash < ::Hash
|
4
|
+
include Symbolize
|
5
|
+
|
6
|
+
def initialize
|
7
|
+
super do |hash, key|
|
8
|
+
if key[-1] == '!'
|
9
|
+
klass = (key[0] == '_' ? Array : Hash)
|
10
|
+
hash[clean_key(key)] = klass.new
|
11
|
+
end
|
12
|
+
end
|
13
|
+
end
|
14
|
+
|
15
|
+
def merge(hash)
|
16
|
+
super symbolize(hash)
|
17
|
+
end
|
18
|
+
|
19
|
+
def merge!(hash)
|
20
|
+
super symbolize(hash)
|
21
|
+
end
|
22
|
+
|
23
|
+
def store(key, val)
|
24
|
+
if key[-1] == '='
|
25
|
+
super key[0..-2].to_sym, val.extend(AsIs)
|
26
|
+
else
|
27
|
+
super clean_key(key), symbolize(val)
|
28
|
+
end
|
29
|
+
end
|
30
|
+
alias_method :[]=, :store
|
31
|
+
|
32
|
+
def fetch(key, *rest, &block)
|
33
|
+
cleaned = clean_key(key)
|
34
|
+
super has_key?(cleaned) ? cleaned : key.to_sym, *rest, &block
|
35
|
+
end
|
36
|
+
|
37
|
+
def [](key)
|
38
|
+
cleaned = clean_key(key)
|
39
|
+
super has_key?(cleaned) ? cleaned : key.to_sym
|
40
|
+
end
|
41
|
+
|
42
|
+
def deep_merge(*hashes)
|
43
|
+
dupe = deep_dup(self)
|
44
|
+
hashes.each {|h2| dupe.replace(deep_merge_hash(dupe,h2))}
|
45
|
+
dupe
|
46
|
+
end
|
47
|
+
|
48
|
+
def deep_merge!(*hashes)
|
49
|
+
replace deep_merge(*hashes)
|
50
|
+
end
|
51
|
+
|
52
|
+
module Nil
|
53
|
+
def method_missing(*)
|
54
|
+
self
|
55
|
+
end
|
56
|
+
end
|
57
|
+
|
58
|
+
def try(key)
|
59
|
+
has_key?(key) ? self[key] : nil.extend(Nil)
|
60
|
+
end
|
61
|
+
|
62
|
+
def try_delete(key, *rest, &block)
|
63
|
+
val = delete clean_key(key), *rest, &block
|
64
|
+
val.nil? ? nil.extend(Nil) : val
|
65
|
+
end
|
66
|
+
|
67
|
+
|
68
|
+
private
|
69
|
+
|
70
|
+
def clean_key(key)
|
71
|
+
key[-1] == '!' ? key[0..-2].to_sym : key.to_sym
|
72
|
+
end
|
73
|
+
|
74
|
+
def deep_merge_hash(h1, h2)
|
75
|
+
h2 ||= {}
|
76
|
+
h1.merge(h2) do |key, oldval, newval|
|
77
|
+
case
|
78
|
+
when oldval.is_a?(Hash) && newval.is_a?(Hash)
|
79
|
+
deep_merge_hash(oldval, newval)
|
80
|
+
when oldval.is_a?(Array) && newval.is_a?(Array)
|
81
|
+
oldval | newval
|
82
|
+
else
|
83
|
+
newval
|
84
|
+
end
|
85
|
+
end
|
86
|
+
end
|
87
|
+
|
88
|
+
def deep_dup(obj)
|
89
|
+
case obj
|
90
|
+
when ::Hash, Flex::Struct::Hash
|
91
|
+
h = obj.dup
|
92
|
+
h.each_pair do |k,v|
|
93
|
+
h[k] = deep_dup(v)
|
94
|
+
end
|
95
|
+
h
|
96
|
+
when ::Array, Flex::Struct::Array
|
97
|
+
obj.map{|i| deep_dup(i)}
|
98
|
+
else
|
99
|
+
obj
|
100
|
+
end
|
101
|
+
end
|
102
|
+
|
103
|
+
end
|
104
|
+
end
|
105
|
+
end
|