aliyun-log 0.2.6 → 0.2.8

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.
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: ed78b39497d6a671405b2d815292ca1e1e11ee301d816395264add76a9bc1235
4
- data.tar.gz: 1b2f92c27745b27be61ca5f8fbb41470eaffcd710b22979671604173646c5450
3
+ metadata.gz: b389451492d70d3427b9a940de87c2166d862ac543f882b97a3924e31b51e92c
4
+ data.tar.gz: 6f8d17bad66ed46c0a8da7e514c10cfaa1198418e48e7d2f03be8f8de499d16d
5
5
  SHA512:
6
- metadata.gz: 19e6a18d2c168882054e14875da1baa18bab32217b2626a33e209e25c867b011deb5cba7d0b6de2f4af40a67ff4c66fb12ff3908881d9e28ced5ca8020c152b4
7
- data.tar.gz: 7cfdddb1c6d804b4f62ee34cac200d4eb869ff32c70cf5bda5a4fd10947d06c5df61ba273662aff908a57e685a8ce2c586d1e3efed8d71e1dc2c8a59b649e117
6
+ metadata.gz: 1c68ba2d18b987e36c96187a7007ede440af4f0beb36748e49ce213872f57dd5db78dfb82ed63c4f9ee5f87b558daa4fc0686b13e71533ba4d3dc6fdaa3333ae
7
+ data.tar.gz: 3ce13d9720545a7394a0b64b2b6adb78f196dd83a554f7b872a57460e29dac5dd2876a5a528c7c728c537ece778bc4913900b0487856dd65c7d563ff5f0045ee
@@ -7,7 +7,8 @@ module Aliyun
7
7
  @endpoint = 'https://cn-beijing.log.aliyuncs.com'
8
8
  @open_timeout = 10
9
9
  @read_timeout = 120
10
- @log_level = Logger::DEBUG
10
+ @log_level = Logger::WARN
11
+ @log_file = STDOUT
11
12
  @timestamps = true
12
13
  class << self
13
14
  attr_accessor :endpoint, :access_key_id, :access_key_secret,
@@ -9,8 +9,7 @@ require 'forwardable'
9
9
  require_relative 'record/exception'
10
10
  require_relative 'record/field'
11
11
  require_relative 'record/persistence'
12
- require_relative 'record/relation'
13
- require_relative 'record/scope_registry'
12
+ require_relative 'record/scoping'
14
13
 
15
14
  module Aliyun
16
15
  module Log
@@ -28,7 +27,7 @@ module Aliyun
28
27
 
29
28
  Log.included_models << self unless Log.included_models.include? self
30
29
 
31
- field :created_at, :text if Config.timestamps
30
+ field :created_at, type: :text, cast_type: :datetime if Config.timestamps
32
31
 
33
32
  define_model_callbacks :save, :create, :initialize
34
33
 
@@ -37,6 +36,7 @@ module Aliyun
37
36
 
38
37
  include Field
39
38
  include Persistence
39
+ include Scoping
40
40
 
41
41
  include ActiveModel::AttributeMethods
42
42
 
@@ -52,40 +52,6 @@ module Aliyun
52
52
  opt[:field_doc_value] = opt[:field_doc_value] != false
53
53
  self.options = opt
54
54
  end
55
-
56
- delegate :load, :result, :count, to: :all
57
- delegate :where, :query, :search, :sql, :from, :to, :page, :line, :limit, :offset, to: :all
58
- delegate :first, :last, :second, :third, :fourth, :fifth, :find_offset, to: :all
59
-
60
- def current_scope
61
- ScopeRegistry.value_for(:current_scope, self)
62
- end
63
-
64
- def current_scope=(scope)
65
- ScopeRegistry.set_value_for(:current_scope, self, scope)
66
- end
67
-
68
- def scope(name, body)
69
- raise ArgumentError, 'The scope body needs to be callable.' unless body.respond_to?(:call)
70
-
71
- singleton_class.send(:define_method, name) do |*args|
72
- scope = all
73
- scope = scope.scoping { body.call(*args) }
74
- scope
75
- end
76
- end
77
-
78
- def all
79
- scope = current_scope
80
- scope ||= relation.from(0).to(Time.now.to_i)
81
- scope
82
- end
83
-
84
- private
85
-
86
- def relation
87
- Relation.new(self)
88
- end
89
55
  end
