arql 0.3.29 → 0.3.30

Sign up to get free protection for your applications and to get access to all the features.
@@ -0,0 +1,216 @@
1
+ * 定义快速按时间查询的便利方法
2
+
3
+ ** 问题
4
+
5
+ 对于按照时间范围查询的需求,一般需要构造一个表示时间范围的 Range 对象,例如:
6
+
7
+ #+BEGIN_SRC ruby
8
+ Student.where(created_at: Time.now.beginning_of_day..Time.now.end_of_day)
9
+ #+END_SRC
10
+
11
+ 或者
12
+
13
+ #+BEGIN_SRC ruby
14
+ Student.where(created_at: Time.new(2016, 1, 1)..Time.new(2016, 12, 31))
15
+ #+END_SRC
16
+
17
+ ** 定义一个便利方法
18
+
19
+ 为了简化构造时间 Range 对象的过程,我们可以定义一个便利方法,例如:
20
+
21
+ 创建一个文件 =~/.arql.d/date_range.rb= ,内容如下:
22
+
23
+ #+BEGIN_SRC ruby
24
+ module Kernel
25
+
26
+ # == Example:
27
+ # Range
28
+ # 1. -3..0
29
+ # 2. '27'..'29'
30
+ # 3. '04-01'..'04-10'
31
+ # 4. '2021-04-01'..'2021-04-10'
32
+ # Array
33
+ # 1. [nil, nil] or [] => 1970-01-01 ~ 1000 days later from now
34
+ # 2. [:yday, :today] or [:yesterday, :today]
35
+ # 3. [-10.months, :today]
36
+ # Others
37
+ # 1. Time.now or Date.today
38
+ # 2. :now or :today
39
+ # 3. :yesterday or :yday
40
+ # 4. -2
41
+ # 5. -2.months
42
+ def dates_range(dates)
43
+ dates = :yesterday if dates == :yday
44
+ if dates.is_a?(Range)
45
+ s = if dates.begin.is_a?(Integer)
46
+ (Time.now+dates.begin.days).beginning_of_day
47
+ elsif dates.begin.is_a?(String) && dates.begin =~ /^\d{4}-\d{2}-\d{2}$/
48
+ Time.parse(dates.begin).beginning_of_day
49
+ elsif dates.begin.is_a?(String) && dates.begin =~ /^\d{2}-\d{2}$/
50
+ Time.strptime(dates.begin, '%m-%d').beginning_of_day
51
+ elsif dates.begin.is_a?(String) && dates.begin =~ /^\d{2}$/
52
+ Time.strptime(dates.begin, '%d').beginning_of_day
53
+ else
54
+ dates.begin
55
+ end
56
+ e = if dates.end.is_a?(Integer)
57
+ (Time.now+dates.end.days).end_of_day
58
+ elsif dates.end.is_a?(String) && dates.end =~ /^\d{4}-\d{2}-\d{2}$/
59
+ Time.parse(dates.end).end_of_day
60
+ elsif dates.end.is_a?(String) && dates.end =~ /^\d{2}-\d{2}$/
61
+ Time.strptime(dates.end, '%m-%d').end_of_day
62
+ elsif dates.end.is_a?(String) && dates.end =~ /^\d{2}$/
63
+ Time.strptime(dates.end, '%d').end_of_day
64
+ else
65
+ dates.end
66
+ end
67
+ s..e
68
+ elsif dates.is_a?(Array)
69
+ if dates.size == 2
70
+ s = dates.first || Date.new(1970, 1, 1)
71
+ s = if s.is_a?(Time) || s.is_a?(Date)
72
+ s.beginning_of_day
73
+ elsif s == :now
74
+ Time.now.beginning_of_day
75
+ elsif s == :today
76
+ Date.today.beginning_of_day
77
+ elsif s == :yesterday || s == :yday
78
+ Date.yesterday.beginning_of_day
79
+ elsif s.in?(Time.instance_methods)
80
+ Time.now.send(s).beginning_of_day
81
+ elsif s.is_a?(ActiveSupport::Duration)
82
+ (Time.now+s).beginning_of_day
83
+ elsif s.is_a?(Integer)
84
+ (Time.now+s.days).beginning_of_day
85
+ else
86
+ raise "Not supported s: #{s}"
87
+ end
88
+ e = dates.last || Date.today + 1000.days
89
+ e = if e.is_a?(Time) || e.is_a?(Date)
90
+ e.end_of_day
91
+ elsif e == :now
92
+ Time.now.end_of_day
93
+ elsif e == :today
94
+ Date.today.end_of_day
95
+ elsif e == :yesterday || e == :yday
96
+ Date.yesterday.end_of_day
97
+ elsif e.in?(Time.instance_methods)
98
+ Time.now.send(e).end_of_day
99
+ elsif e.is_a?(ActiveSupport::Duration)
100
+ (Time.now+e).end_of_day
101
+ elsif e.is_a?(Integer)
102
+ (Time.now+e.days).beginning_of_day
103
+ else
104
+ raise "Not supported e: #{e}"
105
+ end
106
+ return s..e
107
+ else
108
+ times = dates.map do |date|
109
+ if date.nil?
110
+ nil
111
+ elsif date.is_a?(Time)
112
+ date
113
+ elsif date == :now
114
+ Time.now
115
+ elsif date.in?(Time.instance_methods)
116
+ Time.now.send(date)
117
+ elsif date.is_a?(ActiveSupport::Duration)
118
+ Time.now+date
119
+ elsif date.is_a?(Integer)
120
+ (Time.now+date.days).beginning_of_day
121
+ else
122
+ raise "Not supported date: #{date}"
123
+ end
124
+ end
125
+ s = times.first || Time.new(1970, 1, 1, 0, 0, 0)
126
+ e = times.last || Time.now + 1000.days
127
+ return s..e
128
+ end
129
+ else
130
+ if dates.is_a?(Time) or dates.is_a?(Date)
131
+ dates = dates.beginning_of_day..dates.end_of_day
132
+ elsif dates == :now
133
+ dates = Time.now.beginning_of_day..Time.now.end_of_day
134
+ elsif dates == :today
135
+ dates = Date.today.beginning_of_day..Date.today.end_of_day
136
+ elsif dates == :yday || dates == :yesterday
137
+ dates = Date.yesterday.beginning_of_day..Date.yesterday.end_of_day
138
+ elsif dates.in?(Time.instance_methods)
139
+ dates = Time.now.send(dates).beginning_of_day..Time.now.send(dates).end_of_day
140
+ elsif dates.is_a?(ActiveSupport::Duration)
141
+ dates = (Time.now+dates).beginning_of_day..(Time.now+dates).end_of_day
142
+ elsif dates.is_a?(Integer)
143
+ (Time.now+dates.days).beginning_of_day..(Time.now+dates.days).end_of_day
144
+ else
145
+ raise "Not supported dates: #{dates}"
146
+ end
147
+ end
148
+ end
149
+
150
+ alias_method :dates, :dates_range
151
+ end
152
+
153
+ class ::ArqlModel
154
+
155
+ class << self
156
+ def ts_attribute_for_create
157
+ (timestamp_attributes_for_create||[]).find { |e| e.in?(column_names) }
158
+ end
159
+
160
+ def ts_attribute_for_update
161
+ (timestamp_attributes_for_update||[]).find { |e| e.in?(column_names) }
162
+ end
163
+
164
+
165
+ def created_on(dates)
166
+ attr = ts_attribute_for_create
167
+ raise 'No attrtibute for create defined' unless attr
168
+ where(attr => dates_range(dates))
169
+ end
170
+
171
+ alias on created_on
172
+
173
+ def today
174
+ created_on(0)
175
+ end
176
+
177
+ def modified_on(dates)
178
+ attr = ts_attribute_for_update
179
+ raise 'No attrtibute for update defined' unless attr
180
+ where(attr: dates_range(dates))
181
+ end
182
+ end
183
+ end
184
+ #+END_SRC
185
+
186
+ 然后在 =~/.arql.d/init.rb= 中引入这个文件:
187
+
188
+ #+BEGIN_SRC ruby
189
+ load(File.absolute_path(File.dirname(__FILE__) + "/date_range.rb"))
190
+ #+END_SRC
191
+
192
+
193
+ ** 使用方法
194
+
195
+ 然后就可以使用这个方法了:
196
+
197
+ #+BEGIN_SRC ruby
198
+ Student.where(created_at: dates(0)). # 查询今天创建的
199
+ Student.where(created_at: dates(:today)). # 查询今天创建的
200
+ Student.where(created_at: dates('2016-01-01'..'2016-01-31')) # 查询 2016 年 1 月创建的
201
+ Student.where(created_at: dates('01'..'10')) # 查询当月 1-10 号创建的
202
+ Student.where(created_at: dates('03-01'..'04-10')) # 查询当年 3 月 1 日到 4 月 10 日创建的
203
+ Student.where(created_at: dates(-20..-1)) # 查询 20 天前到昨天创建的
204
+ #+END_SRC
205
+
206
+ 如果是对 created_at 字段 (在 =init.yaml= 中通过 =created_at= 配置,默认值是 =created_at=) 进行查询, 可以直接使用:
207
+
208
+ #+BEGIN_SRC ruby
209
+ Student.on(0). # 查询今天创建的
210
+ Student.on(:today). # 查询今天创建的
211
+ Student.on('2016-01-01'..'2016-01-31') # 查询 2016 年 1 月创建的
212
+ Student.on('01'..'10') # 查询当月 1-10 号创建的
213
+ Student.on('03-01'..'04-10') # 查询当年 3 月 1 日到 4 月 10 日创建的
214
+ Student.on(-20..-1) # 查询 20 天前到昨天创建的
215
+ #+END_SRC
216
+
@@ -0,0 +1,218 @@
1
+ * Define helper function to create datetime range objects
2
+
3
+ ** Problem
4
+
5
+ When we need to query records within a time range, we need to construct a Range object representing the time range, for example:
6
+
7
+ #+BEGIN_SRC ruby
8
+ Student.where(created_at: Time.now.beginning_of_day..Time.now.end_of_day)
9
+ #+END_SRC
10
+
11
+ or
12
+
13
+ #+BEGIN_SRC ruby
14
+ Student.where(created_at: Time.new(2016, 1, 1)..Time.new(2016, 12, 31))
15
+ #+END_SRC
16
+
17
+ ** Define a helper function
18
+
19
+ To simplify the process of constructing a time Range object, we can define a helper function, for example:
20
+
21
+ Create a file =~/.arql.d/date_range.rb= with the following content:
22
+
23
+ #+BEGIN_SRC ruby
24
+ module Kernel
25
+
26
+ # == Example:
27
+ # Range
28
+ # 1. -3..0
29
+ # 2. '27'..'29'
30
+ # 3. '04-01'..'04-10'
31
+ # 4. '2021-04-01'..'2021-04-10'
32
+ # Array
33
+ # 1. [nil, nil] or [] => 1970-01-01 ~ 1000 days later from now
34
+ # 2. [:yday, :today] or [:yesterday, :today]
35
+ # 3. [-10.months, :today]
36
+ # Others
37
+ # 1. Time.now or Date.today
38
+ # 2. :now or :today
39
+ # 3. :yesterday or :yday
40
+ # 4. -2
41
+ # 5. -2.months
42
+ def dates_range(dates)
43
+ dates = :yesterday if dates == :yday
44
+ if dates.is_a?(Range)
45
+ s = if dates.begin.is_a?(Integer)
46
+ (Time.now+dates.begin.days).beginning_of_day
47
+ elsif dates.begin.is_a?(String) && dates.begin =~ /^\d{4}-\d{2}-\d{2}$/
48
+ Time.parse(dates.begin).beginning_of_day
49
+ elsif dates.begin.is_a?(String) && dates.begin =~ /^\d{2}-\d{2}$/
50
+ Time.strptime(dates.begin, '%m-%d').beginning_of_day
51
+ elsif dates.begin.is_a?(String) && dates.begin =~ /^\d{2}$/
52
+ Time.strptime(dates.begin, '%d').beginning_of_day
53
+ else
54
+ dates.begin
55
+ end
56
+ e = if dates.end.is_a?(Integer)
57
+ (Time.now+dates.end.days).end_of_day
58
+ elsif dates.end.is_a?(String) && dates.end =~ /^\d{4}-\d{2}-\d{2}$/
59
+ Time.parse(dates.end).end_of_day
60
+ elsif dates.end.is_a?(String) && dates.end =~ /^\d{2}-\d{2}$/
61
+ Time.strptime(dates.end, '%m-%d').end_of_day
62
+ elsif dates.end.is_a?(String) && dates.end =~ /^\d{2}$/
63
+ Time.strptime(dates.end, '%d').end_of_day
64
+ else
65
+ dates.end
66
+ end
67
+ s..e
68
+ elsif dates.is_a?(Array)
69
+ if dates.size == 2
70
+ s = dates.first || Date.new(1970, 1, 1)
71
+ s = if s.is_a?(Time) || s.is_a?(Date)
72
+ s.beginning_of_day
73
+ elsif s == :now
74
+ Time.now.beginning_of_day
75
+ elsif s == :today
76
+ Date.today.beginning_of_day
77
+ elsif s == :yesterday || s == :yday
78
+ Date.yesterday.beginning_of_day
79
+ elsif s.in?(Time.instance_methods)
80
+ Time.now.send(s).beginning_of_day
81
+ elsif s.is_a?(ActiveSupport::Duration)
82
+ (Time.now+s).beginning_of_day
83
+ elsif s.is_a?(Integer)
84
+ (Time.now+s.days).beginning_of_day
85
+ else
86
+ raise "Not supported s: #{s}"
87
+ end
88
+ e = dates.last || Date.today + 1000.days
89
+ e = if e.is_a?(Time) || e.is_a?(Date)
90
+ e.end_of_day
91
+ elsif e == :now
92
+ Time.now.end_of_day
93
+ elsif e == :today
94
+ Date.today.end_of_day
95
+ elsif e == :yesterday || e == :yday
96
+ Date.yesterday.end_of_day
97
+ elsif e.in?(Time.instance_methods)
98
+ Time.now.send(e).end_of_day
99
+ elsif e.is_a?(ActiveSupport::Duration)
100
+ (Time.now+e).end_of_day
101
+ elsif e.is_a?(Integer)
102
+ (Time.now+e.days).beginning_of_day
103
+ else
104
+ raise "Not supported e: #{e}"
105
+ end
106
+ return s..e
107
+ else
108
+ times = dates.map do |date|
109
+ if date.nil?
110
+ nil
111
+ elsif date.is_a?(Time)
112
+ date
113
+ elsif date == :now
114
+ Time.now
115
+ elsif date.in?(Time.instance_methods)
116
+ Time.now.send(date)
117
+ elsif date.is_a?(ActiveSupport::Duration)
118
+ Time.now+date
119
+ elsif date.is_a?(Integer)
120
+ (Time.now+date.days).beginning_of_day
121
+ else
122
+ raise "Not supported date: #{date}"
123
+ end
124
+ end
125
+ s = times.first || Time.new(1970, 1, 1, 0, 0, 0)
126
+ e = times.last || Time.now + 1000.days
127
+ return s..e
128
+ end
129
+ else
130
+ if dates.is_a?(Time) or dates.is_a?(Date)
131
+ dates = dates.beginning_of_day..dates.end_of_day
132
+ elsif dates == :now
133
+ dates = Time.now.beginning_of_day..Time.now.end_of_day
134
+ elsif dates == :today
135
+ dates = Date.today.beginning_of_day..Date.today.end_of_day
136
+ elsif dates == :yday || dates == :yesterday
137
+ dates = Date.yesterday.beginning_of_day..Date.yesterday.end_of_day
138
+ elsif dates.in?(Time.instance_methods)
139
+ dates = Time.now.send(dates).beginning_of_day..Time.now.send(dates).end_of_day
140
+ elsif dates.is_a?(ActiveSupport::Duration)
141
+ dates = (Time.now+dates).beginning_of_day..(Time.now+dates).end_of_day
142
+ elsif dates.is_a?(Integer)
143
+ (Time.now+dates.days).beginning_of_day..(Time.now+dates.days).end_of_day
144
+ else
145
+ raise "Not supported dates: #{dates}"
146
+ end
147
+ end
148
+ end
149
+
150
+ alias_method :dates, :dates_range
151
+ end
152
+
153
+ class ::ArqlModel
154
+
155
+ class << self
156
+ def ts_attribute_for_create
157
+ (timestamp_attributes_for_create||[]).find { |e| e.in?(column_names) }
158
+ end
159
+
160
+ def ts_attribute_for_update
161
+ (timestamp_attributes_for_update||[]).find { |e| e.in?(column_names) }
162
+ end
163
+
164
+
165
+ def created_on(dates)
166
+ attr = ts_attribute_for_create
167
+ raise 'No attrtibute for create defined' unless attr
168
+ where(attr => dates_range(dates))
169
+ end
170
+
171
+ alias on created_on
172
+
173
+ def today
174
+ created_on(0)
175
+ end
176
+
177
+ def modified_on(dates)
178
+ attr = ts_attribute_for_update
179
+ raise 'No attrtibute for update defined' unless attr
180
+ where(attr: dates_range(dates))
181
+ end
182
+ end
183
+ end
184
+ #+END_SRC
185
+
186
+
187
+ Then include this file in =~/.arql.d/init.rb=:
188
+
189
+ #+BEGIN_SRC ruby
190
+ load(File.absolute_path(File.dirname(__FILE__) + "/date_range.rb"))
191
+ #+END_SRC
192
+
193
+
194
+ ** Usage
195
+
196
+ Then you can use this method:
197
+
198
+ #+BEGIN_SRC ruby
199
+ Student.where(created_at: dates(0)). # Query records created today
200
+ Student.where(created_at: dates(:today)). # Query records created today
201
+ Student.where(created_at: dates('2016-01-01'..'2016-01-31')) # Query records created in January 2016
202
+ Student.where(created_at: dates('01'..'10')) # Query records created on the 1st to the 10th of the current month
203
+ Student.where(created_at: dates('03-01'..'04-10')) # Query records created from March 1st to April 10th of the current year
204
+ Student.where(created_at: dates(-20..-1)) # Query records created 20 days ago to yesterday
205
+ #+END_SRC
206
+
207
+ If you are querying the =created_at= field (configured in =init.yaml= with =created_at=, the default value is =created_at=), you can use:
208
+
209
+ #+BEGIN_SRC ruby
210
+ Student.on(0). # Query records created today
211
+ Student.on(:today). # Query records created today
212
+ Student.on('2016-01-01'..'2016-01-31') # Query records created in January 2016
213
+ Student.on('01'..'10') # Query records created on the 1st to the 10th of the current month
214
+ Student.on('03-01'..'04-10') # Query records created from March 1st to April 10th of the current year
215
+ Student.on(-20..-1) # Query records created 20 days ago to yesterday
216
+ #+END_SRC
217
+
218
+
@@ -0,0 +1,33 @@
1
+ * 将不同环境的初始化代码放在不同的文件中
2
+
3
+ 配置文件中往往有多个数据库的多个环境配置,这时候可以使用这里的方法,将不同环境的初始化代码放在不同的文件中来避免冲突。
4
+
5
+ 假设配置文件中配置了 4 个数据库环境:
6
+
7
+ + apollo.dev
8
+ + apollo.prod
9
+ + space.dev
10
+ + space.prod
11
+
12
+ 那么可以在 =~/.arql.d/= 目录下创建以下文件:
13
+
14
+ + apollo.rb
15
+ + space.rb
16
+
17
+ 在 =apollo.rb= 文件中放置 apollo 项目的初始化代码; 在 =space.rb= 文件中放置 space 项目的初始化代码。
18
+
19
+ 然后在 =~/.arql.d/init.eb= 文件中写入以下代码:
20
+
21
+ #+BEGIN_SRC ruby
22
+ ["apollo", "space"].each do |project|
23
+ if Arql::App.env.try { |e| e.include?(project + ".") }
24
+ load(File.absolute_path(File.dirname(__FILE__) + "/#{project}.rb"))
25
+ break
26
+ end
27
+ end
28
+ #+END_SRC
29
+
30
+ 这样,当执行 =arql -e apollo.dev= 或 =arql =e apollo.prod= 时,就会加载 =apollo.rb= 文件中的初始化代码;当执行 =arql
31
+ -e space.dev= 或 =arql -e space.prod= 时,就会加载 =space.rb= 文件中的初始化代码。
32
+
33
+
@@ -0,0 +1,31 @@
1
+ * Place your initialization code in a file named after the environment
2
+
3
+ If you have multiple environments in your configuration file, you can place the initialization code for each environment in a separate file to avoid conflicts.
4
+
5
+ Suppose you have 4 database environments in your configuration file:
6
+
7
+ + apollo.dev
8
+ + apollo.prod
9
+ + space.dev
10
+ + space.prod
11
+
12
+ You can create the following files in the =~/.arql.d/= directory:
13
+
14
+ + apollo.rb
15
+ + space.rb
16
+
17
+ Place the initialization code for the apollo project in the =apollo.rb= file; place the initialization code for the space project in the =space.rb= file.
18
+
19
+ Then write the following code in the =~/.arql.d/init.eb= file:
20
+
21
+ #+BEGIN_SRC ruby
22
+ ["apollo", "space"].each do |project|
23
+ if Arql::App.env.try { |e| e.include?(project + ".") }
24
+ load(File.absolute_path(File.dirname(__FILE__) + "/#{project}.rb"))
25
+ break
26
+ end
27
+ end
28
+ #+END_SRC
29
+
30
+ Now, when you run =arql -e apollo.dev= or =arql =e apollo.prod=, the initialization code in the =apollo.rb= file will be loaded; when you run =arql -e space.dev= or =arql -e space.prod=, the initialization code in the =space.rb= file will be loaded.
31
+
data/lib/arql/cli.rb CHANGED
@@ -36,7 +36,7 @@ module Arql
36
36
  @options.env = env
