better_record 0.1.0 → 0.1.1

Sign up to get free protection for your applications and to get access to all the features.
Files changed (30) hide show
  1. checksums.yaml +4 -4
  2. data/app/assets/stylesheets/scaffold.css +80 -0
  3. data/app/controllers/better_record/application_controller.rb +1 -0
  4. data/app/controllers/better_record/table_sizes_controller.rb +28 -0
  5. data/app/helpers/better_record/table_sizes_helper.rb +4 -0
  6. data/app/models/better_record/base.rb +16 -8
  7. data/app/models/better_record/current.rb +5 -0
  8. data/app/models/better_record/{audit.rb → logged_action.rb} +2 -2
  9. data/app/models/better_record/table_size.rb +20 -17
  10. data/{lib/better_record → config/initializers/active_record}/associations.rb +5 -8
  11. data/config/initializers/active_record/reflection.rb +21 -0
  12. data/config/initializers/boolean.rb +11 -0
  13. data/config/initializers/content_security_policy.rb +25 -0
  14. data/config/initializers/filter_parameter_logging.rb +10 -0
  15. data/config/initializers/inflections.rb +21 -0
  16. data/config/initializers/integer.rb +5 -0
  17. data/config/initializers/jazz_fingers.rb +7 -0
  18. data/config/initializers/mime_types.rb +4 -0
  19. data/config/initializers/money_type.rb +32 -0
  20. data/config/routes.rb +2 -0
  21. data/db/migrate/20180725160802_create_better_record_db_functions.rb +94 -0
  22. data/db/migrate/20180725201614_create_better_record_table_sizes.rb +24 -0
  23. data/db/postgres-audit-trigger.psql +347 -0
  24. data/lib/better_record.rb +14 -19
  25. data/lib/better_record/version.rb +1 -1
  26. data/lib/tasks/spec/attributes.rake +42 -0
  27. metadata +95 -9
  28. data/lib/generators/create_helper_functions/USAGE +0 -9
  29. data/lib/generators/create_helper_functions/create_helper_functions_generator.rb +0 -61
  30. data/lib/tasks/db/create_audits.rake +0 -0
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: b1f24408013032454f5f6384cfa1723d81fd5b741c544f35ce95352fe4c334a8
4
- data.tar.gz: 5476435865403fef0645f2502eaf10a412fe27d222b43568e5eb7f557f60f558
3
+ metadata.gz: 714bdfe5593dca0ec99aa991dc68e1c432f2ff730ad8c78bad43d6172714b10c
4
+ data.tar.gz: 05523c1546ac5d85673b2f8d9473dc2371f9d6bce315d4fb6aa77832b3cdedd9
5
5
  SHA512:
