extend_at 0.0.3 → 0.1.0

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.
data/Gemfile CHANGED
@@ -2,3 +2,8 @@ source "http://rubygems.org"
2
2
 
3
3
  # Specify your gem's dependencies in acts_as_configuration.gemspec
4
4
  gemspec
5
+
6
+
7
+ group :test do
8
+ gem "rails", "~> 3.1.0"
9
+ end
data/README.markdown CHANGED
@@ -8,16 +8,21 @@ This gem allows you to extend models without migrations: This way you can, i.e.,
8
8
 
9
9
  ### Rails 3
10
10
  Add in your Gemfile:
11
+
11
12
  <code>gem 'extend_at'</code>
12
13
 
14
+ After that, you need execute:
15
+
16
+ <code>rails generate extend_at:install</code>
17
+
18
+ This will generate one migration with all necessary tables.
19
+
13
20
  ## Usage
14
21
 
15
- You only need to add the next line in your model.
22
+ You don't need an extra column in your model. Only you need is put next code in your model.
16
23
 
17
24
  <code>extend_at :extra</code>
18
25
 
19
- The column <code>extra</code> must be string or text.
20
-
21
26
  For example:
22
27
 
23
28
  class User < ActiveRecord::Base
@@ -80,6 +85,33 @@ You can set the colum's type.
80
85
  end
81
86
  end
82
87
 
88
+ ##### Valid types
89
+
90
+ Valid symbols:
91
+
92
+ * <code>:any</code>
93
+ * <code>:binary</code>
94
+ * <code>:boolean</code>
95
+ * <code>:date</code>
96
+ * <code>:datetime</code>
97
+ * <code>:decimal</code>
98
+ * <code>:float</code>
99
+ * <code>:integer</code>
100
+ * <code>:string</code>
101
+ * <code>:text</code>
102
+ * <code>:time</code>
103
+ * <code>:timestamp</code>
104
+
105
+ But you can use classes.
106
+
107
+ * Float: <code>:any</code>
108
+ * Fixnum: <code>:integer</code>
109
+ * String: <code>:text</code>
110
+ * Time: <code>:timestamp</code>
111
+ * Date: <code>:datetime</code>
112
+
113
+ Else, return <code>:any</code>
114
+
83
115
  You can use any class, but if you need use boolean values, you must use :boolean.
84
116
 
85
117
  #### Set default value
@@ -133,8 +165,8 @@ You can use any class, but if you need use boolean values, you must use :boolean
133
165
  :default => 1,
134
166
  :validate => lambda {
135
167
  |age|
136
- errors.add :config_age, "Are you Matusalen?" if age > 150
137
- errors.add :config_age, "Are you a fetus?" if age <= 0
168
+ errors.add :extra_age, "Are you Matusalen?" if age > 150
169
+ errors.add :extra_age, "Are you a fetus?" if age <= 0
138
170
  }
139
171
  }, :profile_description => {
140
172
  :type => lambda {
@@ -149,7 +181,7 @@ You can use any class, but if you need use boolean values, you must use :boolean
149
181
  },
150
182
  :validate => lambda {
151
183
  |time|
152
- errors.add :config_last_loggin, "You can't loggin on the future" if time > Time.now
184
+ errors.add :extra_last_loggin, "You can't loggin on the future" if time > Time.now
153
185
  }
154
186
  }, :subscribe_to_rss => :get_rss_config
155
187
  }
@@ -179,9 +211,28 @@ You can use any class, but if you need use boolean values, you must use :boolean
179
211
  end
180
212
  end
181
213
 
214
+ ### Scopes
215
+
216
+ You can use scope like:
217
+
218
+ User.extra_last_loggin_gt_eq(1.week.ago).extra_age_gt_eq(18).where(:column => "value").all
219
+
220
+ Valid scopes:
221
+
222
+ <extention>_<column_name>_<comparation>
223
+
224
+ Comparations:
225
+
226
+ * lt
227
+ * lt_eq
228
+ * eq
229
+ * gt_eq
230
+ * gt
231
+ * match
232
+
182
233
  ### Integration in the views
183
234
 
184
- If you like to use some configuration variable in your views you only need put the name of the input like <code>:config_name</code>, for example:
235
+ If you like to use some configuration variable in your views you only need put the name of the input like <code>:extra_name</code>, for example:
185
236
 
186
237
  <% form_for(@user) do |f| %>
187
238
  ...
@@ -227,6 +278,11 @@ This code read the configuration of the columns when you access to the extra col
227
278
 
228
279
  If you found a bug, create an issue. If you have a recomendation, idea, etc., create a request or fork the project.
229
280
 