90
56
 
91
57
  def initialize(attrs = {})
@@ -4,6 +4,7 @@ module Aliyun
4
4
  class ArgumentError < StandardError; end
5
5
  class UnknownAttributeError < StandardError; end
6
6
  class ProjectNameError < StandardError; end
7
+ class ParseStatementInvalid < StandardError; end
7
8
  end
8
9
  end
9
10
  end
@@ -1,5 +1,7 @@
1
1
  # frozen_string_literal: true
2
2
 
3
+ require_relative 'type_casting'
4
+
3
5
  module Aliyun
4
6
  module Log
5
7
  module Record
@@ -18,10 +20,12 @@ module Aliyun
18
20
 
19
21
  included do
20
22
  class_attribute :attributes, instance_accessor: false, default: {}
23
+ extend Common::Logging
21
24
  end
22
25
 
23
26
  module ClassMethods
24
- def field(name, type = :text, options = {})
27
+ def field(name, options = {})
28
+ type = options[:type] || :text
25
29
  unless PERMITTED_KEY_TYPES.include?(type)
26
30
  raise ArgumentError, "Field #{name} type(#{type}) error, key type only support text/long/double/json"
27
31
  end
@@ -33,7 +37,7 @@ module Aliyun
33
37
  warn_about_method_overriding("#{named}=", name)
34
38
  warn_about_method_overriding("#{named}?", name)
35
39
 
36
- define_attribute_method(name) # Dirty API
40
+ define_attribute_method(name)
37
41
 
38
42
  generated_methods.module_eval do
39
43
  define_method(named) { read_attribute(named) }
@@ -61,7 +65,6 @@ module Aliyun
61
65
  remove_method field
62
66
  remove_method :"#{field}="
63
67
  remove_method :"#{field}?"
64
- remove_method :"#{field}_before_type_cast"
65
68
  end
66
69
  end
67
70
 
@@ -77,7 +80,8 @@ module Aliyun
77
80
 
78
81
  def warn_about_method_overriding(method_name, field_name)
79
82
  if instance_methods.include?(method_name.to_sym)
80
- Common::Logging.logger.warn("Method #{method_name} generated for the field #{field_name} overrides already existing method")
83
+ logger.warn("Method #{method_name} generated for the field #{field_name} " \
84
+ 'overrides already existing method')
81
85
  end
82
86
  end
83
87
  end
@@ -103,7 +107,7 @@ module Aliyun
103
107
  attr_accessor :attributes
104
108
 
105
109
  def write_attribute(name, value)
106
- attributes[name.to_sym] = value
110
+ attributes[name.to_sym] = TypeCasting.cast_field(value, self.class.attributes[name.to_sym])
107
111
  end
108
112
 
109
113
  alias []= write_attribute
@@ -114,7 +118,7 @@ module Aliyun
114
118
  alias [] read_attribute
115
119
 
116
120
  def set_created_at
117
- self.created_at ||= DateTime.now.in_time_zone(Time.zone).to_s if timestamps_enabled?
121
+ self.created_at ||= DateTime.now.in_time_zone(Time.zone).iso8601 if timestamps_enabled?
118
122
  end
119
123
 
120
124
  def timestamps_enabled?
@@ -56,9 +56,8 @@ module Aliyun
56
56
  def create(data, opts = {})
57
57
  auto_load_schema
58
58
  if data.is_a?(Array)
59
- logs = []
60
- data.each do |log_attr|
61
- logs << new(log_attr).save_array
59
+ logs = data.map do |log_attr|
60
+ new(log_attr).save_array
62
61
  end
