betterplace-bi 0.7.0

Sign up to get free protection for your applications and to get access to all the features.
Files changed (52) hide show
  1. checksums.yaml +7 -0
  2. data/.gitignore +10 -0
  3. data/.semaphore/semaphore.yml +26 -0
  4. data/.tool-versions +2 -0
  5. data/.utilsrc +26 -0
  6. data/Gemfile +5 -0
  7. data/README.md +76 -0
  8. data/Rakefile +37 -0
  9. data/VERSION +1 -0
  10. data/betterplace-bi.gemspec +39 -0
  11. data/lib/betterplace-bi.rb +1 -0
  12. data/lib/bi/ab_test_helper.rb +81 -0
  13. data/lib/bi/api.rb +115 -0
  14. data/lib/bi/commands/base.rb +11 -0
  15. data/lib/bi/commands/collector.rb +32 -0
  16. data/lib/bi/commands/connection.rb +25 -0
  17. data/lib/bi/commands/delete.rb +33 -0
  18. data/lib/bi/commands/serializer.rb +40 -0
  19. data/lib/bi/commands/update.rb +29 -0
  20. data/lib/bi/commands.rb +10 -0
  21. data/lib/bi/commands_job.rb +24 -0
  22. data/lib/bi/event.rb +52 -0
  23. data/lib/bi/planning_value_parser.rb +100 -0
  24. data/lib/bi/planning_value_validations.rb +15 -0
  25. data/lib/bi/railtie.rb +13 -0
  26. data/lib/bi/request_analyzer.rb +64 -0
  27. data/lib/bi/session_id.rb +11 -0
  28. data/lib/bi/shared_value.rb +102 -0
  29. data/lib/bi/tracking.rb +28 -0
  30. data/lib/bi/type_generator.rb +79 -0
  31. data/lib/bi/update_error.rb +4 -0
  32. data/lib/bi/updater.rb +61 -0
  33. data/lib/bi/version.rb +8 -0
  34. data/lib/bi.rb +27 -0
  35. data/lib/tasks/bime.rake +55 -0
  36. data/spec/bi/ab_test_helper_spec.rb +145 -0
  37. data/spec/bi/commands/collector_spec.rb +26 -0
  38. data/spec/bi/commands/connection_spec.rb +15 -0
  39. data/spec/bi/commands_job_spec.rb +24 -0
  40. data/spec/bi/commands_spec.rb +46 -0
  41. data/spec/bi/event_spec.rb +77 -0
  42. data/spec/bi/planning_value_parser_spec.rb +94 -0
  43. data/spec/bi/request_analyzer_spec.rb +75 -0
  44. data/spec/bi/shared_value_spec.rb +47 -0
  45. data/spec/bi/tracking_spec.rb +37 -0
  46. data/spec/bi/type_generator_spec.rb +44 -0
  47. data/spec/bi/updater_spec.rb +61 -0
  48. data/spec/bime_dir/.keep +0 -0
  49. data/spec/config/bi.yml +18 -0
  50. data/spec/spec_helper.rb +31 -0
  51. data/spec/support/models.rb +106 -0
  52. metadata +331 -0