281
+ ## TODO:
282
+
283
+ * RSpec test
284
+ * Support static columns
285
+
230
286
  ## License
231
287
 
232
288
  This gem is under MIT license.
data/Rakefile CHANGED
@@ -1 +1,12 @@
1
1
  require "bundler/gem_tasks"
2
+ require 'rake/testtask'
3
+ require 'rspec'
4
+ require 'rspec/core/rake_task'
5
+
6
+
7
+ task :default => :spec
8
+ task :test => :spec
9
+
10
+ RSpec::Core::RakeTask.new(:spec) do
11
+ system "echo \"recreating database \" && cd #{File.join(File.dirname(__FILE__), 'spec', 'app')} && rake db:migrate:reset"
12
+ end
data/extend_at.gemspec CHANGED
@@ -18,5 +18,7 @@ Gem::Specification.new do |s|
18
18
  s.executables = `git ls-files -- bin/*`.split("\n").map{ |f| File.basename(f) }
19
19
  s.require_paths = ["lib"]
20
20
 
21
- s.add_development_dependency "rspec"
21
+ s.add_runtime_dependency 'rails', '~> 3.1'
22
+ s.add_development_dependency "rspec", '~> 2.5'
23
+ s.add_development_dependency "rspec-core"
22
24
  end
@@ -0,0 +1,136 @@
1
+ require File.expand_path('../models/all', __FILE__)
2
+
3
+ module ExtendModelAt
4
+ class ModelManager
5
+ def initialize(column_name,model, config)
6
+ @column_name, @model, @config = column_name, model, config
7
+
8
+ if not @model.new_record?
9
+ @extend_at = ExtendAt.find_by_model_id_and_model_type @model.id, @model.class.to_s
10
+ else
11
+ @extend_at = ExtendAt.new
12
+ @extend_at.save
13
+ end
14
+ end
15
+
16
+ def assign(column,value)
17
+ raise "#{value} is not valid" if not @model.send(:valid_type?, value, @config[column.to_sym].try(:[], :type))
18
+
19
+ last_model = get_column_model column
20
+ type_class = get_type_class @config[column.to_sym].try(:[], :type)
21
+
22
+ if last_model.nil?
23
+ eval "
24
+ new_column = Column.new :extend_at_id => @extend_at.id
25
+ new_column.save
26
+ new_value = #{type_class}.new(:column => column, :value => value, :extend_at_column_id => new_column.id)
27
+ new_value.save
28
+ new_column.column_id = new_value.id
29
+ new_column.save
30
+ "
31
+ else
32
+ eval "last_model.value = value
33
+ last_model.save"
34
+ end
35
+ end
36
+
37
+ def get_value(column)
38
+ model = get_column_model column #, get_type(column)
39
+ model.try(:value)
40
+ end
41
+
42
+ protected
43
+ def get_column_model(column)
44
+ type = get_type column
45
+ type_class = get_type_class type
46
+ eval "::#{type_class}.where(
47
+ ::#{type_class}.arel_table[:column].eq(column).and(
48
+ ::#{type_class}.arel_table[:extend_at_column_id].in(
49
+ ::Column.arel_table.project(:id).where(
50
+ ::Column.arel_table[:extend_at_id].eq(@extend_at.id)
51
+ )
52
+ )
53
+ )
54
+ ).try(:first)"
55
+ end
56
+
57
+ def get_type(column)
58
+ if @config[column.to_sym].kind_of? Hash
59
+ @config[column.to_sym][:type]
60
+ else
61
+ nil
62
+ end
63
+ end
64
+
65
+ def get_type_class(type)
66
+ type = type
67
+ if type == :any or type.nil?
68
+ return "AnyValue"
69
+ elsif type == :binary
70
+ return "BinaryValue"
71
+ elsif type == :boolean
72
+ return "BooleanValue"
73
+ elsif type == :date
74
+ return "DateValue"
75
+ elsif type == :datetime
76
+ return "DatetimeValue"
77
+ elsif type == :decimal
78
+ return "DecimalValue"
79
+ elsif type == :float
80
+ return "FloatValue"
81
+ elsif type == :integer
82
+ return "IntegerValue"
83
+ elsif type == :string
84
+ return "StringValue"
85
+ elsif type == :text
86
+ return "TextValue"
87
+ elsif type == :time
88
+ return "TimeValue"
89
+ elsif type == :timestamp
90
+ return "TimestampValue"
91
+ else
92
+ return "AnyValue"
93
+ end
94
+ end
95
+
96
+ def update
97
+ @extend_at.model_id = @model.id
98
+ @extend_at.model_type = @model.class.to_s
99
+ @extend_at.save
100
+ end
101
+
102
+ EQUIVALENCE_METHODS = {
103
+ 'lt' => 'lt',
104
+ 'lt_eq' => 'lteq',
105
+ 'eq' => 'eq',
106
+ 'gt' => 'gt',
107
+ 'gt_eq' => 'gteq',
108
+ 'match' => 'matches'
109
+ }
110
+
111
+ def search(column, method,value)
112
+ type = get_type column
113
+ type_class = get_type_class type
114
+ equivalence_method = EQUIVALENCE_METHODS[method.to_s]
115
+ "
116
+ where(
117
+ ExtendAt.arel_table.project('model_id').where(
118
+ ExtendAt.arel_table[:model_type].eq(\"#{@model.class.name}\").and(
119
+ ExtendAt.arel_table[:id].in(
120
+ ::Column.arel_table.project('extend_at_id').where(
121
+ ::Column.arel_table[:id].in(
122
+ ::#{type_class}.arel_table.project('extend_at_column_id').where(
123
+ ::#{type_class}.arel_table[:column].eq(column).and(
124
+ ::#{type_class}.arel_table[:value].#{equivalence_method}(value)
125
+ )
126
+ )
127
+ )
128
+ )
129
+ )
130
+ )
131
+ ).to_sql
132
+ )
133
+ "
134
+ end
135
+ end
136
+ end
@@ -0,0 +1,15 @@
1
+ require File.expand_path('../any_value', __FILE__)
2
+ require File.expand_path('../string_value', __FILE__)
3
+ require File.expand_path('../text_value', __FILE__)
4
+ require File.expand_path('../integer_value', __FILE__)
5
+ require File.expand_path('../float_value', __FILE__)
6
+ require File.expand_path('../decimal_value', __FILE__)
7
+ require File.expand_path('../datetime_value', __FILE__)
8
+ require File.expand_path('../timestamp_value', __FILE__)
9
+ require File.expand_path('../time_value', __FILE__)
10
+ require File.expand_path('../date_value', __FILE__)
11
+ require File.expand_path('../binary_value', __FILE__)
12
+ require File.expand_path('../boolean_value', __FILE__)
13
+
14
+ require File.expand_path('../column', __FILE__)
15
+ require File.expand_path('../extend_at', __FILE__)
@@ -0,0 +1,5 @@
1
+ class AnyValue < ActiveRecord::Base
2
+ set_table_name "extend_at_anies"
3
+ belongs_to :extend_at_column, :class_name => 'Column', :foreign_key => 'extend_at_column_id'
4
+ end
5
+
@@ -0,0 +1,4 @@
1
+ class BinaryValue < ActiveRecord::Base
2
+ set_table_name "extend_at_binaries"
3
+ belongs_to :extend_at_column, :class_name => 'Column', :foreign_key => 'extend_at_column_id'
4
+ end
@@ -0,0 +1,4 @@
1
+ class BooleanValue < ActiveRecord::Base
2
+ set_table_name "extend_at_booleans"
3
+ belongs_to :extend_at_column, :class_name => 'Column', :foreign_key => 'extend_at_column_id'
4
+ end
@@ -0,0 +1,10 @@
1
+ class Column < ActiveRecord::Base
2
+ set_table_name "extend_at_columns"
3
+ # belongs_to :extend_at
4
+ belongs_to :column, :polymorphic => true
5
+ belongs_to :extend_at, :class_name => 'ExtendAt'
6
+ scope :for_model, lambda { |model|
7
+ where(:model_id => model.try(:id) || model.to_i)
8
+ }
9
+ end
10
+
@@ -0,0 +1,4 @@
1
+ class DateValue < ActiveRecord::Base
2
+ set_table_name "extend_at_dates"
3
+ belongs_to :extend_at_column, :class_name => 'Column', :foreign_key => 'extend_at_column_id'
4
+ end
@@ -0,0 +1,4 @@
1
+ class DatetimeValue < ActiveRecord::Base
2
+ set_table_name "extend_at_datetimes"
3
+ belongs_to :extend_at_column, :class_name => 'Column', :foreign_key => 'extend_at_column_id'
4
+ end
@@ -0,0 +1,4 @@
1
+ class DecimalValue < ActiveRecord::Base
2
+ set_table_name "extend_at_decimals"
3
+ belongs_to :extend_at_column, :class_name => 'Column', :foreign_key => 'extend_at_column_id'
4
+ end
@@ -0,0 +1,6 @@
1
+ class ExtendAt < ActiveRecord::Base
2
+ set_table_name "extend_ats"
3
+ belongs_to :model, :polymorphic => true
4
+ has_many :extend_at_columns, :source => :extend_at, :class_name => 'Column'
5
+ # has_many :columns, :through => :extend_at_columns, :source => :column
6
+ end
@@ -0,0 +1,4 @@
1
+ class FloatValue < ActiveRecord::Base
2
+ set_table_name "extend_at_floats"
3
+ belongs_to :extend_at_column, :class_name => 'Column', :foreign_key => 'extend_at_column_id'
4
+ end
@@ -0,0 +1,4 @@
1
+ class IntegerValue < ActiveRecord::Base
2
+ set_table_name "extend_at_integers"
3
+ belongs_to :extend_at_column, :class_name => 'Column', :foreign_key => 'extend_at_column_id'
4
+ end
@@ -0,0 +1,5 @@
1
+ class StringValue < ActiveRecord::Base
2
+ set_table_name "extend_at_strings"
3
+ belongs_to :extend_at_column, :class_name => 'Column', :foreign_key => 'extend_at_column_id'
4
+ end
5
+
@@ -0,0 +1,5 @@
1
+ class TextValue < ActiveRecord::Base
2
+ set_table_name "extend_at_texts"
3
+ belongs_to :extend_at_column, :class_name => 'Column', :foreign_key => 'extend_at_column_id'
4
+ end
5
+
@@ -0,0 +1,4 @@
1
+ class TimeValue < ActiveRecord::Base
2
+ set_table_name "extend_at_times"
3
+ belongs_to :extend_at_column, :class_name => 'Column', :foreign_key => 'extend_at_column_id'
4
+ end
@@ -0,0 +1,4 @@
1
+ class TimestampValue < ActiveRecord::Base
2
+ set_table_name "extend_at_timestamps"
3
+ belongs_to :extend_at_column, :class_name => 'Column', :foreign_key => 'extend_at_column_id'
4
+ end
@@ -1,3 +1,3 @@
1
1
  module ExtendModelAt