6
- metadata.gz: 307dc6781a0ae40f1d66ebb7f9e8ea7a4e4fec739554a314c0dceb4302b0f3bbaa928dfacf7b8649ec99b87bebd243ac107446d4ad4b61d5e91ef054d7156e6c
7
- data.tar.gz: 3fc5c79c31830dacda3dc341c18584ed0c30ede6aa915011c40bcad19acbb78a3c65c97b4ea3754324077c1edce7215bb6c3837a4e35f0382de9036e3d1199b6
6
+ metadata.gz: 0b4fc40ce72b44600fe6a8167745f1a3147080ea38afecb84fe2bb35b599dc7b6ec22ba6f0b71963a020c08805fd5697437dadcfaaa592612f1032bf3195e88d
7
+ data.tar.gz: 1d40b47387cae6dbf65d539ae10aea6b29074b3837fc218604c407390f348df9b4cd7b8bf82981fa8a31a5f0f4603b7c685b3574547eb1058dd39f8bc8d7166a
@@ -0,0 +1,80 @@
1
+ body {
2
+ background-color: #fff;
3
+ color: #333;
4
+ margin: 33px;
5
+ }
6
+
7
+ body, p, ol, ul, td {
8
+ font-family: verdana, arial, helvetica, sans-serif;
9
+ font-size: 13px;
10
+ line-height: 18px;
11
+ }
12
+
13
+ pre {
14
+ background-color: #eee;
15
+ padding: 10px;
16
+ font-size: 11px;
17
+ }
18
+
19
+ a {
20
+ color: #000;
21
+ }
22
+
23
+ a:visited {
24
+ color: #666;
25
+ }
26
+
27
+ a:hover {
28
+ color: #fff;
29
+ background-color: #000;
30
+ }
31
+
32
+ th {
33
+ padding-bottom: 5px;
34
+ }
35
+
36
+ td {
37
+ padding: 0 5px 7px;
38
+ }
39
+
40
+ div.field,
41
+ div.actions {
42
+ margin-bottom: 10px;
43
+ }
44
+
45
+ #notice {
46
+ color: green;
47
+ }
48
+
49
+ .field_with_errors {
50
+ padding: 2px;
51
+ background-color: red;
52
+ display: table;
53
+ }
54
+
55
+ #error_explanation {
56
+ width: 450px;
57
+ border: 2px solid red;
58
+ padding: 7px 7px 0;
59
+ margin-bottom: 20px;
60
+ background-color: #f0f0f0;
61
+ }
62
+
63
+ #error_explanation h2 {
64
+ text-align: left;
65
+ font-weight: bold;
66
+ padding: 5px 5px 5px 15px;
67
+ font-size: 12px;
68
+ margin: -7px -7px 0;
69
+ background-color: #c00;
70
+ color: #fff;
71
+ }
72
+
73
+ #error_explanation ul li {
74
+ font-size: 12px;
75
+ list-style: square;
76
+ }
77
+
78
+ label {
79
+ display: block;
80
+ }
@@ -1,5 +1,6 @@
1
1
  module BetterRecord
2
2
  class ApplicationController < ActionController::Base
3
3
  protect_from_forgery with: :exception
4
+ layout BetterRecord.layout_template.presence || 'better_record/application'
4
5
  end
5
6
  end
@@ -0,0 +1,28 @@
1
+ require_dependency "better_record/application_controller"
2
+
3
+ module BetterRecord
4
+ class TableSizesController < ApplicationController
5
+ before_action :set_table_size, only: [:show]
6
+
7
+ # GET /table_sizes
8
+ def index
9
+ TableSize.reload_data if Boolean.parse(params[:reload])
10
+ @table_sizes = TableSize.all
11
+ end
12
+
13
+ # GET /table_sizes/1
14
+ def show
15
+ end
16
+
17
+ private
18
+ # Use callbacks to share common setup or constraints between actions.
19
+ def set_table_size
20
+ @table_size = TableSize.find_by(oid: params[:id])
21
+ end
22
+
23
+ # Only allow a trusted parameter "white list" through.
24
+ def table_size_params
25
+ params.fetch(:table_size, {})
26
+ end
27
+ end
28
+ end
@@ -0,0 +1,4 @@
1
+ module BetterRecord
2
+ module TableSizesHelper
3
+ end
4
+ end
@@ -9,14 +9,6 @@ module BetterRecord
9
9
  # == Extensions ===========================================================
10
10
 
11
11
  # == Relationships ========================================================
12
- if (ha = BetterRecord.has_audits_by_default)
13
- has_many :audits,
14
- class_name: 'BetterRecord::Audit',
15
- primary_type: :table_name,
16
- foreign_key: :row_id,
17
- foreign_type: :table_name,
18
- as: :audits
19
- end
20
12
 
21
13
  # == Validations ==========================================================
22
14
  before_validation :set_booleans
@@ -26,6 +18,10 @@ module BetterRecord
26
18
  # == Callbacks ============================================================
27
19
 
28
20
  # == Class Methods ========================================================
21
+ def self.audit_relation_name
22
+ @@audit_relation_name ||= (BetterRecord.audit_relation_name.presence || :audits).to_sym
23
+ end
24
+
29
25
  def self.boolean_columns
30
26
  []