63
62
  res = Log.record_connection.put_log(project_name, logstore_name, logs, opts)
64
63
  res.code == 200
@@ -80,17 +79,11 @@ module Aliyun
80
79
  end
81
80
 
82
81
  def field_indices
83
- indices = if options[:field_index] == false
84
- attributes.select { |_, value| value[:index] == true }
85
- else
86
- attributes.reject { |_, value| value[:index] == false }
87
- end
88
- indices.each do |_, v|
89
- next unless v[:doc_value].nil?
90
-
91
- v[:doc_value] = options[:field_doc_value] != false
82
+ if options[:field_index] == false
83
+ attributes.select { |_, value| value[:index] == true }
84
+ else
85
+ attributes.reject { |_, value| value[:index] == false }
92
86
  end
93
- indices
94
87
  end
95
88
 
96
89
  def create_index
@@ -104,18 +97,38 @@ module Aliyun
104
97
  def update_index
105
98
  conf_res = Log.record_connection.get_index(project_name, logstore_name)
106
99
  raw_conf = JSON.parse(conf_res)
107
- index_conf = raw_conf.dup
108
- field_indices.each do |k, v|
109
- index_conf['keys'][k.to_s] ||= v
100
+ index_conf = raw_conf.deep_dup
101
+ field_index_types.each do |k, v|
102
+ index_conf['keys'][k.to_s] ||= {}
103
+ index_conf['keys'][k.to_s].merge!(v.as_json)
110
104
  end
111
105
  return if index_conf['keys'] == raw_conf['keys']
112
106
 
113
107
  Log.record_connection.update_index(
114
108
  project_name,
115
109
  logstore_name,
116
- index_conf['keys']
110
+ index_conf['keys'].with_indifferent_access
117
111
  )
118
112
  end
113
+
114
+ def field_index_types
115
+ field_indices.tap do |tap|
116
+ tap.each do |_, v|
117
+ v[:alias] ||= ''
118
+ v[:caseSensitive] ||= false
119
+ v[:chn] ||= false
120
+ v[:doc_value] = options[:field_doc_value] != false if v[:doc_value].nil?
121
+ end
122
+ end
123
+ end
124
+ end
125
+
126
+ def dump_attributes
127
+ attributes.dup.tap do |tap|
128
+ tap.each do |k, v|
129
+ tap[k] = TypeCasting.dump_field(v, self.class.attributes[k])
130
+ end
131
+ end
119
132
  end
120
133
 
121
134
  def save
@@ -123,7 +136,11 @@ module Aliyun
123
136
  run_callbacks(:create) do
124
137
  run_callbacks(:save) do
125
138
  if valid?
126
- res = Log.record_connection.put_log(self.class.project_name, self.class.logstore_name, attributes)
139
+ res = Log.record_connection.put_log(
140
+ self.class.project_name,
141
+ self.class.logstore_name,
142
+ dump_attributes
143
+ )
127
144
  res.code == 200
128
145
  else
129
146
  false
@@ -135,7 +152,7 @@ module Aliyun
135
152
  def save_array
136
153
  run_callbacks(:create) do
137
154
  run_callbacks(:save) do
138
- validate! && attributes
155
+ validate! && dump_attributes
139
156
  end
140
157
  end
141
158
  end
@@ -8,12 +8,11 @@ module Aliyun
8
8
  @klass = klass
9
9
  @opts = opts
10
10
  @klass.auto_load_schema
11
- @opts[:search] ||= '*'
12
11
  end
13
12
 
14
- def inspect
15
- "#<#{self.class}>"
16
- end
13
+ # def inspect
14
+ # "#<#{self.class}>"
15
+ # end
17
16
 
18
17
  def first(line = 1)
19
18
  find_offset(0, line, false)
@@ -87,13 +86,15 @@ module Aliyun
87
86
  self
88
87
  end
89
88
 
90
- def search(str)
91
- @opts[:search] = str
89
+ def search(*statement)
90
+ ql = statement_ql(*statement)
91
+ @opts[:search] = ql if ql.present?
92
92
  self
