Commit 87d7ed404acf7ee97f4b1c5b6178a24bb73daed3
0 parents
Exists in
master
create from has_magic_columns
Showing
21 changed files
with
692 additions
and
0 deletions
Show diff stats
1 | +++ a/LICENSE.txt | ||
@@ -0,0 +1,22 @@ | @@ -0,0 +1,22 @@ | ||
1 | +Copyright (c) 2014 ikeqiao | ||
2 | + | ||
3 | +MIT License | ||
4 | + | ||
5 | +Permission is hereby granted, free of charge, to any person obtaining | ||
6 | +a copy of this software and associated documentation files (the | ||
7 | +"Software"), to deal in the Software without restriction, including | ||
8 | +without limitation the rights to use, copy, modify, merge, publish, | ||
9 | +distribute, sublicense, and/or sell copies of the Software, and to | ||
10 | +permit persons to whom the Software is furnished to do so, subject to | ||
11 | +the following conditions: | ||
12 | + | ||
13 | +The above copyright notice and this permission notice shall be | ||
14 | +included in all copies or substantial portions of the Software. | ||
15 | + | ||
16 | +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, | ||
17 | +EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF | ||
18 | +MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND | ||
19 | +NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE | ||
20 | +LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION | ||
21 | +OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION | ||
22 | +WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. |
1 | +++ a/README.md | ||
@@ -0,0 +1,29 @@ | @@ -0,0 +1,29 @@ | ||
1 | +# HasMagicFields | ||
2 | + | ||
3 | +TODO: Write a gem description | ||
4 | + | ||
5 | +## Installation | ||
6 | + | ||
7 | +Add this line to your application's Gemfile: | ||
8 | + | ||
9 | + gem 'has_magic_fields' | ||
10 | + | ||
11 | +And then execute: | ||
12 | + | ||
13 | + $ bundle | ||
14 | + | ||
15 | +Or install it yourself as: | ||
16 | + | ||
17 | + $ gem install has_magic_fields | ||
18 | + | ||
19 | +## Usage | ||
20 | + | ||
21 | +TODO: Write usage instructions here | ||
22 | + | ||
23 | +## Contributing | ||
24 | + | ||
25 | +1. Fork it | ||
26 | +2. Create your feature branch (`git checkout -b my-new-feature`) | ||
27 | +3. Commit your changes (`git commit -am 'Add some feature'`) | ||
28 | +4. Push to the branch (`git push origin my-new-feature`) | ||
29 | +5. Create new Pull Request |
1 | +++ a/has_magic_fields.gemspec | ||
@@ -0,0 +1,25 @@ | @@ -0,0 +1,25 @@ | ||
1 | +# coding: utf-8 | ||
2 | +lib = File.expand_path('../lib', __FILE__) | ||
3 | +$LOAD_PATH.unshift(lib) unless $LOAD_PATH.include?(lib) | ||
4 | +require 'has_magic_fields/version' | ||
5 | + | ||
6 | +Gem::Specification.new do |s| | ||
7 | + s.name = "has_magic_fields" | ||
8 | + s.version = HasMagicFields::VERSION | ||
9 | + s.authors = ["ikeqiao"] | ||
10 | + s.email = ["zhzsi@126.com"] | ||
11 | + s.description = %q{TODO: Write a gem description} | ||
12 | + s.summary = %q{TODO: Write a gem summary} | ||
13 | + s.homepage = "" | ||
14 | + s.license = "MIT" | ||
15 | + | ||
16 | + s.files = `git ls-files`.split($/) | ||
17 | + s.executables = s.files.grep(%r{^bin/}) { |f| File.basename(f) } | ||
18 | + s.test_files = s.files.grep(%r{^(test|s|features)/}) | ||
19 | + s.require_paths = ["lib"] | ||
20 | + | ||
21 | + s.add_development_dependency "bundler", "~> 1.3" | ||
22 | + s.add_development_dependency "rake" | ||
23 | + s.add_dependency("rails", [">= 4.0.0"]) | ||
24 | + | ||
25 | +end |
lib/generators/has_magic_fields/install/install_generator.rb
0 → 100644
1 | +++ a/lib/generators/has_magic_fields/install/install_generator.rb | ||
@@ -0,0 +1,24 @@ | @@ -0,0 +1,24 @@ | ||
1 | +require 'rails/generators/base' | ||
2 | + | ||
3 | +module HasMagicFields | ||
4 | + class InstallGenerator < Rails::Generators::Base | ||
5 | + include Rails::Generators::Migration | ||
6 | + | ||
7 | + source_root File.expand_path('../templates',__FILE__) | ||
8 | + | ||
9 | + desc "Add has_magic_fields migration." | ||
10 | + | ||
11 | + def self.next_migration_number(path) | ||
12 | + unless @prev_migration_nr | ||
13 | + @prev_migration_nr = Time.now.utc.strftime("%Y%m%d%H%M%S").to_i | ||
14 | + else | ||
15 | + @prev_migration_nr += 1 | ||
16 | + end | ||
17 | + @prev_migration_nr.to_s | ||
18 | + end | ||
19 | + | ||
20 | + def add_migration | ||
21 | + migration_template "migration.rb", "db/migrate/add_has_magic_fields_tables.rb" | ||
22 | + end | ||
23 | + end | ||
24 | +end |
lib/generators/has_magic_fields/install/templates/migration.rb
0 → 100644
1 | +++ a/lib/generators/has_magic_fields/install/templates/migration.rb | ||
@@ -0,0 +1,51 @@ | @@ -0,0 +1,51 @@ | ||
1 | +class AddHasMagicFieldsTables < ActiveRecord::Migration | ||
2 | + def change | ||
3 | + create_table :magic_fields do |t| | ||
4 | + t.column :name, :string | ||
5 | + t.column :pretty_name, :string | ||
6 | + t.column :datatype, :string, :default => "string" | ||
7 | + t.column :default, :string | ||
8 | + t.column :is_required, :boolean, :default => false | ||
9 | + t.column :include_blank, :boolean, :default => false | ||
10 | + t.column :allow_other, :boolean, :default => true | ||
11 | + t.column :created_at, :datetime | ||
12 | + t.column :updated_at, :datetime | ||
13 | + end | ||
14 | + | ||
15 | + create_table :magic_attributes do |t| | ||
16 | + t.column :magic_field_id, :integer | ||
17 | + t.column :magic_option_id, :integer | ||
18 | + t.column :value, :string | ||
19 | + t.column :created_at, :datetime | ||
20 | + t.column :updated_at, :datetime | ||
21 | + end | ||
22 | + | ||
23 | + create_table :magic_options do |t| | ||
24 | + t.column :magic_field_id, :integer | ||
25 | + t.column :value, :string | ||
26 | + t.column :synonym, :string | ||
27 | + t.column :created_at, :datetime | ||
28 | + t.column :updated_at, :datetime | ||
29 | + end | ||
30 | + | ||
31 | + create_table :magic_field_relationships do |t| | ||
32 | + t.column :magic_field_id, :integer | ||
33 | + t.column :owner_id, :integer | ||
34 | + t.column :owner_type, :string | ||
35 | + t.column :created_at, :datetime | ||
36 | + t.column :updated_at, :datetime | ||
37 | + end | ||
38 | + | ||
39 | + create_table :magic_attribute_relationships do |t| | ||
40 | + t.column :magic_attribute_id, :integer | ||
41 | + t.column :owner_id, :integer | ||
42 | + t.column :owner_type, :string | ||
43 | + end | ||
44 | + | ||
45 | + add_index :magic_attributes, [:magic_field_id, :magic_option_id], name:"attributes_column_option" | ||
46 | + add_index :magic_attribute_relationships, [:magic_attribute_id, :owner_id], name:"magic_attribute_owner" | ||
47 | + add_index :magic_field_relationships, [:magic_field_id, :owner_id], name:"magic_field_owner" | ||
48 | + | ||
49 | + end | ||
50 | + | ||
51 | +end | ||
0 | \ No newline at end of file | 52 | \ No newline at end of file |
1 | +++ a/lib/has_magic_fields.rb | ||
@@ -0,0 +1,10 @@ | @@ -0,0 +1,10 @@ | ||
1 | +require "has_magic_fields/version" | ||
2 | +require "has_magic_fields/models/magic_attribute" | ||
3 | +require "has_magic_fields/models/magic_field" | ||
4 | +require "has_magic_fields/models/magic_option" | ||
5 | +require "has_magic_fields/models/magic_attribute_relationship" | ||
6 | +require "has_magic_fields/models/magic_field_relationship" | ||
7 | +require "has_magic_fields/extend" | ||
8 | + | ||
9 | + | ||
10 | + |
1 | +++ a/lib/has_magic_fields/extend.rb | ||
@@ -0,0 +1,173 @@ | @@ -0,0 +1,173 @@ | ||
1 | +module HasMagicFields | ||
2 | + module Extend extend ActiveSupport::Concern | ||
3 | + module ClassMethods | ||
4 | + def has_magic_fields(options = {}) | ||
5 | + # Associations | ||
6 | + has_many :magic_attribute_relationships, :as => :owner, :dependent => :destroy | ||
7 | + has_many :magic_attributes, :through => :magic_attribute_relationships, :dependent => :destroy | ||
8 | + | ||
9 | + # Eager loading - EXPERIMENTAL! | ||
10 | + if options[:eager] | ||
11 | + class_eval do | ||
12 | + def after_initialize | ||
13 | + initialize_magic_fields | ||
14 | + end | ||
15 | + end | ||
16 | + end | ||
17 | + | ||
18 | + # Inheritence | ||
19 | + cattr_accessor :inherited_from | ||
20 | + | ||
21 | + # if options[:through] is supplied, treat as an inherited relationship | ||
22 | + if self.inherited_from = options[:through] | ||
23 | + class_eval do | ||
24 | + def inherited_magic_fields | ||
25 | + raise "Cannot inherit MagicFields from a non-existant association: #{@inherited_from}" unless self.class.method_defined?(inherited_from)# and self.send(inherited_from) | ||
26 | + self.send(inherited_from).magic_fields | ||
27 | + end | ||
28 | + end | ||
29 | + alias_method :magic_fields, :inherited_magic_fields unless method_defined? :magic_fields | ||
30 | + | ||
31 | + # otherwise the calling model has the relationships | ||
32 | + else | ||
33 | + has_many :magic_field_relationships, :as => :owner, :dependent => :destroy | ||
34 | + has_many :magic_fields, :through => :magic_field_relationships, :dependent => :destroy | ||
35 | + end | ||
36 | + | ||
37 | + # # Hook into Base | ||
38 | + class_eval do | ||
39 | + alias_method :reload_without_magic, :reload | ||
40 | + alias_method :create_or_update_without_magic, :create_or_update | ||
41 | + alias_method :read_attribute_without_magic, :read_attribute | ||
42 | + end | ||
43 | + | ||
44 | + | ||
45 | + # Add Magic to Base | ||
46 | + alias_method :reload, :reload_with_magic | ||
47 | + alias_method :read_attribute, :read_attribute_with_magic | ||
48 | + alias_method :create_or_update, :create_or_update_with_magic | ||
49 | + end | ||
50 | + end | ||
51 | + | ||
52 | + included do | ||
53 | + # Reinitialize MagicFields and MagicAttributes when Model is reloaded | ||
54 | + def reload_with_magic | ||
55 | + initialize_magic_fields | ||
56 | + reload_without_magic | ||
57 | + end | ||
58 | + | ||
59 | + def update_attributes(new_attributes) | ||
60 | + attributes = new_attributes.stringify_keys | ||
61 | + magic_attrs = magic_fields.map(&:name) | ||
62 | + | ||
63 | + super(attributes.select{ |k, v| !magic_attrs.include?(k) }) | ||
64 | + attributes.select{ |k, v| magic_attrs.include?(k) }.each do |k, v| | ||
65 | + col = find_magic_field_by_name(k) | ||
66 | + attr = find_magic_attribute_by_column(col).first | ||
67 | + attr.update_attributes(:value => v) | ||
68 | + end | ||
69 | + end | ||
70 | + | ||
71 | + private | ||
72 | + | ||
73 | + # Save MagicAttributes from @attributes | ||
74 | + def create_or_update_with_magic | ||
75 | + if result = create_or_update_without_magic | ||
76 | + magic_fields.each do |column| | ||
77 | + value = @attributes[column.name] | ||
78 | + existing = find_magic_attribute_by_column(column) | ||
79 | + | ||
80 | + unless column.datatype == 'check_box_multiple' | ||
81 | + (attr = existing.first) ? | ||
82 | + update_magic_attribute(attr, value) : | ||
83 | + create_magic_attribute(column, value) | ||
84 | + else | ||
85 | + #TODO - make this more efficient | ||
86 | + value = [value] unless value.is_a? Array | ||
87 | + existing.map(&:destroy) if existing | ||
88 | + value.collect {|v| create_magic_attribute(column, v)} | ||
89 | + end | ||
90 | + end | ||
91 | + end | ||
92 | + result | ||
93 | + end | ||
94 | + | ||
95 | + # Load (lazily) MagicAttributes or fall back | ||
96 | + def method_missing(method_id, *args) | ||
97 | + super(method_id, *args) | ||
98 | + rescue NoMethodError | ||
99 | + method_name = method_id.to_s | ||
100 | + attr_names = magic_fields.map(&:name) | ||
101 | + initialize_magic_fields and retry if attr_names.include?(method_name) or | ||
102 | + (md = /[\?|\=]/.match(method_name) and | ||
103 | + attr_names.include?(md.pre_match)) | ||
104 | + super(method_id, *args) | ||
105 | + end | ||
106 | + | ||
107 | + # Load the MagicAttribute(s) associated with attr_name and cast them to proper type. | ||
108 | + def read_attribute_with_magic(attr_name) | ||
109 | + return read_attribute_without_magic(attr_name) if column_for_attribute(attr_name) # filter for regular columns | ||
110 | + attr_name = attr_name.to_s | ||
111 | + | ||
112 | + if !(value = @attributes[attr_name]).nil? | ||
113 | + if column = find_magic_field_by_name(attr_name) | ||
114 | + if value.is_a? Array | ||
115 | + value.map {|v| column.type_cast(v)} | ||
116 | + else | ||
117 | + column.type_cast(value) | ||
118 | + end | ||
119 | + else | ||
120 | + value | ||
121 | + end | ||
122 | + else | ||
123 | + nil | ||
124 | + end | ||
125 | + end | ||
126 | + | ||
127 | + # Lookup all MagicAttributes and setup @attributes | ||
128 | + def initialize_magic_fields | ||
129 | + magic_fields.each do |column| | ||
130 | + attribute = find_magic_attribute_by_column(column) | ||
131 | + name = column.name | ||
132 | + | ||
133 | + # Validation | ||
134 | + self.class.validates_presence_of(name) if column.is_required? | ||
135 | + | ||
136 | + # Write attribute | ||
137 | + unless column.datatype == 'check_box_multiple' | ||
138 | + (attr = attribute.first) ? | ||
139 | + write_magic_attribute(name, attr.to_s) : | ||
140 | + write_magic_attribute(name, column.default) | ||
141 | + else | ||
142 | + write_magic_attribute(name, attribute.map(&:to_s)) | ||
143 | + end | ||
144 | + end | ||
145 | + end | ||
146 | + | ||
147 | + | ||
148 | + def write_magic_attribute(magic_attribute, value) | ||
149 | + @attributes[magic_attribute] = value | ||
150 | + @attributes_cache[magic_attribute] = value | ||
151 | + end | ||
152 | + | ||
153 | + def find_magic_attribute_by_column(column) | ||
154 | + magic_attributes.to_a.find_all {|attr| attr.magic_field_id == column.id} | ||
155 | + end | ||
156 | + | ||
157 | + def find_magic_field_by_name(attr_name) | ||
158 | + magic_fields.to_a.find {|column| column.name == attr_name} | ||
159 | + end | ||
160 | + | ||
161 | + def create_magic_attribute(magic_field, value) | ||
162 | + magic_attributes << MagicAttribute.create(:magic_field => magic_field, :value => value) | ||
163 | + end | ||
164 | + | ||
165 | + def update_magic_attribute(magic_attribute, value) | ||
166 | + magic_attribute.update_attributes(:value => value) | ||
167 | + end | ||
168 | + end | ||
169 | + | ||
170 | + end | ||
171 | +end | ||
172 | + | ||
173 | + |
1 | +++ a/lib/has_magic_fields/models/magic_attribute.rb | ||
@@ -0,0 +1,29 @@ | @@ -0,0 +1,29 @@ | ||
1 | +# Always work through the interface MagicAttribute.value | ||
2 | +class MagicAttribute < ActiveRecord::Base | ||
3 | + belongs_to :magic_field | ||
4 | + belongs_to :magic_option | ||
5 | + | ||
6 | + before_save :update_magic | ||
7 | + | ||
8 | + def to_s | ||
9 | + (magic_option) ? magic_option.value : value | ||
10 | + end | ||
11 | + | ||
12 | + def update_magic | ||
13 | + if option = find_magic_option_for(value) | ||
14 | + unless magic_option and magic_option == option | ||
15 | + self.value = nil | ||
16 | + self.magic_option = option | ||
17 | + end | ||
18 | + elsif magic_field.allow_other | ||
19 | + self.magic_option = nil | ||
20 | + end | ||
21 | + end | ||
22 | + | ||
23 | +private | ||
24 | + | ||
25 | + def find_magic_option_for(value) | ||
26 | + magic_field.magic_options.find(:first, | ||
27 | + :conditions => ["value = ? or synonym = ?", value, value]) unless magic_field.nil? or magic_field.magic_options.blank? | ||
28 | + end | ||
29 | +end |
lib/has_magic_fields/models/magic_attribute_relationship.rb
0 → 100644
1 | +++ a/lib/has_magic_fields/models/magic_field.rb | ||
@@ -0,0 +1,54 @@ | @@ -0,0 +1,54 @@ | ||
1 | +class MagicField < ActiveRecord::Base | ||
2 | + has_many :magic_field_relationships | ||
3 | + has_many :owners, :through => :magic_field_relationships, :as => :owner | ||
4 | + has_many :magic_options | ||
5 | + has_many :magic_attributes, :dependent => :destroy | ||
6 | + | ||
7 | + validates_presence_of :name, :datatype | ||
8 | + validates_format_of :name, :with => /\A[a-z][a-z0-9_]+\z/ | ||
9 | + | ||
10 | + def type_cast(value) | ||
11 | + begin | ||
12 | + case datatype.to_sym | ||
13 | + when :check_box_boolean | ||
14 | + (value.to_int == 1) ? true : false | ||
15 | + when :date | ||
16 | + Date.parse(value) | ||
17 | + when :datetime | ||
18 | + Time.parse(value) | ||
19 | + when :integer | ||
20 | + value.to_int | ||
21 | + else | ||
22 | + value | ||
23 | + end | ||
24 | + rescue | ||
25 | + value | ||
26 | + end | ||
27 | + end | ||
28 | + | ||
29 | + # Display a nicer (possibly user-defined) name for the column or use a fancified default. | ||
30 | + def pretty_name | ||
31 | + super || name.humanize | ||
32 | + end | ||
33 | + | ||
34 | + #get or set a variable with the variable as the called method | ||
35 | + def self.method_missing(method, *args) | ||
36 | + debugger | ||
37 | + | ||
38 | + method_name = method.to_s | ||
39 | + super(method, *args) | ||
40 | + rescue NoMethodError | ||
41 | + debugger | ||
42 | + #set a value for a variable | ||
43 | + if method_name =~ /=$/ | ||
44 | + var_name = method_name.gsub('=', '') | ||
45 | + value = args.first | ||
46 | + self[var_name] = value | ||
47 | + #retrieve a value | ||
48 | + else | ||
49 | + self[method_name] | ||
50 | + end | ||
51 | + end | ||
52 | + | ||
53 | + | ||
54 | +end |
1 | +++ a/lib/has_magic_fields/models/magic_option.rb | ||
@@ -0,0 +1,7 @@ | @@ -0,0 +1,7 @@ | ||
1 | +class MagicOption < ActiveRecord::Base | ||
2 | + belongs_to :magic_field | ||
3 | + | ||
4 | + validates_presence_of :value | ||
5 | + validates_uniqueness_of :value, :scope => :magic_field_id | ||
6 | + validates_uniqueness_of :synonym, :scope => :magic_field_id, :if => Proc.new{|this| !this.synonym.nil? and !this.synonym.empty?} | ||
7 | +end | ||
0 | \ No newline at end of file | 8 | \ No newline at end of file |
1 | +++ a/spec/has_magic_fields/magic_fileds_spec.rb | ||
@@ -0,0 +1,105 @@ | @@ -0,0 +1,105 @@ | ||
1 | +require 'spec_helper' | ||
2 | + | ||
3 | + | ||
4 | +describe HasMagicFields do | ||
5 | + | ||
6 | + context "on a single model" do | ||
7 | + before(:each) do | ||
8 | + @charlie = Person.create(name: "charlie") | ||
9 | + end | ||
10 | + | ||
11 | + it "initializes magic fields correctly" do | ||
12 | + expect(@charlie).not_to be(nil) | ||
13 | + expect(@charlie.class).to be(Person) | ||
14 | + expect(@charlie.magic_fields).not_to be(nil) | ||
15 | + end | ||
16 | + | ||
17 | + it "allows adding a magic field" do | ||
18 | + @charlie.magic_fields.create(:name => 'salary') | ||
19 | + expect(@charlie.magic_fields.length).to be(1) | ||
20 | + end | ||
21 | + | ||
22 | + it "allows setting and saving of magic attributes" do | ||
23 | + @charlie.magic_fields.create(:name => 'salary') | ||
24 | + @charlie.salary = 50000 | ||
25 | + @charlie.save | ||
26 | + @charlie = Person.find(@charlie.id) | ||
27 | + expect(@charlie.salary).not_to be(nil) | ||
28 | + end | ||
29 | + | ||
30 | + # it "forces required if is_required is true" do | ||
31 | + # # TODO figure out why this fails | ||
32 | + # @charlie.magic_fields.create(:name => "last_name", :is_required => true) | ||
33 | + # expect(@charlie.save).to be(false) | ||
34 | + # end | ||
35 | + | ||
36 | + it "allows datatype to be :date" do | ||
37 | + @charlie.magic_fields.create(:name => "birthday", :datatype => :date) | ||
38 | + @charlie.birthday = Date.today | ||
39 | + expect(@charlie.save).to be(true) | ||
40 | + end | ||
41 | + | ||
42 | + it "allows datatype to be :datetime" do | ||
43 | + @charlie.magic_fields.create(:name => "signed_up_at", :datatype => :datetime) | ||
44 | + @charlie.signed_up_at = DateTime.now | ||
45 | + expect(@charlie.save).to be(true) | ||
46 | + end | ||
47 | + | ||
48 | + it "allows datatype to be :integer" do | ||
49 | + @charlie.magic_fields.create(:name => "age", :datatype => :integer) | ||
50 | + @charlie.age = 5 | ||
51 | + expect(@charlie.save).to be(true) | ||
52 | + end | ||
53 | + | ||
54 | + it "allows datatype to be :check_box_boolean" do | ||
55 | + @charlie.magic_fields.create(:name => "retired", :datatype => :check_box_boolean) | ||
56 | + @charlie.retired = false | ||
57 | + expect(@charlie.save).to be(true) | ||
58 | + end | ||
59 | + | ||
60 | + it "allows default to be set" do | ||
61 | + @charlie.magic_fields.create(:name => "bonus", :default => "40000") | ||
62 | + expect(@charlie.bonus).to eq("40000") | ||
63 | + end | ||
64 | + | ||
65 | + it "allows a pretty display name to be set" do | ||
66 | + @charlie.magic_fields.create(:name => "zip", :pretty_name => "Zip Code") | ||
67 | + expect(@charlie.magic_fields.last.pretty_name).to eq("Zip Code") | ||
68 | + end | ||
69 | + end | ||
70 | + | ||
71 | + context "in a parent-child relationship" do | ||
72 | + before(:each) do | ||
73 | + @account = Account.create(name:"important") | ||
74 | + @alice = User.create(name:"alice", account: @account ) | ||
75 | + end | ||
76 | + | ||
77 | + it "initializes magic fields correctly" do | ||
78 | + expect(@alice).not_to be(nil) | ||
79 | + expect(@alice.class).to be(User) | ||
80 | + expect(@alice.magic_fields).not_to be(nil) | ||
81 | + | ||
82 | + expect(@account).not_to be(nil) | ||
83 | + expect(@account.class).to be(Account) | ||
84 | + expect(@alice.magic_fields).not_to be(nil) | ||
85 | + end | ||
86 | + | ||
87 | + it "allows adding a magic field to the child" do | ||
88 | + @alice.magic_fields.create(:name => 'salary') | ||
89 | + expect(lambda{@alice.salary}).not_to raise_error | ||
90 | + expect(lambda{@account.reload_with_magic.salary}).not_to raise_error | ||
91 | + end | ||
92 | + | ||
93 | + it "allows adding a magic field to the parent" do | ||
94 | + @account.magic_fields.create(:name => 'age') | ||
95 | + expect(lambda{@alice.reload_with_magic.age}).not_to raise_error | ||
96 | + end | ||
97 | + | ||
98 | + it "sets magic fields for all child models" do | ||
99 | + @bob = User.create(name:"bob", account: @account ) | ||
100 | + @bob.magic_fields.create(:name => 'birthday') | ||
101 | + @alice.reload_with_magic | ||
102 | + expect(lambda{@alice.birthday}).not_to raise_error | ||
103 | + end | ||
104 | + end | ||
105 | +end |
1 | +++ a/spec/spec_helper.rb | ||
@@ -0,0 +1,108 @@ | @@ -0,0 +1,108 @@ | ||
1 | +require 'rubygems' | ||
2 | +require "active_record" | ||
3 | +require 'active_support' | ||
4 | +require 'sqlite3' | ||
5 | +require 'active_record/fixtures' | ||
6 | +require 'debugger' | ||
7 | + | ||
8 | + | ||
9 | +$LOAD_PATH.unshift(File.dirname(__FILE__)) | ||
10 | +$LOAD_PATH.unshift(File.join(File.dirname(__FILE__), '..', 'rails')) | ||
11 | +require "init" | ||
12 | + | ||
13 | +require "rails/railtie" | ||
14 | + | ||
15 | +# ActiveRecord::Base.logger = Logger.new(STDOUT) | ||
16 | +ActiveRecord::Base.establish_connection(:adapter => "sqlite3", :database => ":memory:") | ||
17 | +# ActiveRecord::Base.configurations = true | ||
18 | + | ||
19 | +ActiveRecord::Schema.verbose = false | ||
20 | + | ||
21 | +ActiveRecord::Schema.define do | ||
22 | + | ||
23 | + create_table "users", :force => true do |t| | ||
24 | + t.column "name", :text | ||
25 | + t.column "account_id", :integer | ||
26 | + end | ||
27 | + | ||
28 | + create_table "accounts", :force => true do |t| | ||
29 | + t.column "name", :text | ||
30 | + end | ||
31 | + | ||
32 | + create_table "people", :force => true do |t| | ||
33 | + t.column "name", :text | ||
34 | + end | ||
35 | + | ||
36 | + | ||
37 | + # Has Magic Columns migration generator output | ||
38 | + | ||
39 | + create_table :magic_fields do |t| | ||
40 | + t.column :name, :string | ||
41 | + t.column :pretty_name, :string | ||
42 | + t.column :datatype, :string, :default => "string" | ||
43 | + t.column :default, :string | ||
44 | + t.column :is_required, :boolean, :default => false | ||
45 | + t.column :include_blank, :boolean, :default => false | ||
46 | + t.column :allow_other, :boolean, :default => true | ||
47 | + t.column :created_at, :datetime | ||
48 | + t.column :updated_at, :datetime | ||
49 | + end | ||
50 | + | ||
51 | + create_table :magic_attributes do |t| | ||
52 | + t.column :magic_field_id, :integer | ||
53 | + t.column :magic_option_id, :integer | ||
54 | + t.column :value, :string | ||
55 | + t.column :created_at, :datetime | ||
56 | + t.column :updated_at, :datetime | ||
57 | + end | ||
58 | + | ||
59 | + create_table :magic_options do |t| | ||
60 | + t.column :magic_field_id, :integer | ||
61 | + t.column :value, :string | ||
62 | + t.column :synonym, :string | ||
63 | + t.column :created_at, :datetime | ||
64 | + t.column :updated_at, :datetime | ||
65 | + end | ||
66 | + | ||
67 | + create_table :magic_field_relationships do |t| | ||
68 | + t.column :magic_field_id, :integer | ||
69 | + t.column :owner_id, :integer | ||
70 | + t.column :owner_type, :string | ||
71 | + t.column :created_at, :datetime | ||
72 | + t.column :updated_at, :datetime | ||
73 | + end | ||
74 | + | ||
75 | + create_table :magic_attribute_relationships do |t| | ||
76 | + t.column :magic_attribute_id, :integer | ||
77 | + t.column :owner_id, :integer | ||
78 | + t.column :owner_type, :string | ||
79 | + end | ||
80 | + | ||
81 | +end | ||
82 | + | ||
83 | + | ||
84 | +RSpec.configure do |config| | ||
85 | + | ||
86 | + config.before(:all) do | ||
87 | + class Account < ActiveRecord::Base | ||
88 | + include HasMagicFields::Extend | ||
89 | + has_many :users | ||
90 | + has_magic_fields | ||
91 | + end | ||
92 | + | ||
93 | + class Person < ActiveRecord::Base | ||
94 | + include HasMagicFields::Extend | ||
95 | + has_magic_fields | ||
96 | + end | ||
97 | + | ||
98 | + class User < ActiveRecord::Base | ||
99 | + include HasMagicFields::Extend | ||
100 | + belongs_to :account | ||
101 | + has_magic_fields :through => :account | ||
102 | + end | ||
103 | + end | ||
104 | + | ||
105 | + config.after(:all) do | ||
106 | + end | ||
107 | +end | ||
108 | + |