schemaless 0.0.3

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.
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