Module: Line::Bot::V2::Utils

Defined in:
lib/line/bot/v2/utils.rb

Constant Summary collapse

NO_CAMELIZE_PARENT_KEYS =

NOTE: Although it should be set in the request class side,

it can only be set here because requests in Hash are also permitted.
If there is a mix of camelize and non-camelize cases even with the same key name,
breaking change that does not allow Hash requests will be necessary.
%w(substitution).freeze
PATH_TRAVERSAL =

Matches a path that contains a “.” or “..” segment (literal or percent-encoded), so a path parameter cannot escape its endpoint. See square/retrofit’s RequestBuilder for the same idea.

%r{\A(?:.*/)?(?:\.|%2e|%2E){1,2}(?:/.*)?\z}.freeze

Class Method Summary collapse

Class Method Details

.build_path(path_template, params) ⇒ Object

NOTE: line-bot-sdk-ruby users should not use this. Breaking changes may occur, so use at your own risk.

Substitutes path parameters into a path template and rejects any result that would let a parameter perform path traversal.



21
22
23
24
25
26
27
28
29
30
31
32
33
34
# File 'lib/line/bot/v2/utils.rb', line 21

def self.build_path(path_template, params)
  path = path_template
  params.each do |name, value|
    raise ArgumentError, "#{name} is required" if value.nil?

    path = path.gsub("{#{name}}", URI.encode_uri_component(value.to_s))
  end

  if PATH_TRAVERSAL.match?(path)
    raise ArgumentError, "Path parameters shouldn't perform path traversal ('.' or '..'): #{path}"
  end

  path
end

.camelize(str) ⇒ Object

NOTE: line-bot-sdk-ruby users should not use this. Breaking changes may occur, so use at your own risk.



162
163
164
# File 'lib/line/bot/v2/utils.rb', line 162

def self.camelize(str)
  str.to_s.split('_').inject { |memo, word| memo + word.capitalize }
end

.deep_camelize(object) ⇒ Object

NOTE: line-bot-sdk-ruby users should not use this. Breaking changes may occur, so use at your own risk.



98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
# File 'lib/line/bot/v2/utils.rb', line 98

def self.deep_camelize(object)
  case object
  when Array
    object.map { |item| deep_camelize(item) }
  when Hash
    object.each_with_object({}) do |(k, v), new_object| # steep:ignore UnannotatedEmptyCollection
      if NO_CAMELIZE_PARENT_KEYS.include?(k.to_s)
        new_object[k.to_sym] = if v.is_a?(Hash)
                                 v.transform_keys(&:to_sym).transform_values { deep_camelize(_1) }
                               else
                                 v
                               end
      else
        camel_key = camelize(k)&.to_sym
        new_object[camel_key] = v.is_a?(Array) || v.is_a?(Hash) ? deep_camelize(v) : v if camel_key
      end
    end
  else
    object
  end
end

.deep_compact(object) ⇒ Object

NOTE: line-bot-sdk-ruby users should not use this. Breaking changes may occur, so use at your own risk.



121
122
123
124
125
126
127
128
129
130
131
132
133
# File 'lib/line/bot/v2/utils.rb', line 121

def self.deep_compact(object)
  case object
  when Hash
    object.each_with_object({}) do |(k, v), new_hash| # steep:ignore UnannotatedEmptyCollection
      new_value = deep_compact(v)
      new_hash[k] = new_value unless new_value.nil? || (new_value.is_a?(Hash) && new_value.empty?)
    end
  when Array
    object.map { |item| deep_compact(item) }.compact
  else
    object
  end
end

.deep_convert_reserved_words(object) ⇒ Object

NOTE: line-bot-sdk-ruby users should not use this. Breaking changes may occur, so use at your own risk.



136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
# File 'lib/line/bot/v2/utils.rb', line 136

def self.deep_convert_reserved_words(object)
  case object
  when Hash
    object.each_with_object({}) do |(key, value), new_hash| # steep:ignore UnannotatedEmptyCollection
      new_key = if Line::Bot::V2::RESERVED_WORDS.include?(key.to_sym)
                  "_#{key}"
                else
                  key
                end
      new_hash[new_key] = deep_convert_reserved_words(value)
    end
  when Array
    object.map { |element| deep_convert_reserved_words(element) }
  else
    object
  end
end

.deep_symbolize(object) ⇒ Object

NOTE: line-bot-sdk-ruby users should not use this. Breaking changes may occur, so use at your own risk.



53
54
55
56
57
58
59
60
61
62
63
64
65
# File 'lib/line/bot/v2/utils.rb', line 53

def self.deep_symbolize(object)
  case object
  when Hash
    object.each_with_object({}) do |(key, value), new_hash| # steep:ignore UnannotatedEmptyCollection
      sym_key = key.is_a?(String) ? key.to_sym : key
      new_hash[sym_key] = deep_symbolize(value)
    end
  when Array
    object.map { |element| deep_symbolize(element) }
  else
    object
  end
end

.deep_to_hash(object) ⇒ Object

NOTE: line-bot-sdk-ruby users should not use this. Breaking changes may occur, so use at your own risk.



68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
# File 'lib/line/bot/v2/utils.rb', line 68

def self.deep_to_hash(object)
  if object.is_a?(Array)
    object.map { |item| deep_to_hash(item) }
  elsif object.is_a?(Hash)
    result = object.transform_keys do |k|
      if k.to_s.start_with?('_') && Line::Bot::V2::RESERVED_WORDS.include?(k.to_s.delete_prefix('_').to_sym)
        k.to_s.delete_prefix('_').to_sym
      else
        k.to_sym
      end
    end
    result.transform_values { |v| deep_to_hash(v) }
  elsif object.instance_variables.empty?
    object
  else
    object.instance_variables.each_with_object({}) do |var, hash| # steep:ignore UnannotatedEmptyCollection
      value = object.instance_variable_get(var)

      key = var.to_s.delete('@')
      if key.start_with?('_') && Line::Bot::V2::RESERVED_WORDS.include?(key.delete_prefix('_').to_sym)
        key = key.delete_prefix('_')
      end
      key = key.to_sym

      hash[key] = deep_to_hash(value)
    end
  end
end

.deep_underscore(hash) ⇒ Object

NOTE: line-bot-sdk-ruby users should not use this. Breaking changes may occur, so use at your own risk.



37
38
39
40
41
42
43
44
45
46
47
48
49
50
# File 'lib/line/bot/v2/utils.rb', line 37

def self.deep_underscore(hash)
  hash.each_with_object({}) do |(key, value), result| # steep:ignore UnannotatedEmptyCollection
    # Convert key to string if it's a symbol, then apply the regex
    new_key = key.to_s.gsub(/([A-Z\d]+)([A-Z][a-z])|([a-z\d])([A-Z])/, '\1\3_\2\4').downcase.to_sym
    new_value = if value.is_a?(Hash)
                  deep_underscore(value)
                elsif value.is_a?(Array)
                  value.map { |v| v.is_a?(Hash) ? deep_underscore(v) : v }
                else
                  value
                end
    result[new_key] = new_value
  end
end

.hash_to_struct(hash) ⇒ Object

NOTE: line-bot-sdk-ruby users should not use this. Breaking changes may occur, so use at your own risk.



155
156
157
158
159
# File 'lib/line/bot/v2/utils.rb', line 155

def self.hash_to_struct(hash)
  struct_klass = Struct.new(*hash.keys.map(&:to_sym))
  struct_values = hash.map { |_k, v| v.is_a?(Hash) ? hash_to_struct(v) : v }
  struct_klass.new(*struct_values)
end