31
27
  end
@@ -133,6 +129,10 @@ module BetterRecord
133
129
  self.class.boolean_columns
134
130
  end
135
131
 
132
+ def default_print
133
+ self.class.default_print
134
+ end
135
+
136
136
  private
137
137
  # def table_name_has_schema?
138
138
  # self.class.table_name_has_schema?
@@ -153,5 +153,13 @@ module BetterRecord
153
153
  true
154
154
  end
155
155
 
156
+ if (ha = BetterRecord.has_audits_by_default)
157
+ has_many self.audit_relation_name,
158
+ class_name: 'BetterRecord::LoggedAction',
159
+ primary_type: :table_name,
160
+ foreign_key: :row_id,
161
+ foreign_type: :table_name,
162
+ as: self.audit_relation_name
163
+ end
156
164
  end
157
165
  end
@@ -0,0 +1,5 @@
1
+ module BetterRecord
2
+ class Current < ActiveSupport::CurrentAttributes
3
+ attribute :user, :ip_address
4
+ end
5
+ end
@@ -1,5 +1,5 @@
1
1
  module BetterRecord
2
- class Audit < Base
2
+ class LoggedAction < Base
3
3
  # == Constants ============================================================
4
4
  ACTIONS = {
5
5
  D: 'DELETE',
@@ -9,7 +9,7 @@ module BetterRecord
9
9
  }.with_indifferent_access
10
10
 
11
11
  # == Attributes ===========================================================
12
- self.table_name = 'audit.logged_actions'
12
+ self.table_name = "#{BetterRecord.db_audit_schema}.logged_actions"
13
13
 
14
14
  # == Extensions ===========================================================
15
15
 
@@ -1,9 +1,12 @@
1
1
  module BetterRecord
2
2
  class TableSize < Base
3
3
  # == Constants ============================================================
4
- CREATE_TABLE_SQL = <<-SQL
5
- DROP TABLE IF EXISTS table_sizes;
6
- CREATE TEMPORARY TABLE table_sizes AS (
4
+ LOAD_TABLE_DATA_SQL = <<-SQL
5
+ BEGIN WORK;
6
+ LOCK #{BetterRecord.db_audit_schema}.table_sizes;
7
+ TRUNCATE #{BetterRecord.db_audit_schema}.table_sizes;
8
+ INSERT INTO #{BetterRecord.db_audit_schema}.table_sizes
9
+ (
7
10
  SELECT
8
11
  *,
9
12
  pg_size_pretty(total_bytes) AS total,
@@ -16,21 +19,22 @@ module BetterRecord
16
19
  total_bytes - idx_bytes - COALESCE(toast_bytes,0) AS tbl_bytes
17
20
  FROM (
18
21
  SELECT c.oid,nspname AS schema, relname AS name
19
- , c.reltuples AS row_estimate
22
+ , c.reltuples AS apx_row_count
20
23
  , pg_total_relation_size(c.oid) AS total_bytes
21
24
  , pg_indexes_size(c.oid) AS idx_bytes
22
25
  , pg_total_relation_size(reltoastrelid) AS toast_bytes
23
26
  FROM pg_class c
24
27
  LEFT JOIN pg_namespace n ON n.oid = c.relnamespace
25
28
  WHERE relkind = 'r'
26
- ) table_sizes
27
- ) table_sizes
28
- )
29
+ ) tmp_table_sizes
30
+ ) tmp_table_sizes
31
+ );
32
+ COMMIT WORK;
29
33
  SQL
30
34
 
31
35
  # == Attributes ===========================================================
32
36
  self.primary_key = :oid
33
- self.table_name = 'table_sizes'
37
+ self.table_name = "#{BetterRecord.db_audit_schema}.table_sizes"
34
38
 
35
39
  # == Extensions ===========================================================
36
40
 
@@ -43,18 +47,17 @@ module BetterRecord
43
47
  # == Callbacks ============================================================
44
48
 
45
49
  # == Class Methods ========================================================
46
- def self.load_schema(reload = false)
47
- unless @loaded_schema && !reload
48
- connection.execute CREATE_TABLE_SQL
49
- @loaded_schema = true
50
- end
50
+ def self.find_by(*args)
51
+ reload_data
52
+ super *args
53
+ end
51
54
 
52
- super()
55
+ def self.reload_data
56
+ connection.execute LOAD_TABLE_DATA_SQL
53
57
  end
54
58
 
55
- def self.find_by(*args)
56
- load_schema(true)
57
- super *args
59
+ def self.tmp_table_exists?
60
+ connection.query_values("SELECT temp_table_exists('tmp_table_sizes')").first
58
61
  end
59
62
 
60
63
  # def self.default_print
@@ -1,4 +1,4 @@
1
- module BetterRecord
1
+ module ActiveRecord
2
2
  module Associations
3
3
  class AssociationScope #:nodoc:
4
4
  def self.get_bind_values(owner, chain)
@@ -7,12 +7,12 @@ module BetterRecord
7
7
 
8
8
  binds << last_reflection.join_id_for(owner)
9
9
  if last_reflection.type
10
- binds << owner.class.__send__(last_reflection.options[:primary_type].presence || BetterRecord.default_polymorphic_method.presence || :polymorphic_name)
10
+ binds << owner.class.__send__(last_reflection.options[:primary_type].presence || :table_name)
11
11
  end
12
12
 
13
13
  chain.each_cons(2).each do |reflection, next_reflection|
14
14
  if reflection.type
15
- binds << next_reflection.klass.__send__(reflection.options[:primary_type].presence || next_reflection[:primary_type].presence || BetterRecord.default_polymorphic_method.presence || :polymorphic_name)
15
+ binds << next_reflection.klass.__send__(reflection.options[:primary_type].presence || next_reflection[:primary_type].presence || :table_name)
16
16
  end
17
17
  end
18
18
  binds
@@ -29,7 +29,7 @@ module BetterRecord
29
29
  scope = apply_scope(scope, table, key, value)
30
30
 
31
31
  if reflection.type
32
- polymorphic_type = transform_value(owner.class.__send__(reflection.options[:primary_type].presence || BetterRecord.default_polymorphic_method.presence || :polymorphic_name))
32
+ polymorphic_type = transform_value(owner.class.__send__(reflection.options[:primary_type].presence || :table_name))
33
33
  scope = apply_scope(scope, table, reflection.type, polymorphic_type)
34
34
  end
35
35
 
@@ -46,7 +46,7 @@ module BetterRecord
46
46
  constraint = table[key].eq(foreign_table[foreign_key])
47
47
 
48
48
  if reflection.type
49
- value = transform_value(next_reflection.klass.__send__(reflection.options[:primary_type].presence || BetterRecord.default_polymorphic_method.presence || :polymorphic_name))
49
+ value = transform_value(next_reflection.klass.__send__(reflection.options[:primary_type].presence || :table_name))
50
50
  scope = apply_scope(scope, table, reflection.type, value)
51
51
  end
52
52
 
@@ -62,9 +62,6 @@ module BetterRecord
62
62
  end
63
63
  end
64
64
 
65
- class BelongsToAssociation
66
- end
67
-
68
65
  class BelongsToPolymorphicAssociation < BelongsToAssociation
69
66
  def klass
70
67
  type = owner[reflection.foreign_type]
@@ -0,0 +1,21 @@
1
+ module ActiveRecord
2
+ module Reflection
3
+ class AbstractReflection
4
+ def join_scope(table, foreign_klass)
5
+ predicate_builder = predicate_builder(table)
6
+ scope_chain_items = join_scopes(table, predicate_builder)
7
+ klass_scope = klass_join_scope(table, predicate_builder)
8
+
9
+ if type
10
+ klass_scope.where!(type => foreign_klass.__send__(options[:primary_type] || :table_name))
11
+ end
12
+
13
+ scope_chain_items.inject(klass_scope, &:merge!)
14
+ end
15
+ end
16
+
17
+ class RuntimeReflection < AbstractReflection # :nodoc:
18
+ delegate :scope, :type, :constraints, :get_join_keys, :options, to: :@reflection
19
+ end
20
+ end
21
+ end
@@ -0,0 +1,11 @@
1
+ class Boolean
2
+ def self.parse(value)
3
+ ActiveRecord::Type::Boolean.new.cast(value)
4
+ end
5
+ end
6
+
7
+ class Object
8
+ def yes_no_to_s
9
+ !!self == self ? (self ? 'yes' : 'no') : to_s
10
+ end
11
+ end
@@ -0,0 +1,25 @@
1
+ # Be sure to restart your server when you modify this file.
2
+
3
+ # Define an application-wide content security policy
4
+ # For further information see the following documentation
5
+ # https://developer.mozilla.org/en-US/docs/Web/HTTP/Headers/Content-Security-Policy
6
+
7
+ # Rails.application.config.content_security_policy do |policy|
8
+ # policy.default_src :self, :https
9
+ # policy.font_src :self, :https, :data
10
+ # policy.img_src :self, :https, :data
11
+ # policy.object_src :none
12
+ # policy.script_src :self, :https
13
+ # policy.style_src :self, :https
14
+
15
+ # # Specify URI for violation reports
16
+ # # policy.report_uri "/csp-violation-report-endpoint"
17
+ # end
18
+
19
+ # If you are using UJS then enable automatic nonce generation
20
+ # Rails.application.config.content_security_policy_nonce_generator = -> request { SecureRandom.base64(16) }
21
+
22
+ # Report CSP violations to a specified URI
23
+ # For further information see the following documentation:
24
+ # https://developer.mozilla.org/en-US/docs/Web/HTTP/Headers/Content-Security-Policy-Report-Only
25
+ # Rails.application.config.content_security_policy_report_only = true
@@ -0,0 +1,10 @@
1
+ # Be sure to restart your server when you modify this file.
2
+
3
+ # Configure sensitive parameters which will be filtered from the log file.
4
+ Rails.application.config.filter_parameters |= [
5
+ :password,
6
+ :password_confirmation,
7
+ :new_password,
8
+ :new_password_confirmation,
9
+ :register_secret
10
+ ]
@@ -0,0 +1,21 @@
1
+ # Be sure to restart your server when you modify this file.
2
+
3
+ # Add new inflection rules using the following format. Inflections
4
+ # are locale specific, and you may define rules for as many different
5
+ # locales as you wish. All of these examples are active by default:
6
+ # ActiveSupport::Inflector.inflections(:en) do |inflect|
7
+ # inflect.plural /^(ox)$/i, '\1en'
8
+ # inflect.singular /^(ox)en/i, '\1'
9
+ # inflect.irregular 'person', 'people'
10
+ # inflect.uncountable %w( fish sheep )
11
+ # end
12
+
13
+ # These inflection rules are supported but not enabled by default:
14
+ ActiveSupport::Inflector.inflections(:en) do |inflect|
15
+ inflect.acronym 'API'
16
+ inflect.acronym 'APIs'
17
+ inflect.acronym 'DB'
18
+ inflect.acronym 'DBs'
19
+ inflect.acronym 'GNU'
20
+ inflect.acronym 'GNUs'
21
+ end
@@ -0,0 +1,5 @@
1
+ class Integer
2
+ def cents
3
+ StoreAsInt.money(self)
4
+ end
5
+ end
@@ -0,0 +1,7 @@
1
+ if defined?(JazzFingers)
2
+ JazzFingers.configure do |config|
3
+ config.colored_prompt = true
4
+ config.awesome_print = true
5
+ config.coolline = false
6
+ end
7
+ end
@@ -0,0 +1,4 @@
1
+ # Be sure to restart your server when you modify this file.
2
+
3
+ # Add new mime types for use in respond_to blocks:
4
+ # Mime::Type.register "text/richtext", :rtf