2
- VERSION = "0.0.3"
2
+ VERSION = "0.1.0"
3
3
  end
data/lib/extend_at.rb CHANGED
@@ -1,5 +1,7 @@
1
1
  # encoding: utf-8
2
2
  require "extend_at/version"
3
+ require "extend_at/model_manager"
4
+ require "extend_at/models/all"
3
5
 
4
6
  module ExtendModelAt
5
7
  def self.included(base)
@@ -13,6 +15,7 @@ module ExtendModelAt
13
15
  @column_name = options[:column_name].to_s
14
16
  @columns = options[:columns]
15
17
  @value = get_defaults_values options
18
+ @model_manager = ::ExtendModelAt::ModelManager.new(@column_name, @model, @columns)
16
19
 
17
20
  raise "#{@column_name} should by text or string not #{options[:model].column_for_attribute(@column_name.to_sym).type}" if not [:text, :stiring].include? options[:model].column_for_attribute(@column_name.to_sym).type
18
21
 
@@ -26,21 +29,31 @@ module ExtendModelAt
26
29
 
27
30
  initialize_values
28
31
 
32
+
29
33
  @model.attributes[@column_name] = @value
30
- @model.save(:validate => false)
31
34
  end
32
35
 
33
36
  def [](key)
34
- @value[key.to_s]
37
+ @model_manager.get_value(key)
35
38
  end