37
37
  end
38
38
 
39
- opts.on('-aDB_ADAPTER', '--db-adapter=DB_ADAPTER', 'Specify database Adapter, default is mysql2') do |db_adapter|
39
+ opts.on('-aDB_ADAPTER', '--db-adapter=DB_ADAPTER', 'Specify database Adapter, default is sqlite3') do |db_adapter|
40
40
  @options.adapter = db_adapter
41
41
  end
42
42
 
@@ -119,7 +119,11 @@ module Arql
119
119
  tables.each do |table_name|
120
120
  table_comment = comments[table_name]
121
121
  primary_keys[table_name].tap do |pkey|
122
- table_name.send(@@classify_method).tap do |const_name|
122
+ table_name_prefixes = options[:table_name_prefixes] || []
123
+ normalized_table_name = table_name_prefixes.each_with_object(table_name.dup) do |prefix, name|
124
+ name.sub!(/^#{prefix}/, '')
125
+ end
126
+ normalized_table_name.send(@@classify_method).tap do |const_name|
123
127
  const_name = 'Modul' if const_name == 'Module'
124
128
  const_name = 'Clazz' if const_name == 'Class'
125
129
  Class.new(::ArqlModel) do
@@ -213,7 +217,11 @@ module Arql
213
217
  tables.each do |table_name|
214
218
  table_comment = comments[table_name]
215
219
  primary_keys[table_name].tap do |pkey|
216
- table_name.send(@@classify_method).tap do |const_name|
220
+ table_name_prefixes = options[:table_name_prefixes] || []
221
+ normalized_table_name = table_name_prefixes.each_with_object(table_name.dup) do |prefix, name|
222
+ name.sub!(/^#{prefix}/, '')
223
+ end
224
+ normalized_table_name.send(@@classify_method).tap do |const_name|
217
225
  const_name = 'Modul' if const_name == 'Module'
218
226
  const_name = 'Clazz' if const_name == 'Class'
219
227
  if const_name !~ /^[A-Z][A-Za-z0-9_]*$/
@@ -359,4 +367,4 @@ module Arql
359
367
  end
360
368
  end
361
369
  end
362
- end
370
+ end
data/lib/arql/version.rb CHANGED
@@ -1,3 +1,3 @@
1
1
  module Arql
2
- VERSION = "0.3.29"
2
+ VERSION = "0.3.30"
3
3
  end