93
93
  end
94
94
 
95
- def sql(str)
96
- @opts[:sql] = str
95
+ def sql(*statement)
96
+ ql = statement_ql(*statement)
97
+ @opts[:sql] = ql if ql.present?
97
98
  self
98
99
  end
99
100
 
@@ -120,7 +121,7 @@ module Aliyun
120
121
  query[:line] ||= 100
121
122
  query[:offset] = query[:page] * query[:line]
122
123
  end
123
- query[:query] = query[:search]
124
+ query[:query] = query[:search] || '*'
124
125
  query[:query] = "#{query[:query]}|#{query[:sql]}" if query[:sql].present?
125
126
  res = Log.record_connection.get_logs(@klass.project_name, @klass.logstore_name, query)
126
127
  JSON.parse(res)
@@ -128,16 +129,72 @@ module Aliyun
128
129
 
129
130
  def load
130
131
  result.map do |json_attr|
131
- attrs = {}
132
- @klass.attributes.keys.each do |k, _|
133
- attrs[k] = json_attr[k.to_s]
132
+ record = @klass.new
133
+ json_attr.each do |key, value|
134
+ record.send("#{key}=", value) if record.respond_to?("#{key}=")
134
135
  end
135
- @klass.new(attrs)
136
+ record
136
137
  end
137
138
  end
138
139
 
139
140
  private
140
141
 
142
+ def statement_ql(*statement)
143
+ if statement.size == 1
144
+ sanitize_hash(statement.first)
145
+ elsif statement.size > 1
146
+ sanitize_array(*statement)
147
+ end
148
+ end
149
+
150
+ def sanitize_hash(search_content)
151
+ return search_content unless search_content.is_a?(Hash)
152
+
153
+ search_content.select { |_, v| v.present? }.map do |key, value|
154
+ unless @klass.attributes[:"#{key}"]
155
+ raise UnknownAttributeError, "unknown field '#{key}' for #{@klass.name}."
156
+ end
157
+
158
+ "#{key}: #{value}"
159
+ end.join(' AND ')
160
+ end
161
+
162
+ def sanitize_array(*ary)
163
+ statement, *values = ary
164
+ if values.first.is_a?(Hash) && /:\w+/.match?(statement)
165
+ replace_named_bind_variables(statement, values.first)
166
+ elsif statement.include?('?')
167
+ replace_bind_variables(statement, values)
168
+ elsif statement.blank?
169
+ statement
170
+ else
171
+ statement % values.collect(&:to_s)
172
+ end
173
+ end
174
+
175
+ def replace_named_bind_variables(statement, bind_vars)
176
+ statement.gsub(/(:?):([a-zA-Z]\w*)/) do |match|
177
+ if bind_vars.include?(match = Regexp.last_match(2).to_sym)
178
+ "'#{bind_vars[match]}'"
179
+ else
180
+ raise ParseStatementInvalid, "missing value for :#{match} in #{statement}"
181
+ end
182
+ end
183
+ end
184
+
185
+ def replace_bind_variables(statement, values)
186
+ expected = statement.count('?')
187
+ provided = values.size
188
+ if expected != provided
189
+ raise ParseStatementInvalid, "wrong number of bind variables (#{provided} " \
190
+ "for #{expected}) in: #{statement}"
191
+ end
192
+ bound = values.dup
193
+ statement.gsub(/\?/) do
194
+ "'#{bound.shift}'"
195
+ end
196
+ end
197
+
141
198
  def method_missing(method, *args, &block)
142
199
  if @klass.respond_to?(method)
143
200
  scoping { @klass.public_send(method, *args, &block) }