36
39
 
37
40
  def []=(key, value)
38
- if @columns[key.to_sym].kind_of? Hash and ((@columns[key.to_sym][:type] == :boolean and (not [true.class, false.class].include? value.class)) or
39
- ((not [:boolean, nil].include?(@columns[key.to_sym][:type])) and @columns[key.to_sym][:type] != value.class ))
40
- raise "#{value.inspect} is not a valid type, expected #{@columns[key.to_sym][:type]}"
41
+ if not valid_type? value, @columns[key.to_sym].try(:[],:type)
42
+ # Try to adapt the value
43
+ adapter = get_adapter key, value
44
+ raise "#{value.inspect} is not a valid type, expected #{@columns[key.to_sym][:type]}" if adapter.nil? # We can't adapt the value
45
+ value = value.send adapter
41
46
  end
42
47
  @value[key.to_s] = value
43
- @model.send :"#{@column_name}=", @value.to_yaml
48
+ @model_manager.assign(key,value)
49
+ end
50
+
51
+ def self.respond_to?(symbol, include_private=false)
52
+ true
53
+ end
54
+
55
+ def respond_to?(symbol, include_private=false)
56
+ true
44
57
  end
45
58
 
46
59
  # Use the undefined method as a column
@@ -57,6 +70,21 @@ module ExtendModelAt
57
70
 
58
71
  private
59
72
 