@@ -0,0 +1,145 @@
1
+ require 'spec_helper'
2
+
3
+ describe BI::ABTestHelper do
4
+ before do
5
+ allow(Rails).to receive(:logger).and_return(double('logger').as_null_object)
6
+ end
7
+
8
+ let :helper do
9
+ double('helper').extend described_class
10
+ end
11
+
12
+ let :session do
13
+ double(id: 'deadbeef' * 5, loaded?: true)
14
+ end
15
+
16
+ let :request do
17
+ double(session: session, headers: {}, params: {}, user_agent: nil)
18
+ end
19
+
20
+ let :alternatives do
21
+ %w[ alt1 alt2 alt3 ]
22
+ end
23
+
24
+ describe '#ab_test' do
25
+ it 'can be overriden' do
26
+ allow(helper).to receive(:ab_test_override?).and_return 'alt2'
27
+ expect(helper.ab_test('foo', alternatives, request: request)).to eq 'alt2'
28
+ end
29
+
30
+ it 'can ignore certain users' do
31
+ expect(helper).to receive(:ab_test_ignore_user?).and_return true
32
+ expect(helper.ab_test('foo', alternatives, request: request)).to eq 'alt1'
33
+ end
34
+
35
+ it 'triggers loading of session unless loaded' do
36
+ unloaded_session = double(id: 'deadbeef' * 5, loaded?: false)
37
+ expect(unloaded_session).to receive(:load!)
38
+ allow(request).to receive(:session).and_return unloaded_session
39
+ expect(helper.ab_test('foo', alternatives, request: request)).to eq 'alt2'
40
+ end
41
+
42
+ it 'can choose alternatives based on session id' do
43
+ allow(helper).to receive(:session).and_return session
44
+ expect(helper.ab_test('foo', alternatives, request: request)).to eq 'alt2'
45
+ end
46
+
47
+ it 'does not default even without session_id' do
48
+ allow(request.session).to receive(:id)
49
+ allow(helper).to receive(:rand).and_return(666)
50
+ expect(helper.ab_test('foo', alternatives, request: request)).to eq 'alt1'
51
+ allow(helper).to receive(:rand).and_return(667)
52
+ expect(helper.ab_test('foo', alternatives, request: request)).to eq 'alt2'
53
+ end
54
+
55
+ it 'does not default even without session' do
56
+ allow(request).to receive(:session)
57
+ allow(helper).to receive(:rand).and_return(666)
58
+ expect(helper.ab_test('foo', alternatives, request: request)).to eq 'alt1'
59
+ allow(helper).to receive(:rand).and_return(667)
60
+ expect(helper.ab_test('foo', alternatives, request: request)).to eq 'alt2'
61
+ end
62
+
63
+ it 'can be called with block' do
64
+ allow(helper).to receive(:session).and_return session
65
+ chosen = nil
66
+ expect(helper.ab_test('foo', *alternatives, request: request) {
67
+ |choice| chosen = choice; :result
68
+ }).to eq :result
69
+ expect(chosen).to eq 'alt2'
70
+ end
71
+ end
72
+
73
+ describe '#ab_test_ignore_user?' do
74
+ it 'does not ignore users by default' do
75
+ expect(helper).not_to be_ab_test_ignore_user(request: request)
76
+ end
77
+
78
+ it 'does ignore some users' do
79
+ allow_any_instance_of(BI::RequestAnalyzer).to receive(:ignore?).and_return true
80
+ expect(helper).to be_ab_test_ignore_user(request: request)
81
+ end
82
+ end
83
+
84
+ describe '#ab_test_override?' do
85
+ it 'does not override by default' do
86
+ expect(helper).not_to be_ab_test_override('foo', alternatives, request: request)
87
+ end
88
+
89
+ it 'can be used to choose alternatives for all ab tests' do
90
+ allow(request).to receive(:params).and_return({
91
+ 'ab_test' => 'alternative'
92
+ })
93
+ expect(helper.ab_test_override?('foo', alternatives, request: request)).to eq 'alt2'
94
+ end
95
+
96
+ it 'can be used to choose alternatives for all ab tests with specific name' do
97
+ allow(request).to receive(:params).and_return({
98
+ 'ab_test' => 'alt3'
99
+ })
100
+ expect(helper.ab_test_override?('foo', alternatives, request: request)).to eq 'alt3'
101
+ end
102
+
103
+ it 'can be used to choose alternatives for a specific ab test' do
104
+ allow(request).to receive(:params).and_return({
105
+ 'ab_test' => { 'foo' => 'alternative' },
106
+ })
107
+ expect(helper.ab_test_override?('foo', alternatives, request: request)).to eq 'alt2'
108
+ expect(helper.ab_test_override?('bar', alternatives, request: request)).not_to eq 'alt2'
109
+ end
110
+
111
+ it 'can be used to choose alternatives with specific name' do
112
+ allow(request).to receive(:params).and_return({
113
+ 'ab_test' => { 'foo' => 'alt3' },
114
+ })
115
+ expect(helper.ab_test_override?('foo', alternatives, request: request)).to eq 'alt3'
116
+ expect(helper.ab_test_override?('bar', alternatives, request: request)).not_to eq 'alt3'
117
+ end
118
+ end
119
+
120
+ describe '#ab_test_override_alternative' do
121
+ it 'can choose the control' do
122
+ expect(helper.ab_test_override_alternative('control', alternatives, request: request)).to\
123
+ eq 'alt1'
124
+ end
125
+
126
+ it 'can choose the first alternative' do
127
+ expect(helper.ab_test_override_alternative('alternative', alternatives, request: request)).to\
128
+ eq 'alt2'
129
+ end
130
+
131
+ it 'can choose the nth alternative, starting to count from 0' do
132
+ expect(helper.ab_test_override_alternative('1', alternatives, request: request)).to\
133
+ eq 'alt2'
134
+ end
135
+
136
+ it 'defaults to control if there is no nth alternative' do
137
+ expect(helper.ab_test_override_alternative('42', alternatives, request: request)).to\
138
+ eq 'alt1'
139
+ end
140
+
141
+ it 'defaults to nil in any other case' do
142
+ expect(helper.ab_test_override_alternative('nix', alternatives, request)).to be_nil
143
+ end
144
+ end
145
+ end
@@ -0,0 +1,26 @@
1
+ require 'spec_helper'
2
+
3
+ describe BI::Commands::Collector do
4
+ let :collector do
5
+ described_class.new
6
+ end
7
+
8
+ it 'can check if job should be scheduled now' do
9
+ object = double(destroyed?: false)
10
+ expect(collector).not_to be_schedule_job(object)
11
+ collector.add(object, double.as_null_object)
12
+ expect(collector).to be_schedule_job(object)
13
+ end
14
+
15
+ it 'can add Update commands' do
16
+ object = double(destroyed?: false, id: 666)
17
+ expect(BI::Commands::Update).to receive(:new).and_call_original
18
+ collector.add(object, double.as_null_object)
19
+ end
20
+
21
+ it 'can add Delete commands' do
22
+ object = double(destroyed?: true, id: 666)
23
+ expect(BI::Commands::Delete).to receive(:new).and_call_original
24
+ collector.add(object, double.as_null_object)
25
+ end
26
+ end
@@ -0,0 +1,15 @@
1
+ require 'spec_helper'
2
+
3
+ describe BI::Commands::Connection do
4
+ describe '#connect_to_server' do
5
+ it 'creates one connection' do
6
+ expect(described_class).to receive(:create_connection).once.and_return double.as_null_object
7
+ connection = nil
8
+ described_class.connect_to_server do |c|
9
+ connection = c
10
+ expect(connection).to receive(:reset)
11
+ end
12
+ expect(connection).not_to be_nil
13
+ end
14
+ end
15
+ end
@@ -0,0 +1,24 @@
1
+ require 'spec_helper'
2
+
3
+ describe BI::CommandsJob do
4
+ let :job do
5
+ described_class.new
6
+ end
7
+
8
+ it 'connects to server for commands' do
9
+ expect(job).to receive(:connect_to_server)
10
+ job.perform :something
11
+ end
12
+
13
+ it 'does not connect to server without commands' do
14
+ expect(job).not_to receive(:connect_to_server)
15
+ job.perform
16
+ end
17
+
18
+ it 'performs all given commands on server' do
19
+ command = double
20
+ expect(command).to receive(:perform_via)
21
+ allow(job).to receive(:create_connection).and_return(double.as_null_object)
22
+ job.perform command
23
+ end
24
+ end
@@ -0,0 +1,46 @@
1
+ require 'spec_helper'
2
+
3
+ describe BI::Commands do
4
+ before do
5
+ allow(Log.instance).to receive(:info)
6
+ end
7
+
8
+ let :object do
9
+ double
10
+ end
11
+
12
+ let :connection do
13
+ double
14
+ end
15
+
16
+ context BI::Commands::Update do
17
+ let :command do
18
+ described_class.new(value: object)
19
+ end
20
+
21
+ it 'can be configured' do
22
+ expect(command.value).to eq object
23
+ end
24
+
25
+ it 'can be performed' do
26
+ allow(object).to receive(:url)
27
+ expect(connection).to receive(:post).and_return double(status: 200)
28
+ command.perform_via(connection)
29
+ end
30
+ end
31
+
32
+ context BI::Commands::Delete do
33
+ let :command do
34
+ described_class.new(url: object)
35
+ end
36
+
37
+ it 'can be configured' do
38
+ expect(command.url).to eq object
39
+ end
40
+
41
+ it 'can be performed' do
42
+ expect(connection).to receive(:delete).and_return double(status: 200)
43
+ command.perform_via(connection)
44
+ end
45
+ end
46
+ end
@@ -0,0 +1,77 @@
1
+ require 'spec_helper'
2
+
3
+ describe BI::Event do
4
+ before do
5
+ allow(Rails).to receive(:logger).and_return(double('logger').as_null_object)
6
+ end
7
+
8
+ class ABEvent
9
+ class << self
10
+ def serialize(*)
11
+ end
12
+
13
+ def create!(*)
14
+ new
15
+ end
16
+ end
17
+
18
+ def attributes
19
+ {}
20
+ end
21
+
22
+ include BI::Tracking
23
+ end
24
+
25
+ let :session_id do
26
+ SecureRandom.hex(32)
27
+ end
28
+
29
+ let :session do
30
+ double('session', loaded?: true, id: session_id)
31
+ end
32
+
33
+ subject do
34
+ described_class.new
35
+ end
36
+
37
+ it 'offers static write method' do
38
+ args = { channel: 'default', name: 'hello', version: 'world', session: session }
39
+ expect(ABEvent).to receive(:create!).with(tracking: { session: session_id } | args).and_call_original
40
+ described_class.write(**args)
41
+ end
42
+
43
+ it 'can handle sessions with string id' do
44
+ args = { channel: 'default', name: 'hello', version: 'world', session: session_id }
45
+ expect(ABEvent).to receive(:create!).with(tracking: args).and_call_original
46
+ described_class.write(**args)
47
+ end
48
+
49
+ it 'ignores unexpected session objects' do
50
+ args = { channel: 'default', name: 'hello', version: 'world', session: Object.new }
51
+ expect(ABEvent).to receive(:create!).with(tracking: { session: nil } | args).and_call_original
52
+ described_class.write(**args)
53
+ end
54
+
55
+ it 'logs any errors during writing and raises again' do
56
+ error = StandardError.new
57
+ allow(ABEvent).to receive(:create!).and_raise error
58
+ expect(Log).to receive(:error).with(error, notify: true, meta: { module: 'bi' })
59
+ expect {
60
+ subject.write(channel: 'default', name: 'hello', version: 'world', session: session)
61
+ }.to raise_error error
62
+ end
63
+
64
+ it 'calls write method on configured event_class' do
65
+ event_data_input = {
66
+ channel: 'default', name: 'hello', version: 'world', session: session,
67
+ my: :data
68
+ }
69
+ event_data_output = {
70
+ channel: 'default', name: 'hello', version: 'world', session: session.id,
71
+ my: :data
72
+ }
73
+ expect(ABEvent).to receive(:create!).with(tracking: event_data_output).and_call_original
74
+ expect(Log).to receive(:info)
75
+ subject.write(**event_data_input)
76
+ end
77
+ end
@@ -0,0 +1,94 @@
1
+ require 'spec_helper'
2
+
3
+ describe BI::PlanningValueParser do
4
+ let :csv do
5
+ <<~end
6
+ product_tracking;amount_in_cents;yearmonth;data_type;data_version
7
+ foo;66600;2019-01;Foo;2018-06
8
+ bar;2300;2019-01;Bar;2018-06
9
+ end
10
+ end
11
+
12
+ let :instance do
13
+ described_class.parse(csv, class_name: 'TestPlanning')
14
+ end
15
+
16
+ context 'correct headers' do
17
+ it 'parses' do
18
+ expect(instance.first).to be_a TestPlanning
19
+ end
20
+ end
21
+
22
+ context 'invalid headers' do
23
+ let :csv do
24
+ <<~end
25
+ product_tracking;amount;yearmonth;data_type;data_version
26
+ foo;66600;2019-01;Foo;2018-06
27
+ end
28
+ end
29
+
30
+ it 'raises ParserError' do
31
+ expect(instance.errors).to eq(
32
+ 2 => 'headers need to be ["amount_in_cents", "data_type", "data_version", "product_tracking", "yearmonth"]',
33
+ )
34
+ end
35
+
36
+ it 'is not valid' do
37
+ expect(instance).to_not be_valid
38
+ end
39
+ end
40
+
41
+ context 'invalid #values' do
42
+ let :csv do
43
+ <<~end
44
+ product_tracking;amount;yearmonth;data_type;data_version
45
+ foo;66600;2019-01;Foo;2018-06;nope
46
+ end
47
+ end
48
+
49
+ it 'raises ParserError' do
50
+ expect(instance.errors).to eq(
51
+ 2 => '#values has to match #headers = 5'
52
+ )
53
+ end
54
+
55
+ it 'is not valid' do
56
+ expect(instance).to_not be_valid
57
+ end
58
+ end
59
+
60
+ context 'correct values' do
61
+ it 'parses' do
62
+ expect(instance.count).to eq 2
63
+ end
64
+ end
65
+
66
+ context 'incorrect values' do
67
+ let :csv do
68
+ <<~end
69
+ product_tracking;amount_in_cents;yearmonth;data_type;data_version
70
+ foo;66600;2019-13;Foo;2018-06
71
+ end
72
+ end
73
+
74
+ let :errors do
75
+ { 2 => [ "yearmonth: is invalid" ] }
76
+ end
77
+
78
+ before do
79
+ allow_any_instance_of(TestPlanning).to\
80
+ receive(:errors).and_return(errors)
81
+ allow(errors).to receive(:messages).and_return errors
82
+ end
83
+
84
+ it 'is not valid' do
85
+ expect(instance).to_not be_valid
86
+ end
87
+
88
+ it 'gives errorrs' do
89
+ expect(instance.errors).to eq(
90
+ 2 => "2: yearmonth: is invalid",
91
+ )
92
+ end
93
+ end
94
+ end
@@ -0,0 +1,75 @@
1
+ require 'spec_helper'
2
+
3
+ describe BI::RequestAnalyzer do
4
+ before do
5
+ described_class.mize_cache_clear
6
+ end
7
+
8
+ let :request do
9
+ double(
10
+ session: double,
11
+ user_agent: 'Agent Smith',
12
+ headers: {},
13
+ )
14
+ end
15
+
16
+ subject do
17
+ described_class.new(request)
18
+ end
19
+
20
+ it 'can figure out user_agent' do
21
+ expect(subject.user_agent).to eq 'Agent Smith'
22
+ end
23
+
24
+ it 'can ignore or not' do
25
+ expect(subject).not_to be_ignore
26
+ allow(subject).to receive(:preview?).and_return true
27
+ expect(subject).to be_ignore
28
+ allow(subject).to receive(:robot?).and_return true
29
+ expect(subject).to be_ignore
30
+ end
31
+
32
+ it 'can figure out if request is a preview' do
33
+ request.headers['x-purpose'] = 'preview'
34
+ expect(subject).to be_preview
35
+ end
36
+
37
+ context 'mobile' do
38
+ it 'attempts to categorize mobile devices' do
39
+ allow(subject).to receive(:user_agent_configuration).and_return\
40
+ double(mobile?: %w[ Foo Mobile Baz ].map { |x| [x] * 2 }.to_h)
41
+ allow(request).to receive(:user_agent).and_return\
42
+ 'Mozilla/5.0 (Linux; Android 6.0.1; SM-G920V Build/MMB29K) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/52.0.2743.98 Mobile Safari/537.36'
43
+ expect(subject).to be_mobile
44
+ expect(subject.device_type).to eq :mobile
45
+ end
46
+
47
+ it 'attempts to categorize desktop devices' do
48
+ allow(request).to receive(:user_agent).and_return\
49
+ 'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/42.0.2311.135 Safari/537.36 Edge/12.246'
50
+ expect(subject).not_to be_mobile
51
+ expect(subject.device_type).to eq :desktop
52
+ end
53
+ end
54
+
55
+ context 'robot' do
56
+ before do
57
+ allow(subject).to receive(:user_agent_configuration).and_return\
58
+ double(mobile?: nil, robot?: %w[ Foo Slurp Baz ].map { |x| [x] * 2 }.to_h)
59
+ end
60
+
61
+ it 'attempts to categorize non robots' do
62
+ allow(request).to receive(:user_agent).and_return\
63
+ 'Mozilla/5.0 (Macintosh; Intel Mac OS X 10_11_2) AppleWebKit/601.3.9 (KHTML, like Gecko) Version/9.0.2 Safari/601.3.9'
64
+ expect(subject).not_to be_robot
65
+ expect(subject.device_type).to eq :desktop
66
+ end
67
+
68
+ it 'attempts to categorize robots' do
69
+ allow(request).to receive(:user_agent).and_return\
70
+ 'Mozilla/5.0 (compatible; Yahoo! Slurp; http://help.yahoo.com/help/us/ysearch/slurp)'
71
+ expect(subject).to be_robot
72
+ expect(subject.device_type).to eq :bot
73
+ end
74
+ end
75
+ end
@@ -0,0 +1,47 @@
1
+ require 'spec_helper'
2
+
3
+ describe BI::SharedValue do
4
+ let :test_value do
5
+ BI::TestModelValue.new('foo')
6
+ end
7
+
8
+ it 'can warp a model' do
9
+ tv = BI::TestModelValue.new(TestModel.new('foo'))
10
+ expect(tv).to be_a described_class
11
+ expect(tv.id).to eq 'foo'
12
+ end
13
+
14
+ it 'can be build for a model with globalid' do
15
+ tv = BI::TestModelValue.find('foo')
16
+ expect(tv).to be_a described_class
17
+ expect(tv.id).to eq 'foo'
18
+ end
19
+
20
+ it 'can intialize for a model found via globalid' do
21
+ tv = BI::TestModelValue.new('foo')
22
+ expect(tv).to be_a described_class
23
+ expect(tv.id).to eq 'foo'
24
+ end
25
+
26
+ it 'haz endpoint url' do
27
+ expect(test_value.url).to eq '/api/v1/tests'
28
+ end
29
+
30
+ it 'can be converted to JSON value' do
31
+ Time.dummy('2011-11-11T11:11:11+01:00') do
32
+ expect(JSON(test_value)).to eq \
33
+ '{"id":"foo","number":10000000000,"created_at":"2011-11-11T11:11:11.000000Z","tags":["foo","bar"]}'
34
+ end
35
+ end
36
+
37
+ it 'can be output as string' do
38
+ expect(test_value.to_s).to eq "#<BI::TestModelValue type=TestModel id=foo>"
39
+ end
40
+
41
+ it 'can clear the models from bi' do
42
+ connection = double
43
+ expect(BI::Commands::Connection).to receive(:connect_to_server).and_yield(connection)
44
+ expect_any_instance_of(BI::Commands::Delete).to receive(:perform_via).with(connection)
45
+ test_value.class.clear
46
+ end
47
+ end
@@ -0,0 +1,37 @@
1
+ require 'spec_helper'
2
+
3
+ describe BI::Tracking do
4
+ let :tracked do
5
+ Class.new do
6
+ class << self
7
+ def serialize(*)
8
+ end
9
+ end
10
+
11
+ include BI::Tracking
12
+
13
+ def tracking
14
+ @tracking ||= {}
15
+ end
16
+
17
+ self
18
+ end.new
19
+ end
20
+
21
+ it 'can track foos' do
22
+ expect(tracked.respond_to?(:tracking_foo)).to eq true
23
+ tracked.tracking_foo = :foo
24
+ expect(tracked.tracking_foo).to eq :foo
25
+ end
26
+
27
+ it 'can have a tracking_hash' do
28
+ tracked.tracking_foo = :foo
29
+ expect(tracked.tracking_hash).to eq(foo: :foo)
30
+ end
31
+
32
+ it 'does not answer non_tracking tings' do
33
+ expect {
34
+ tracked.non_tracking_foo = :foo
35
+ }.to raise_error NoMethodError
36
+ end
37
+ end
@@ -0,0 +1,44 @@
1
+ require 'spec_helper'
2
+ require 'fileutils'
3
+
4
+ describe BI::TypeGenerator do
5
+ before do
6
+ allow_any_instance_of(described_class).to receive(:gofmt)
7
+ end
8
+
9
+ let :go_file do
10
+ Pathname.new(cc.bi.bime_dir).join('test_model.go')
11
+ end
12
+
13
+ after do
14
+ FileUtils.rm_rf go_file
15
+ end
16
+
17
+ it 'Can generate a GO types according to spec in a BI::*Value' do
18
+ described_class.new.generate
19
+ expect(File.read(go_file)).to eq(<<~end)
20
+ package bime
21
+
22
+ import (
23
+ "time"
24
+ "github.com/lib/pq"
25
+ )
26
+
27
+ type TestModel struct {
28
+ Id string `json:"id" gorm:"type:uuid;primary_key"`
29
+ Number *int64 `json:"number" gorm:"type:bigint"`
30
+ CreatedAt *time.Time `json:"created_at" gorm:"-" timestamp:"split"`
31
+ CreatedAtLocal *time.Time `json:"-" gorm:"type:timestamp;index"`
32
+ CreatedAtYearLocal *int `json:"-" gorm:"type:int;index"`
33
+ CreatedAtMonthLocal *int `json:"-" gorm:"type:int;index"`
34
+ CreatedAtDayLocal *int `json:"-" gorm:"type:int;index"`
35
+ CreatedAtHourLocal *int `json:"-" gorm:"type:int;index"`
36
+ CreatedAtCWLocal *int `json:"-" gorm:"type:int;index"`
37
+ CreatedAtDateLocal *string `json:"-" gorm:"type:varchar;index"`
38
+ CreatedAtYearMonthLocal *string `json:"-" gorm:"type:varchar;index"`
39
+ CreatedAtYearCWLocal *string `json:"-" gorm:"type:varchar;index"`
40
+ Tags pq.StringArray `json:"tags" gorm:"type:varchar[]"`
41
+ }
42
+ end
43
+ end
44
+ end