@@ -0,0 +1,55 @@
1
+ # frozen_string_literal: true
2
+
3
+ require_relative 'scope_registry'
4
+ require_relative 'relation'
5
+
6
+ module Aliyun
7
+ module Log
8
+ module Record
9
+ module Scoping
10
+ extend ActiveSupport::Concern
11
+
12
+ module ClassMethods
13
+ delegate :load, :result, :count, to: :all
14
+ delegate :where, :query, :search, :sql, :from, :to,
15
+ :page, :line, :limit, :offset, to: :all
16
+ delegate :first, :last, :second, :third, :fourth, :fifth, :find_offset, to: :all
17
+
18
+ def current_scope
19
+ ScopeRegistry.value_for(:current_scope, self)
20
+ end
21
+
22
+ def current_scope=(scope)
23
+ ScopeRegistry.set_value_for(:current_scope, self, scope)
24
+ end
25
+
26
+ def scope(name, body)
27
+ raise ArgumentError, 'The scope body needs to be callable.' unless body.respond_to?(:call)
28
+
29
+ singleton_class.send(:define_method, name) do |*args|
30
+ scope = all
31
+ scope = scope.scoping { body.call(*args) }
32
+ scope
33
+ end
34
+ end
35
+
36
+ def unscoped
37
+ block_given? ? relation.scoping { yield } : relation
38
+ end
39
+
40
+ def all
41
+ scope = current_scope
42
+ scope ||= relation.from(0).to(Time.now.to_i)
43
+ scope
44
+ end
45
+
46
+ private
47
+
48
+ def relation
49
+ Relation.new(self)
50
+ end
51
+ end
52
+ end
53
+ end
54
+ end
55
+ end
@@ -0,0 +1,191 @@
1
+ # frozen_string_literal: true
2
+
3
+ require 'json'
4
+
5
+ module Aliyun
6
+ module Log
7
+ module Record
8
+ module TypeCasting
9
+ TYPE_MAPPING = {
10
+ text: :string,
11
+ long: :integer,
12
+ double: :bigdecimal,
13
+ json: :json
14
+ }.freeze
15
+
16
+ def self.cast_field(value, options)
17
+ options ||= {}
18
+ type = options[:cast_type]
19
+ type ||= TYPE_MAPPING[options[:type]]
20
+
21
+ return value if options.nil?
22
+ return nil if value.nil?
23
+
24
+ caster = Registry.lookup(type)
25
+ raise ArgumentError, "Unknown type #{options[:type]}" if caster.nil?
26
+
27
+ caster.new(options).cast(value)
28
+ end
29
+
30
+ def self.dump_field(value, options)
31
+ options ||= {}
32
+ type = options[:cast_type]
33
+ type ||= TYPE_MAPPING[options[:type]]
34
+
35
+ return value if options.nil?
36
+ return nil if value.nil?
37
+
38
+ dumper = Registry.lookup(type)
39
+ raise ArgumentError, "Unknown type #{options[:type]}" if dumper.nil?
40
+
41
+ dumper.new(options).dump(value)
42
+ end
43
+
44
+ class Value
45
+ def initialize(options)
46
+ @options = options
47
+ end
48
+
49
+ def cast(value)
50
+ value
51
+ end
52
+
53
+ def dump(value)
54
+ value
55
+ end
56
+ end
57
+
58
+ class StringType < Value; end
59
+
60
+ class DateType < Value
61
+ def cast(value)
62
+ return nil unless value.respond_to?(:to_date)
63
+
64
+ value.to_date
65
+ end
66
+
67
+ def dump(value)
68
+ if value.respond_to?(:to_date)
69
+ value.to_datetime.iso8601
70
+ else
71
+ value.to_s
72
+ end
73
+ end
74
+ end
75
+
76
+ class DateTimeType < Value
77
+ def cast(value)
78
+ return nil unless value.respond_to?(:to_datetime)
79
+
80
+ dt = begin
81
+ ::DateTime.parse(value)
82
+ rescue StandardError
83
+ nil
84
+ end
85
+ if dt
86
+ seconds = string_utc_offset(value) || 0
87
+ offset = seconds_to_offset(seconds)
88
+ ::DateTime.new(dt.year, dt.mon, dt.mday, dt.hour, dt.min, dt.sec, offset)
89
+ else
90
+ value.to_datetime
91
+ end
92
+ end
93
+
94
+ def dump(value)
95
+ if value.respond_to?(:to_datetime)
96
+ value.to_datetime.iso8601
97
+ else
98
+ value.to_s
99
+ end
100
+ end
101
+
102
+ private
103
+
104
+ def string_utc_offset(string)
105
+ Date._parse(string)[:offset]
106
+ end
107
+
108
+ def seconds_to_offset(seconds)
109
+ ActiveSupport::TimeZone.seconds_to_utc_offset(seconds)
110
+ end
111
+ end
112
+
113
+ class IntegerType < Value
114
+ def cast(value)
115
+ if value == true
116
+ 1
117
+ elsif value == false
118
+ 0
119
+ elsif value.is_a?(String) && value.blank?
120
+ nil
121
+ elsif value.is_a?(Float) && !value.finite?
122
+ nil
123
+ elsif !value.respond_to?(:to_i)
124
+ nil
125
+ else
126
+ value.to_i
127
+ end
128
+ end
129
+ end
130
+
131
+ class BigDecimalType < Value
132
+ def cast(value)
133
+ if value == true
134
+ 1
135
+ elsif value == false
136
+ 0
137
+ elsif value.is_a?(Symbol)
138
+ value.to_s.to_d
139
+ elsif value.is_a?(String) && value.blank?
140
+ nil
141
+ elsif value.is_a?(Float) && !value.finite?
142
+ nil
143
+ elsif !value.respond_to?(:to_d)
144
+ nil
145
+ else
146
+ value.to_d
147
+ end
148
+ end
149
+ end
150
+
151
+ class JsonType < Value
152
+ def cast(value)
153
+ return value unless value.is_a?(String)
154
+
155
+ begin
156
+ ActiveSupport::JSON.decode(value)
157
+ rescue StandardError
158
+ nil
159
+ end
160
+ end
161
+
162
+ def dump(value)
163
+ ActiveSupport::JSON.encode(value) unless value.nil?
164
+ end
165
+ end
166
+
167
+ module Registry
168
+ module_function
169
+
170
+ @registrations = {}
171
+
172
+ def register(name, klass = nil)
173
+ @registrations[name.to_sym] = klass
174
+ end
175
+
176
+ def lookup(name)
177
+ name ||= :string
178
+ @registrations[name.to_sym]
179
+ end
180
+
181
+ register(:string, StringType)
182
+ register(:date, DateType)
183
+ register(:datetime, DateTimeType)
184
+ register(:bigdecimal, BigDecimalType)
185
+ register(:integer, IntegerType)
186
+ register(:json, JsonType)
187
+ end
188
+ end
189
+ end
190
+ end
191
+ end
@@ -2,6 +2,6 @@
2
2
 
3
3
  module Aliyun
4
4
  module Log
5
- VERSION = '0.2.6'
5
+ VERSION = '0.2.8'
6
6
  end
7
7
  end
metadata CHANGED
@@ -1,14 +1,14 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: aliyun-log
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.2.6
4
+ version: 0.2.8
5
5
  platform: ruby
6
6
  authors:
7
7
  - Yingce Liu
8
8
  autorequire:
9
9
  bindir: exe
10
10
  cert_chain: []
11
- date: 2020-06-09 00:00:00.000000000 Z
11
+ date: 2020-06-12 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: activemodel
@@ -137,6 +137,8 @@ files:
137
137
  - lib/aliyun/log/record/persistence.rb
138
138
  - lib/aliyun/log/record/relation.rb
139
139
  - lib/aliyun/log/record/scope_registry.rb
140
+ - lib/aliyun/log/record/scoping.rb
141
+ - lib/aliyun/log/record/type_casting.rb
140
142
  - lib/aliyun/log/request.rb
141
143
  - lib/aliyun/log/server_error.rb
142
144
  - lib/aliyun/version.rb