73
+ def get_adapter(column, value)
74
+ if @columns[column.to_sym][:type] == String
75
+ return :to_s
76
+ elsif @columns[column.to_sym][:type] == Fixnum
77
+ return :to_i if value.respond_to? :to_i
78
+ elsif @columns[column.to_sym][:type] == Float
79
+ return :to_f if value.respond_to? :to_f
80
+ elsif @columns[column.to_sym][:type] == Time
81
+ return :to_time if value.respond_to? :to_time
82
+ elsif @columns[column.to_sym][:type] == Date
83
+ return :to_date if value.respond_to? :to_date
84
+ end
85
+ nil
86
+ end
87
+
60
88
  def initialize_values
61
89
  if not @value.kind_of? Hash
62
90
  @model.attributes[@column_name] = {}.to_yaml
@@ -71,6 +99,18 @@ module ExtendModelAt
71
99
  end
72
100
  defaults_
73
101
  end
102
+
103
+ def update_model_manager
104
+ @model_manager.send :update
105
+ end
106
+
107
+ def valid_type?(value, type)
108
+ @model.send :valid_type?, value, type
109
+ end
110
+
111
+ def search(column, method, value)
112
+ @model_manager.send:search, column, method, value
113
+ end
74
114
  end
75
115
 
76
116
  module ClassMethods
@@ -104,7 +144,6 @@ module ExtendModelAt
104
144
  def []=(column, value)
105
145
  if column.to_s =~ /^#{column_name}_[a-zA-Z_][a-zA-Z_0-9]*\=?$/
106
146
  rb = \"self.#{column_name}.\#\{column.to_s.gsub(/^#{column_name}_/,'').gsub(/\=$/, '')\} = value\"
107
- puts \"Evaluando: #\{rb}\"
108
147
  eval rb, binding
109
148
  else
110
149
  super
@@ -120,9 +159,37 @@ module ExtendModelAt
120
159
  end
121
160
  end
122
161
 
