schemaless 0.0.3

Sign up to get free protection for your applications and to get access to all the features.
Files changed (81) hide show
  1. checksums.yaml +7 -0
  2. data/README.md +93 -0
  3. data/Rakefile +18 -0
  4. data/lib/generators/schemaless/migrations/USAGE +8 -0
  5. data/lib/generators/schemaless/migrations/migrations_generator.rb +108 -0
  6. data/lib/generators/schemaless/migrations/templates/change_table.rb +34 -0
  7. data/lib/generators/schemaless/migrations/templates/create_table.rb +18 -0
  8. data/lib/generators/schemaless/setup/setup_generator.rb +17 -0
  9. data/lib/generators/schemaless/setup/templates/schemaless.rb +3 -0
  10. data/lib/schemaless/ar/fields.rb +76 -0
  11. data/lib/schemaless/ar/indexes.rb +46 -0
  12. data/lib/schemaless/ar/stubs.rb +15 -0
  13. data/lib/schemaless/field.rb +94 -0
  14. data/lib/schemaless/index.rb +59 -0
  15. data/lib/schemaless/railtie.rb +23 -0
  16. data/lib/schemaless/table.rb +95 -0
  17. data/lib/schemaless/version.rb +4 -0
  18. data/lib/schemaless/worker.rb +47 -0
  19. data/lib/schemaless.rb +21 -0
  20. data/lib/tasks/schemaless_tasks.rake +23 -0
  21. data/spec/dummy/README.rdoc +28 -0
  22. data/spec/dummy/Rakefile +6 -0
  23. data/spec/dummy/app/assets/javascripts/application.js +13 -0
  24. data/spec/dummy/app/assets/stylesheets/application.css +15 -0
  25. data/spec/dummy/app/controllers/application_controller.rb +5 -0
  26. data/spec/dummy/app/helpers/application_helper.rb +2 -0
  27. data/spec/dummy/app/models/bike.rb +18 -0
  28. data/spec/dummy/app/models/place.rb +8 -0
  29. data/spec/dummy/app/models/rider.rb +8 -0
  30. data/spec/dummy/app/models/user.rb +21 -0
  31. data/spec/dummy/app/views/layouts/application.html.erb +14 -0
  32. data/spec/dummy/bin/bundle +3 -0
  33. data/spec/dummy/bin/rails +4 -0
  34. data/spec/dummy/bin/rake +4 -0
  35. data/spec/dummy/config/application.rb +28 -0
  36. data/spec/dummy/config/boot.rb +5 -0
  37. data/spec/dummy/config/database.yml +25 -0
  38. data/spec/dummy/config/environment.rb +5 -0
  39. data/spec/dummy/config/environments/development.rb +37 -0
  40. data/spec/dummy/config/environments/production.rb +78 -0
  41. data/spec/dummy/config/environments/test.rb +39 -0
  42. data/spec/dummy/config/initializers/assets.rb +8 -0
  43. data/spec/dummy/config/initializers/backtrace_silencers.rb +7 -0
  44. data/spec/dummy/config/initializers/cookies_serializer.rb +3 -0
  45. data/spec/dummy/config/initializers/filter_parameter_logging.rb +4 -0
  46. data/spec/dummy/config/initializers/inflections.rb +16 -0
  47. data/spec/dummy/config/initializers/mime_types.rb +4 -0
  48. data/spec/dummy/config/initializers/session_store.rb +3 -0
  49. data/spec/dummy/config/initializers/wrap_parameters.rb +14 -0
  50. data/spec/dummy/config/locales/en.yml +23 -0
  51. data/spec/dummy/config/routes.rb +56 -0
  52. data/spec/dummy/config/secrets.yml +22 -0
  53. data/spec/dummy/config.ru +4 -0
  54. data/spec/dummy/db/development.sqlite3 +0 -0
  55. data/spec/dummy/db/migrate/20141202223732_create_bikes.rb +16 -0
  56. data/spec/dummy/db/migrate/20141202223750_create_places.rb +9 -0
  57. data/spec/dummy/db/migrate/20141202223751_create_riders.rb +10 -0
  58. data/spec/dummy/db/migrate/20141202223752_create_users.rb +8 -0
  59. data/spec/dummy/db/migrate/20141202223753_create_user_skills.rb +8 -0
  60. data/spec/dummy/db/migrate/20141202223754_create_user_extras.rb +8 -0
  61. data/spec/dummy/db/production.sqlite3 +0 -0
  62. data/spec/dummy/db/test.sqlite3 +0 -0
  63. data/spec/dummy/log/development.log +213 -0
  64. data/spec/dummy/log/test.log +185888 -0
  65. data/spec/dummy/public/404.html +67 -0
  66. data/spec/dummy/public/422.html +67 -0
  67. data/spec/dummy/public/500.html +66 -0
  68. data/spec/dummy/public/favicon.ico +0 -0
  69. data/spec/generators/schemaless/migrations/migration_generator_spec.rb +14 -0
  70. data/spec/locales/en.yml +15 -0
  71. data/spec/locales/pt.yml +27 -0
  72. data/spec/schemaless/ar/fields_spec.rb +45 -0
  73. data/spec/schemaless/ar/indexes_spec.rb +43 -0
  74. data/spec/schemaless/field_spec.rb +29 -0
  75. data/spec/schemaless/index_spec.rb +9 -0
  76. data/spec/schemaless/table_spec.rb +25 -0
  77. data/spec/schemaless/worker_spec.rb +57 -0
  78. data/spec/schemaless_spec.rb +15 -0
  79. data/spec/spec_helper.rb +57 -0
  80. data/spec/support/001_create_testing_structure.rb +36 -0
  81. metadata +309 -0
checksums.yaml ADDED
@@ -0,0 +1,7 @@
1
+ ---
2
+ SHA1:
3
+ metadata.gz: cc3e152ecd0dded8e8316950f59c6616276694e4
4
+ data.tar.gz: a4232d47f7faec46ac0886141b215c0eaac023ad
5
+ SHA512:
6
+ metadata.gz: 131f65209c376f9976dbdd973ddf87bfda7ab27f6e29e16303c0f38dc7a3c564dfc077573953a4499a64f9a1b4ace3103e1f2dfe715f1786d5e867e1ffbddcba
7
+ data.tar.gz: 6ac58bc7b86799d04557a64cb2f3f7bdc4e2722354a1a6cbd122b4d6cbef967e273b84c69558d47f0d08c76a810e1364f74a4a6337b0839bf2ae6adb37825c9f
data/README.md ADDED
@@ -0,0 +1,93 @@
1
+ Schemaless
2
+ ==========
3
+
4
+ **On ActiveRecord | PostgreSQL**
5
+
6
+ **Experimental, bugged code**, please use to submit bugs/requests.
7
+ Needs more test code and scenarios also.
8
+
9
+
10
+ ```ruby
11
+ class Bike < ActiveRecord::Base
12
+
13
+ # field column name, column type, null: limit: precision: scale:
14
+ field :name # omit for String
15
+ field :cylinders, Integer # or Float, :decimal, :currency
16
+ field :bought_at, Date # Time, :date, :time, :timestamp
17
+
18
+ # Adds 'default: to AR
19
+ field :three_wheeler, :boolean, default: false
20
+
21
+ # Adds Array/Hash in PG
22
+ field :tags, Hash # hstore
23
+ field :tags, Array # hstore
24
+
25
+ # index columns, name: unique: orders: also supported.
26
+ index :name
27
+ index [:name, :cylinders]
28
+ ...
29
+ end
30
+
31
+ ```
32
+
33
+ # Why?
34
+
35
+ * Schema is **defined in code**, not in the DB.
36
+ (Plus no need for annotate models)
37
+
38
+ * It's easier and **less time** consuming.
39
+ Honestly, you never forgot that field and had to re-run a migration?
40
+
41
+ * There's **no footprint** in production mode.
42
+ No code executed in production mode.
43
+
44
+
45
+ # How?
46
+
47
+ Just include the gem and run:
48
+
49
+ rails g schemaless:setup
50
+
51
+
52
+ ## DEVELOPMENT | Realtime mode
53
+
54
+ This mode runs only in development mode.
55
+
56
+ It's when you are working, happy, without worry about schemas!
57
+ And also now DB fields are nicely described in the model.rb.
58
+ Logic is all in the ruby file now. It's like NoSQL: fun!
59
+
60
+
61
+ ## PRODUCTION | Migrate servers
62
+
63
+ Schemaless only stubs the #fields and #index methods in production.
64
+ There's nothing running/using resources in production.
65
+
66
+ To commit your changes you have two options:
67
+
68
+ ## Safe mode
69
+
70
+ In safe mode there's no change in the production perspective:
71
+
72
+ rails g schemaless:migrations
73
+
74
+ You'll find a migration per changed table waiting for you in git.
75
+ You simply review'em, and actually, there's no need to test them,
76
+ as your test suite is using them.
77
+
78
+
79
+ ## Wild mode
80
+
81
+ Schemaphobia? Very dangerous idea?
82
+
83
+ rake schemaless:run
84
+
85
+ Here we don't use any migration **as files**.
86
+ On the production servers a rake task ensures everything is up to date.
87
+ Tests also use the schema on the models.
88
+
89
+
90
+
91
+ ---
92
+
93
+ Have fun!
data/Rakefile ADDED
@@ -0,0 +1,18 @@
1
+ #!/usr/bin/env rake
2
+ require 'bundler/gem_tasks'
3
+
4
+ require 'rspec/core/rake_task'
5
+ require 'rubocop/rake_task'
6
+
7
+ APP_RAKEFILE = File.expand_path('../spec/dummy/Rakefile', __FILE__)
8
+ # load 'rails/tasks/engine.rake'
9
+
10
+ Bundler::GemHelper.install_tasks
11
+
12
+ # Dir[File.join(File.dirname(__FILE__), 'tasks/**/*.rake')].each {|f| load f }
13
+
14
+ # RSpec::Core::RakeTask.new(:spec => 'app:db:test:prepare')
15
+ RSpec::Core::RakeTask.new
16
+ RuboCop::RakeTask.new
17
+
18
+ task default: [:spec, :rubocop]
@@ -0,0 +1,8 @@
1
+ Description:
2
+ Explain the generator
3
+
4
+ Example:
5
+ rails generate migration Thing
6
+
7
+ This will create:
8
+ what/will/it/create
@@ -0,0 +1,108 @@
1
+ require 'rails/generators'
2
+ require 'rails/generators/migration'
3
+
4
+ module Schemaless
5
+ # Generates migrations for schemaless safe mode
6
+ class MigrationsGenerator < Rails::Generators::Base
7
+ include Rails::Generators::Migration
8
+ source_root File.expand_path('../templates', __FILE__)
9
+ argument :models, type: :array, default: [] # , banner: 'path'
10
+ desc 'Schemaless migration files generator!'
11
+
12
+ def create_migration_files
13
+ Rails.application.eager_load!
14
+ all_tables = Schemaless::Worker.all_tables
15
+ if models.empty?
16
+ tables = all_tables
17
+ else
18
+ tables = all_tables.select { |t| models.include?(t.model.to_s.downcase) }
19
+ end
20
+ tables.each do |table|
21
+ puts "Generating migrations for #{table}"
22
+ create_migration_for table
23
+ end
24
+ end
25
+
26
+ protected
27
+
28
+ attr_reader :table_name, :fields, :indexes, :migration_action, :join_tables
29
+
30
+ def build_file_name(name = [])
31
+ if @table.fields.add.any?
32
+ name << :add
33
+ name << (@table.fields.add.size > 3 ? :many : @table.fields.add)
34
+ name << (@table.fields.remove.any? ? :and : :to)
35
+ end
36
+ if @table.fields.remove.any?
37
+ name << :remove
38
+ name << (@table.fields.remove.size > 3 ? :many : @table.fields.remove)
39
+ name << :from
40
+ end
41
+ name << table_name
42
+ end
43
+
44
+ def file_name
45
+ return @file_name if @file_name
46
+ return "create_#{@table_name}" unless @table.exists?
47
+ @file_name = build_file_name.flatten.join('_')
48
+ end
49
+
50
+ def create_migration_for table
51
+ @table = table
52
+ @fields = @table.fields
53
+ @indexes = @table.indexes
54
+ @table_name = @table.name
55
+
56
+ template = file_name =~ /^create_(.+)/ ? 'create' : 'change'
57
+ migration_template "#{template}_table.rb", "db/migrate/#{file_name}.rb"
58
+
59
+ # case file_name
60
+ # when /join_table/
61
+ # if attributes.length == 2
62
+ # @migration_action = 'join'
63
+ # @join_tables = if pluralize_table_names?
64
+ # attributes.map(&:plural_name)
65
+ # else
66
+ # attributes.map(&:singular_name)
67
+ # end
68
+
69
+ # set_index_names
70
+ # end
71
+ end
72
+
73
+ def set_index_names
74
+ attributes.each_with_index do |att, i|
75
+ att.index_name = [att, attributes[i - 1]].map { |a| index_name_for(a) }
76
+ end
77
+ end
78
+
79
+ def index_name_for(attribute)
80
+ if attribute.foreign_key?
81
+ attribute.name
82
+ else
83
+ attribute.name.singularize.foreign_key
84
+ end.to_sym
85
+ end
86
+
87
+ private
88
+
89
+ # def attributes_with_index
90
+ # attributes.select { |a| !a.reference? && a.has_index? }
91
+ # end
92
+
93
+ def old_migrations
94
+ ActiveRecord::Migrator.get_all_versions
95
+ end
96
+
97
+ # def validate_file_name!
98
+ # unless file_name =~ /^[_a-z0-9]+$/
99
+ # fail IllegalMigrationNameError, file_name
100
+ # end
101
+ # end
102
+
103
+ def self.next_migration_number(dirname)
104
+ next_migration_number = current_migration_number(dirname) + 1
105
+ ActiveRecord::Migration.next_migration_number(next_migration_number)
106
+ end
107
+ end
108
+ end
@@ -0,0 +1,34 @@
1
+ class <%= migration_class_name %> < ActiveRecord::Migration
2
+ def change
3
+ <%- fields.add.each do |field| -%>
4
+ <%- if field.reference? -%>
5
+ add_reference :<%= table_name %>, :<%= field.name %><%= field.opts_text %>
6
+ <%- unless field.polymorphic? -%>
7
+ add_foreign_key :<%= table_name %>, :<%= field.name.pluralize %>
8
+ <%- end -%>
9
+ <%- end -%>
10
+ add_column :<%= table_name %>, :<%= field.name %>, :<%= field.type %><%= field.opts_text %>
11
+ <%- end -%>
12
+ <%- fields.change.each do |field| -%>
13
+ change_column :<%= table_name %>, :<%= field.name %>, :<%= field.type %><%= field.opts_text %>
14
+ <%- end -%>
15
+
16
+ <%- indexes.remove do |index| -%>
17
+ remove_index :<%= table_name %>, <%= index.fields_text %>
18
+ <%- end -%>
19
+ <%- fields.remove.each do |field| -%>
20
+ remove_column :<%= table_name %>, :<%= field.name %>
21
+ <%- end -%>
22
+ <%- indexes.add.each do |index| -%>
23
+ add_index :<%= table_name %>, <%= index.fields_text %><%= index.opts_text %>
24
+ <%- end -%>
25
+ end
26
+ <%- if migration_action == 'join' -%>
27
+ create_join_table :<%= join_tables.first %>, :<%= join_tables.second %> do |t|
28
+ <%- fields.each do |field| -%>
29
+ <%= '# ' unless field.has_index? -%>t.index <%= field.index_name %><%= field.opts_text %>
30
+ <%- end -%>
31
+ end
32
+ end
33
+ <%- end -%>
34
+ end
@@ -0,0 +1,18 @@
1
+ class <%= migration_class_name %> < ActiveRecord::Migration
2
+ def change
3
+ create_table :<%= table_name %> do |t|
4
+ <% fields.add.each do |field| -%>
5
+ t.<%= field.type %> :<%= field.name %><%= field.opts_text %>
6
+ <% end -%>
7
+ <% if options[:timestamps] %>
8
+ t.timestamps null: false
9
+ <% end -%>
10
+ end
11
+ <% indexes.add.each do |index| -%>
12
+ add_index :<%= table_name %>, <%= index.fields_text %><%= index.opts_text %>
13
+ <% end -%>
14
+ <% fields.add.select(&:reference?).reject(&:polymorphic?).each do |field| -%>
15
+ add_foreign_key :<%= table_name %>, :<%= field.name.pluralize %>
16
+ <% end -%>
17
+ end
18
+ end
@@ -0,0 +1,17 @@
1
+ module Schemaless
2
+ # Generates schemaless setup for rails app
3
+ class SetupGenerator < Rails::Generators::Base
4
+ source_root File.expand_path('../templates', __FILE__)
5
+ argument :attributes, type: :array, default: [], banner: 'path'
6
+ desc 'Schemaless config files generator!'
7
+
8
+ def create_config_file
9
+ application 'end'
10
+ application ' Schemaless::Worker.run!'
11
+ application ' Rails.application.eager_load!'
12
+ application 'config.to_prepare do'
13
+ # initializer | application
14
+ # copy 'schemaless.rb'
15
+ end
16
+ end
17
+ end
@@ -0,0 +1,3 @@
1
+ module Schemaless
2
+ <%= comments %>
3
+ end
@@ -0,0 +1,76 @@
1
+ require 'active_support/concern'
2
+ require 'active_support/core_ext/hash/keys'
3
+
4
+ module Schemaless
5
+ #
6
+ # Extend ActiveRecord for #field
7
+ #
8
+ module Fields
9
+ extend ActiveSupport::Concern
10
+
11
+ # Schemaless ActiveRecord fields
12
+ module ClassMethods
13
+ AR_OPTS = ::Schemaless::Field::VALID_OPTS - [:index, :type]
14
+
15
+ def schemaless_fields
16
+ @schemaless_fields ||= []
17
+ end
18
+
19
+ #
20
+ # field(*)
21
+ #
22
+ # Gets all fields in the model in the schamless_fields array.
23
+ #
24
+ # field :name # Defaults String
25
+ # field :cylinders, Integer # Use Class or :symbols
26
+ # field :type, index: true, default: nil, limit: 5
27
+ #
28
+ #
29
+ def field(*params)
30
+ config = params.extract_options!
31
+ config.assert_valid_keys(*::Schemaless::Field::VALID_OPTS)
32
+ type = config.delete(:type)
33
+ type ||= params.size > 1 ? params.pop : :string # TBD
34
+ name = params.join
35
+ schemaless_fields <<
36
+ ::Schemaless::Field.new(name, type, config)
37
+ end
38
+
39
+ #
40
+ # Create the belongs_to foreign keys
41
+ #
42
+ def belongs_to(*params)
43
+ config = params.extract_options!
44
+ name = "#{params.join}_id"
45
+ schemaless_fields <<
46
+ ::Schemaless::Field.new(name, :integer, config)
47
+ super(*params)
48
+ end
49
+
50
+ #
51
+ # Helper for timestamps
52
+ #
53
+ def timestamps(*params)
54
+ config = params.extract_options!
55
+ type = config[:type] || :timestamp
56
+ schemaless_fields << ::Schemaless::Field.new(:created_at, type, null: false)
57
+ schemaless_fields << ::Schemaless::Field.new(:updated_at, type, null: true)
58
+ return unless config[:paranoid]
59
+ schemaless_fields << ::Schemaless::Field.new(:deleted_at, type, null: true)
60
+ end
61
+
62
+ #
63
+ # Get all fields in a schemaless way
64
+ #
65
+ def current_attributes
66
+ columns_hash.map do |k, v|
67
+ next if v.primary
68
+ opts = AR_OPTS.reduce({}) do |a, e|
69
+ v.send(e) ? a.merge(e => v.send(e)) : a
70
+ end
71
+ ::Schemaless::Field.new(k, v.type, opts)
72
+ end.reject!(&:nil?)
73
+ end
74
+ end
75
+ end
76
+ end
@@ -0,0 +1,46 @@
1
+ require 'active_support/concern'
2
+ require 'active_support/core_ext/hash/keys'
3
+
4
+ module Schemaless
5
+ #
6
+ # Extend ActiveRecord for #index
7
+ #
8
+ module Indexes
9
+ extend ActiveSupport::Concern
10
+
11
+ # Schemaless ActiveRecord indexes
12
+ module ClassMethods
13
+
14
+ def schemaless_indexes
15
+ @schemaless_indexes ||= []
16
+ end
17
+
18
+ #
19
+ # index(*)
20
+ #
21
+ # index :name
22
+ # index [:one, :other]
23
+ # index :name, unique: true
24
+ #
25
+ #
26
+ def index(*params)
27
+ config = params.extract_options!
28
+ name = config.delete(:name)
29
+ schemaless_indexes <<
30
+ ::Schemaless::Index.new(params, name, config)
31
+ end
32
+
33
+ #
34
+ # Get all indexes in a schemaless way
35
+ #
36
+ def current_indexes
37
+ ::ActiveRecord::Base.connection.indexes(table_name).map do |i|
38
+ opts = ::Schemaless::Index::VALID_OPTS.reduce({}) do |a, e|
39
+ i.send(e) ? a.merge(e => i.send(e)) : a
40
+ end
41
+ ::Schemaless::Index.new(i.columns, i.name, opts)
42
+ end
43
+ end
44
+ end
45
+ end # ActiveRecord
46
+ end # Schemaless
@@ -0,0 +1,15 @@
1
+ require 'active_support/concern'
2
+
3
+ module Schemaless
4
+ # Stubs for production mode
5
+ module Stubs
6
+ extend ActiveSupport::Concern
7
+
8
+ # Schemaless ActiveRecord Stub
9
+ module ClassMethods
10
+ def field(*) end
11
+
12
+ def index(*) end
13
+ end
14
+ end # Stubs
15
+ end # Schemaless
@@ -0,0 +1,94 @@
1
+ module Schemaless
2
+ #
3
+ # Fields
4
+ #
5
+ class Field
6
+ attr_accessor :name, :type, :opts, :default, :index
7
+ VALID_OPTS = [:type, :limit, :precision, :scale, :null, :default, :index]
8
+
9
+ #
10
+ # Field - name, type, opts: [:default, :null, :unique]
11
+ #
12
+ def initialize(name, type, opts = {})
13
+ fail InvalidArgument, 'opts must be a hash' unless opts.is_a?(Hash)
14
+ @name = name.to_s
15
+ @type = map_field type
16
+ @opts = opts.select { |_k, v| v.present? }
17
+ @opts[:null] = true unless @opts[:null].present?
18
+ @opts[:limit] = 255 unless @opts[:limit].present? || @type != :string
19
+ end
20
+
21
+ def ==(other)
22
+ name == other.name # && type == other.type
23
+ end
24
+
25
+ def reference?
26
+ end
27
+
28
+ def opts_text
29
+ txt = opts.map { |k, v| "#{k}: #{v}" }.join(', ')
30
+ txt.empty? ? '' : ", #{txt}"
31
+ end
32
+
33
+ def to_s
34
+ name
35
+ end
36
+
37
+ #
38
+ # Add Fields
39
+ #
40
+ def add!(table)
41
+ puts "++ Adding '#{self}' to '#{table}'"
42
+ return if Schemaless.sandbox
43
+ ::ActiveRecord::Migration.add_column(table.name, name, type, opts)
44
+ end
45
+
46
+ #
47
+ # Delete Fields
48
+ #
49
+ def remove!(table)
50
+ puts "-- Removing '#{self}' from '#{table}'"
51
+ return if Schemaless.sandbox
52
+ ::ActiveRecord::Migration.remove_column(table.name, name)
53
+ end
54
+
55
+ #
56
+ # Change Fields
57
+ #
58
+ def change!(table)
59
+ return if Schemaless.sandbox
60
+ # ::ActiveRecord::Migration.change_column(table, name)
61
+ # ::ActiveRecord::Migration.change_column_null(table, name)
62
+ # ::ActiveRecord::Migration.change_column_default(table, name)
63
+ ::ActiveRecord::Migration.change_column(table.name, name, type, opts)
64
+ end
65
+
66
+ #
67
+ # binary # boolean
68
+ # date # datetime
69
+ # time # timestamp
70
+ # integer # primary_key # references
71
+ # decimal # float
72
+ # string # text
73
+ # hstore
74
+ # json
75
+ # array
76
+ # cidr_address
77
+ # ip_address
78
+ # mac_address
79
+ #
80
+ def map_field(field) # rubocop:disable Metrics/MethodLength
81
+ return field if field.is_a?(Symbol)
82
+ case field.to_s
83
+ when /Integer|Fixnum|Numeric/ then :integer
84
+ when /BigDecimal|Rational/ then :decimal
85
+ when /Float/ then :float
86
+ when /DateTime/ then :datetime
87
+ when /Date/ then :date
88
+ when /Time/ then :timestamp
89
+ else
90
+ :string
91
+ end
92
+ end
93
+ end
94
+ end
@@ -0,0 +1,59 @@
1
+ module Schemaless
2
+ #
3
+ # Stores indexes
4
+ #
5
+ class Index
6
+ attr_accessor :fields, :name, :opts
7
+ VALID_OPTS = [:unique, :orders]
8
+
9
+ def initialize(fields, name = nil, opts = {})
10
+ @fields = [fields].flatten
11
+ @fields = @fields.first if @fields.size == 1
12
+ @name = name || opts[:name]
13
+ @opts = opts.select { |_k, v| v.present? }
14
+ @opts.merge!(name: @name) if @name
15
+ end
16
+
17
+ def opts_text
18
+ txt = opts.map { |k, v| "#{k}: #{v}" }.join(', ')
19
+ txt.empty? ? '' : ", #{txt}"
20
+ end
21
+
22
+ def fields_text
23
+ @fields.inspect
24
+ end
25
+
26
+ def to_s
27
+ name
28
+ end
29
+
30
+ def ==(other)
31
+ name == other.name
32
+ end
33
+
34
+ #
35
+ # Add Indexes
36
+ #
37
+ def add!(table)
38
+ return if Schemaless.sandbox
39
+ ::ActiveRecord::Migration.add_index(table.name, fields, opts)
40
+ end
41
+
42
+ #
43
+ # Delete Indexes
44
+ #
45
+ def remove!(table)
46
+ return if Schemaless.sandbox
47
+ key = name ? { name: name } : { column: fields }
48
+ ::ActiveRecord::Migration.remove_index(table.name, key)
49
+ end
50
+
51
+ #
52
+ # Change Indexes
53
+ #
54
+ def change!(table)
55
+ return if Schemaless.sandbox
56
+ ::ActiveRecord::Migration.change_index(table.name, name, type, opts)
57
+ end
58
+ end
59
+ end
@@ -0,0 +1,23 @@
1
+ # Rails 3 initialization
2
+ module Schemaless
3
+ require 'rails'
4
+ # Rails include
5
+ class Railtie < Rails::Railtie
6
+ initializer 'schemaless.insert_into_active_record' do
7
+ ActiveSupport.on_load :active_record do
8
+ if Rails.env =~ /production/
9
+ require 'schemaless/ar/stubs'
10
+ ::ActiveRecord::Base.send :include, Schemaless::Stubs
11
+ else
12
+ require 'schemaless/ar/fields'
13
+ require 'schemaless/ar/indexes'
14
+ ::ActiveRecord::Base.send :include, Schemaless::Fields
15
+ ::ActiveRecord::Base.send :include, Schemaless::Indexes
16
+ end
17
+ end
18
+ end
19
+ rake_tasks do
20
+ load 'tasks/schemaless_tasks.rake'
21
+ end
22
+ end
23
+ end