162
+ # Respond to ethod like <column name>_<extended column name> for read or write
163
+ def respond_to?(symbol, include_private=false)
164
+ if symbol.to_s =~ /^#{column_name}_[a-zA-Z_][a-zA-Z_0-9]*\=?$/
165
+ return true
166
+ else
167
+ super
168
+ end
169
+ end
170
+
171
+ def self.method_missing(m, *args, &block)
172
+ if m.to_s =~ /^#{column_name}_[a-zA-Z_][a-zA-Z_0-9]+_(#\{VALID_COMPARATIONS.join('|')})$/
173
+ method = m[/(#\{VALID_COMPARATIONS.join('|')})$/]
174
+ column = m.to_s.gsub(/^#{column_name}_/, '').gsub(/_(#\{VALID_COMPARATIONS.join('|')})$/, '')
175
+
176
+ code = (self.last || self.new).send :search_in_extention, column, method, args.first
177
+
178
+ value = args.first
179
+
180
+ return eval code, binding
181
+ else
182
+ super
183
+ end
184
+ end
185
+
123
186
  # Accept method like <column name>_<extended column name> for read or write
124
187
  def method_missing(m, *args, &block)
125
- if m.to_s =~ /^#{column_name}_[a-zA-Z_][a-zA-Z_0-9]*\=$/
188
+ if m.to_s =~ /^#{column_name}_[a-zA-Z_][a-zA-Z_0-9]+_(#\{VALID_COMPARATIONS.join('|')})$/
189
+ method = m[/(#\{VALID_COMPARATIONS.join('|')})$/]
190
+ column = m.to_s.gsub(/^#{column_name}_/, '').gsub(/(#\{VALID_COMPARATIONS.join('|')})/, '')
191
+ return search_in_extention column, method, args.first
192
+ elsif m.to_s =~ /^#{column_name}_[a-zA-Z_][a-zA-Z_0-9]*\=$/
126
193
  rb = \"self.#{column_name}.\#\{m.to_s.gsub(/^#{column_name}_/, '').gsub(/\=$/, '')} = args.first\"
127
194
  return eval rb, binding
128
195
  elsif m.to_s =~ /^#{column_name}_[a-zA-Z_][a-zA-Z_0-9]*$/
@@ -132,6 +199,13 @@ module ExtendModelAt
132
199
  super
133
200
  end
134
201
  end
202
+
203
+ protected
204
+ VALID_COMPARATIONS = ['gt', 'gt_eq', 'lt', 'lt_eq', 'eq', 'in', 'match']
205
+
206
+ def search_in_extention(column, method, value)
207
+ #{column_name.to_s}.send :search, column, method, value
208
+ end
135
209
  "
136
210
 
137
211
  self.class_eval <<-EOS
@@ -141,13 +215,14 @@ module ExtendModelAt
141
215
  class_eval <<-EOV
142
216
  public
143
217
  validate :extend_at_validations
218
+ after_save :update_model_manager, :on => :create
144
219
 
145
220
  def #{column_name.to_s}
146
221
  if not @#{column_name.to_s}_configuration.kind_of? ExtendModelAt::Extention
147
222
  opts = initialize_options(#{options})
148
223
  options = {
149
224
  :extensible => true # If is false, only the columns defined in :columns can be used
150
- }.merge! opts
225
+ }.merge!(opts)
151
226
  columns = initialize_columns expand_options(options, { :not_call_symbol => [:boolean], :not_expand => [:validate, :default] }) if options.kind_of? Hash
152
227
  @#{column_name.to_s}_configuration ||= ExtendModelAt::Extention.new({:model => self, :column_name => :#{column_name.to_s}, :columns => columns})
153
228
  end
@@ -155,6 +230,8 @@ module ExtendModelAt
155
230
  end
156
231
 
157
232
  protected
233
+ VALID_SYMBOLS = [:any, :binary, :boolean, :date, :datetime, :decimal, :float, :integer, :string, :text, :time, :timestamp]
234
+
158
235
  def extend_at_validations
159
236
  self.#{column_name}.valid?
160
237
  @extend_at_validation ||= {} if not @extend_at_validation.kind_of? Hash
@@ -198,16 +275,11 @@ module ExtendModelAt
198
275
  # Stablish the type
199
276
  if config[:type].class == Class
200
277
  # If exist :type, is a static column
278
+ column_config[:type] = get_type_for_class config[:type]
279
+ elsif config[:type].class == Symbol and VALID_SYMBOLS.include? config[:type]
201
280
  column_config[:type] = config[:type]
202
281
  else
203
- # if not, is a dynamic column
204
- if config[:type].to_sym == :any
205
- column_config[:type] = nil
206
- elsif config[:type].to_sym == :boolean
207
- column_config[:type] = :boolean
208
- else
209
- raise "\#\{config[:type]\} is not a valid column type"
210
- end
282
+ raise "\#\{config[:type]\} is not a valid column type"
211
283
  end
212
284
 
213
285
  # Stablish the default value
@@ -219,8 +291,7 @@ module ExtendModelAt
219
291
  else
220
292
  # If the column have a type, we verify the type
221
293
  if not column_config[:type].nil?
222
- if (column_config[:type] == :boolean and (not [true.class, false.class].include? config[:default].class)) or
223
- ((not [:boolean, nil].include?(column_config[:type])) and column_config[:type] != config[:default].class )
294
+ if not valid_type?(config[:default], column_config[:type])
224
295
  raise "The column \#\{column\} has an invalid default value. Expected \#\{column_config[:type]}, not \#\{config[:default].class}"
225
296
  end
226
297
  column_config[:default] = config[:default]
@@ -234,7 +305,7 @@ module ExtendModelAt
234
305
  if [Symbol, Proc].include? config[:validate].class
235
306
  column_config[:validate] = config[:validate]
236
307
  create_validation_for column, config[:validate]
237
- else
308
+ elsif not config[:validate].nil?
238
309
  raise "The validation of \#\{column\} is invalid"
239
310
  end
240
311
 
@@ -242,6 +313,18 @@ module ExtendModelAt
242
313
  column_config
243
314
  end
244
315
 
316
+ def get_type_from_symbol(type)
317
+ type = type.to_s
318
+ return nil if type == 'any' or type == ''
319
+ return :boolean if type == 'boolean'
320
+ return Float if type == 'float'
321
+ return Fixnum if type == 'integer'
322
+ return String if type == 'string' or type == 'text'
323
+ return Time if type == 'time' or type == 'timestamp'
324
+ return Date if type == 'date' or type == 'datetime'
325
+ return eval type.classify
326
+ end
327
+
245
328
  def create_validation_for(column, validation)
246
329
  column = column.to_sym
247
330
  @extend_at_validation ||= {}
@@ -249,6 +332,7 @@ module ExtendModelAt
249
332
  end
250
333
 
251
334
  def expand_options(options={}, opts={})
335
+ options = get_value_of options
252
336
  config_opts = {
253
337
  :not_expand => [],
254
338
  :not_call_symbol => []
@@ -287,6 +371,40 @@ module ExtendModelAt
287
371
  return value
288
372
  end
289
373
  end
374
+
375
+ def update_model_manager
376
+ self.#{column_name}.send :update_model_manager
377
+ end
378
+
379
+ def get_type_for_class(type)
380
+ type = type.name
381
+ return :any if type == 'NilClass'
382
+ return :float if type == 'Float'
383
+ return :integer if type == 'Fixnum'
384
+ return :text if type == 'String '
385
+ return :timestamp if type == 'Time'
386
+ return :datetime if type == 'Date'
387
+ return :any
388
+ end
389
+
390
+ def compatible_type(value,type)
391
+ return true if value.class == String and [:string, :text, :binary].include? type
392
+ return true if value.class == Fixnum and [:integer, :float].include? type
393
+ return true if [true.class, false.class].include? value.class and [:boolean].include? type
394
+ return true if value.class == BigDecimal and [:decimal].include? type
395
+ return true if [Date, Time].include? value.class and [:date, :time].include? type
396
+ return true if value.class == BigDecimal and [:decimal].include? type
397
+ return true if [Date, Time, ActiveSupport::TimeWithZone].include? value.class and [:datetime, :timestamp].include? type
398
+ false
399
+ end
400
+
401
+ def valid_type?(value, type)
402
+ type = type.to_s.to_sym
403
+ [:"", :any].include? type or
404
+ value.nil? or
405
+ (type == :boolean and ([true.class, false.class].include? value.class)) or
406
+ ((not [:boolean, nil].include?(type)) and not value.nil? and compatible_type(value, type))
407
+ end
290
408
  EOV
291
409
  end
292
410
  end
@@ -0,0 +1,8 @@
1
+ Description:
2
+ Install extend_at gem
3
+
4
+ Example:
5
+ rails generate extend_at:install
6
+
7
+ This will create:
8
+ db/migrate/extend_at_tables.rb
@@ -0,0 +1,22 @@
1
+ class ExtendAt::InstallGenerator < Rails::Generators::Base
2
+ include Rails::Generators::Migration
3
+ source_root File.expand_path('../templates', __FILE__)
4
+ desc "Generate all necesaries models and migrations for extend_at gem"
5
+ def install
6
+ create_model_file
7
+ end
8
+
9
+ def self.next_migration_number(path)
10
+ Time.now.utc.strftime("%Y%m%d%H%M%S")
11
+ end
12
+
13
+ def create_model_file
14
+ # template "models/integer_value.rb", "app/models/integer_value.rb"
15
+ # template "models/float_value.rb", "app/models/float_value.rb"
16
+ # template "models/string_value.rb", "app/models/string_value.rb"
17
+ # template "models/text_value.rb", "app/models/text_value.rb"
18
+ # template "models/any_value.rb", "app/models/any_value.rb"
19
+ # template "models/column.rb", "app/models/column.rb"
20
+ migration_template "create_extend_at_tables.rb", "db/migrate/create_extend_at_tables.rb"
21
+ end
22
+ end
@@ -0,0 +1,102 @@
1
+ class CreateExtendAtTables < ActiveRecord::Migration
2
+ def up
3
+ create_table :extend_at_strings do |t|
4
+ t.belongs_to :extend_at_column
5
+ t.string :column
6
+ t.string :value
7
+ end
8
+
9
+ create_table :extend_at_texts do |t|
10
+ t.belongs_to :extend_at_column
11
+ t.string :column
12
+ t.text :value
13
+ end
14
+
15
+ create_table :extend_at_integers do |t|
16
+ t.belongs_to :extend_at_column
17
+ t.string :column
18
+ t.integer :value
19
+ end
20
+
21
+ create_table :extend_at_floats do |t|
22
+ t.belongs_to :extend_at_column
23
+ t.string :column
24
+ t.float :value
25
+ end
26
+
27
+ create_table :extend_at_decimals do |t|
28
+ t.belongs_to :extend_at_column
29
+ t.string :column
30
+ t.decimal :value
31
+ end
32
+
33
+ create_table :extend_at_datetimes do |t|
34
+ t.belongs_to :extend_at_column
35
+ t.string :column
36
+ t.datetime :value
37
+ end
38
+
39
+ create_table :extend_at_timestamps do |t|
40
+ t.belongs_to :extend_at_column
41
+ t.string :column
42
+ t.timestamp :value
43
+ end
44
+
45
+ create_table :extend_at_times do |t|
46
+ t.belongs_to :extend_at_column
47
+ t.string :column
48
+ t.time :value
49
+ end
50
+
51
+ create_table :extend_at_dates do |t|
52
+ t.belongs_to :extend_at_column
53
+ t.string :column
54
+ t.date :value
55
+ end
56
+
57
+ create_table :extend_at_binaries do |t|
58
+ t.belongs_to :extend_at_column
59
+ t.string :column
60
+ t.binary :value
61
+ end
62
+
63
+ create_table :extend_at_booleans do |t|
64
+ t.belongs_to :extend_at_column
65
+ t.string :column
66
+ t.boolean :value
67
+ end
68
+
69
+ create_table :extend_at_anies do |t|
70
+ t.belongs_to :extend_at_column
71
+ t.string :column
72
+ t.text :value
73
+ end
74
+
75
+ create_table :extend_ats do |t|
76
+ t.references :model, :polymorphic => true
77
+ end
78
+
79
+ create_table :extend_at_columns do |t|
80
+ t.belongs_to :extend_at
81
+ t.belongs_to :column, :polymorphic => true
82
+ end
83
+ end
84
+
85
+ def down
86
+ drop_table :extend_at_strings
87
+ drop_table :extend_at_texts
88
+ drop_table :extend_at_integers
89
+ drop_table :extend_at_floats
90
+ drop_table :extend_at_decimals
91
+ drop_table :extend_at_datetimes
92
+ drop_table :extend_at_timestamps
93
+ drop_table :extend_at_times
94
+ drop_table :extend_at_dates
95
+ drop_table :extend_at_binaries
96
+ drop_table :extend_at_booleans
97
+ drop_table :extend_at_anies
98
+
99
+ drop_table :extend_ats
100
+ drop_table :extend_at_columns
101
+ end
102
+ end
metadata CHANGED
@@ -1,7 +1,7 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: extend_at
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.0.3
4
+ version: 0.1.0
5
5
  prerelease:
6
6
  platform: ruby
7
7
  authors:
@@ -9,11 +9,33 @@ authors:
9
9
  autorequire:
10
10
  bindir: bin
11
11
  cert_chain: []
12
- date: 2012-01-01 00:00:00.000000000 Z
12
+ date: 2012-01-10 00:00:00.000000000 Z
13
13
  dependencies:
14
+ - !ruby/object:Gem::Dependency
15
+ name: rails
16
+ requirement: &14819500 !ruby/object:Gem::Requirement
17
+ none: false
18
+ requirements:
19
+ - - ~>
20
+ - !ruby/object:Gem::Version
21
+ version: '3.1'
22
+ type: :runtime
23
+ prerelease: false
24
+ version_requirements: *14819500
14
25
  - !ruby/object:Gem::Dependency
15
26
  name: rspec
16
- requirement: &10011740 !ruby/object:Gem::Requirement
27
+ requirement: &14799780 !ruby/object:Gem::Requirement
28
+ none: false
29
+ requirements:
30
+ - - ~>
31
+ - !ruby/object:Gem::Version
32
+ version: '2.5'
33
+ type: :development
34
+ prerelease: false
35
+ version_requirements: *14799780
36
+ - !ruby/object:Gem::Dependency
37
+ name: rspec-core
38
+ requirement: &14799380 !ruby/object:Gem::Requirement
17
39
  none: false
18
40
  requirements:
19
41
  - - ! '>='
@@ -21,7 +43,7 @@ dependencies:
21
43
  version: '0'
22
44
  type: :development
23
45
  prerelease: false
24
- version_requirements: *10011740
46
+ version_requirements: *14799380
25
47
  description: ! 'This gem allows you to extend models without migrations: This way
26
48
  you can, i.e., develop your own content types, like in Drupal.'
27
49
  email:
@@ -37,7 +59,26 @@ files:
37
59
  - Rakefile
38
60
  - extend_at.gemspec
39
61
  - lib/extend_at.rb
62
+ - lib/extend_at/model_manager.rb
63
+ - lib/extend_at/models/all.rb
64
+ - lib/extend_at/models/any_value.rb
65
+ - lib/extend_at/models/binary_value.rb
66
+ - lib/extend_at/models/boolean_value.rb
67
+ - lib/extend_at/models/column.rb
68
+ - lib/extend_at/models/date_value.rb
69
+ - lib/extend_at/models/datetime_value.rb
70
+ - lib/extend_at/models/decimal_value.rb
71
+ - lib/extend_at/models/extend_at.rb
72
+ - lib/extend_at/models/float_value.rb
73
+ - lib/extend_at/models/integer_value.rb
74
+ - lib/extend_at/models/string_value.rb
75
+ - lib/extend_at/models/text_value.rb
76
+ - lib/extend_at/models/time_value.rb
77
+ - lib/extend_at/models/timestamp_value.rb
40
78
  - lib/extend_at/version.rb
79
+ - lib/generators/extend_at/USAGE
80
+ - lib/generators/extend_at/install_generator.rb
81
+ - lib/generators/extend_at/templates/create_extend_at_tables.rb
41
82
  homepage: ''
42
83
  licenses: []
43
84
  post_install_message: