aa_associations 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.
Files changed (98) hide show
  1. data/.gitignore +53 -0
  2. data/Gemfile +10 -0
  3. data/MIT_LICENSE.txt +20 -0
  4. data/README.md +213 -0
  5. data/Rakefile +17 -0
  6. data/aa_associations.gemspec +31 -0
  7. data/app/controllers/autocomplete_controller.rb +37 -0
  8. data/app/helpers/active_admin_associations_helper.rb +77 -0
  9. data/app/views/admin/shared/_add_to_association.html.erb +21 -0
  10. data/app/views/admin/shared/_association_collection_table_actions.html.erb +4 -0
  11. data/app/views/admin/shared/_blank_slate.html.erb +3 -0
  12. data/app/views/admin/shared/_collection_table.html.erb +58 -0
  13. data/app/views/admin/shared/_form.html.erb +7 -0
  14. data/config/routes.rb +7 -0
  15. data/lib/aa_associations.rb +20 -0
  16. data/lib/active_admin_associations/active_admin_extensions.rb +13 -0
  17. data/lib/active_admin_associations/association_actions.rb +43 -0
  18. data/lib/active_admin_associations/association_config.rb +50 -0
  19. data/lib/active_admin_associations/autocompleter.rb +64 -0
  20. data/lib/active_admin_associations/engine.rb +25 -0
  21. data/lib/active_admin_associations/form_config_dsl.rb +15 -0
  22. data/lib/active_admin_associations/redirect_destroy_actions.rb +7 -0
  23. data/lib/active_admin_associations/version.rb +3 -0
  24. data/lib/formtastic/inputs/token_input.rb +43 -0
  25. data/lib/formtastic/token_input_default_for_association.rb +19 -0
  26. data/test/active_admin_associations_test.rb +69 -0
  27. data/test/admin_posts_controller_test.rb +52 -0
  28. data/test/association_config_test.rb +43 -0
  29. data/test/autocomplete_controller_test.rb +27 -0
  30. data/test/autocompleter_test.rb +62 -0
  31. data/test/dummy/README.rdoc +261 -0
  32. data/test/dummy/Rakefile +7 -0
  33. data/test/dummy/app/admin/dashboards.rb +44 -0
  34. data/test/dummy/app/admin/posts.rb +20 -0
  35. data/test/dummy/app/admin/tags.rb +11 -0
  36. data/test/dummy/app/assets/javascripts/active_admin.js +8 -0
  37. data/test/dummy/app/assets/javascripts/application.js +15 -0
  38. data/test/dummy/app/assets/stylesheets/active_admin.css.scss +6 -0
  39. data/test/dummy/app/assets/stylesheets/application.css +13 -0
  40. data/test/dummy/app/controllers/application_controller.rb +3 -0
  41. data/test/dummy/app/helpers/application_helper.rb +2 -0
  42. data/test/dummy/app/mailers/.gitkeep +0 -0
  43. data/test/dummy/app/models/.gitkeep +0 -0
  44. data/test/dummy/app/models/admin_user.rb +10 -0
  45. data/test/dummy/app/models/post.rb +11 -0
  46. data/test/dummy/app/models/tag.rb +15 -0
  47. data/test/dummy/app/models/tagging.rb +7 -0
  48. data/test/dummy/app/models/user.rb +8 -0
  49. data/test/dummy/app/views/layouts/application.html.erb +14 -0
  50. data/test/dummy/config.ru +4 -0
  51. data/test/dummy/config/application.rb +63 -0
  52. data/test/dummy/config/boot.rb +10 -0
  53. data/test/dummy/config/database.yml +19 -0
  54. data/test/dummy/config/environment.rb +5 -0
  55. data/test/dummy/config/environments/development.rb +37 -0
  56. data/test/dummy/config/environments/production.rb +67 -0
  57. data/test/dummy/config/environments/test.rb +37 -0
  58. data/test/dummy/config/initializers/active_admin.rb +129 -0
  59. data/test/dummy/config/initializers/backtrace_silencers.rb +7 -0
  60. data/test/dummy/config/initializers/devise.rb +216 -0
  61. data/test/dummy/config/initializers/inflections.rb +15 -0
  62. data/test/dummy/config/initializers/mime_types.rb +5 -0
  63. data/test/dummy/config/initializers/secret_token.rb +7 -0
  64. data/test/dummy/config/initializers/session_store.rb +8 -0
  65. data/test/dummy/config/initializers/wrap_parameters.rb +14 -0
  66. data/test/dummy/config/locales/devise.en.yml +57 -0
  67. data/test/dummy/config/locales/en.yml +5 -0
  68. data/test/dummy/config/routes.rb +62 -0
  69. data/test/dummy/db/.gitignore +1 -0
  70. data/test/dummy/db/migrate/20120504220404_devise_create_admin_users.rb +52 -0
  71. data/test/dummy/db/migrate/20120504221534_create_posts.rb +12 -0
  72. data/test/dummy/db/migrate/20120504221936_create_users.rb +9 -0
  73. data/test/dummy/db/migrate/20120504222040_create_tags.rb +8 -0
  74. data/test/dummy/db/migrate/20120504222247_create_taggings.rb +10 -0
  75. data/test/dummy/db/schema.rb +65 -0
  76. data/test/dummy/lib/assets/.gitkeep +0 -0
  77. data/test/dummy/public/404.html +26 -0
  78. data/test/dummy/public/422.html +26 -0
  79. data/test/dummy/public/500.html +25 -0
  80. data/test/dummy/public/favicon.ico +0 -0
  81. data/test/dummy/script/rails +6 -0
  82. data/test/dummy/test/factories/admin_users.rb +9 -0
  83. data/test/dummy/test/factories/posts.rb +11 -0
  84. data/test/dummy/test/factories/taggings.rb +6 -0
  85. data/test/dummy/test/factories/tags.rb +7 -0
  86. data/test/dummy/test/factories/users.rb +8 -0
  87. data/test/dummy/test/unit/admin_user_test.rb +7 -0
  88. data/test/dummy/test/unit/post_test.rb +7 -0
  89. data/test/dummy/test/unit/tag_test.rb +7 -0
  90. data/test/dummy/test/unit/tagging_test.rb +7 -0
  91. data/test/dummy/test/unit/user_test.rb +7 -0
  92. data/test/support/should_change.rb +29 -0
  93. data/test/test_helper.rb +51 -0
  94. data/vendor/assets/javascripts/active_admin_associations.js +14 -0
  95. data/vendor/assets/javascripts/jquery.tokeninput.js +915 -0
  96. data/vendor/assets/stylesheets/active_admin_associations.css.scss +18 -0
  97. data/vendor/assets/stylesheets/token-input-facebook.css +121 -0
  98. metadata +362 -0
@@ -0,0 +1,21 @@
1
+ <%- if resource_administrated?(relationship_class) && relationship_class.respond_to?(:autocomplete_attribute) && relationship_class.autocomplete_attribute.present? -%>
2
+ <%= form_tag relate_to_url(object), :class => 'relate-to-form', :method => :put do %>
3
+ <fieldset class="inputs">
4
+ <h4>Add a <%= relationship_class.model_name.human %></h4>
5
+ <%= hidden_field_tag 'relationship_name', relationship %>
6
+ <ol>
7
+ <li class="label">
8
+ <label for="related_id">
9
+ <%= relationship_class.model_name.human %>
10
+ <%= relationship_class.human_attribute_name(relationship_class.autocomplete_attribute) %>
11
+ </label>
12
+ </li>
13
+ <li class="input">
14
+ <%= hidden_field_tag 'related_id', nil, :class => 'token-input',
15
+ 'data-model-name' => relationship_class.model_name.singular %>
16
+ </li>
17
+ <li class="button"><%= submit_tag "Add #{relationship_class.model_name.human}" %></li>
18
+ </ol>
19
+ </fieldset>
20
+ <% end %>
21
+ <%- end -%>
@@ -0,0 +1,4 @@
1
+ <%= link_to "Edit", edit_url_for(record) %>
2
+ <%= button_to "Unrelate", {:action => :unrelate, :id => object.to_param,
3
+ :relationship_name => relationship,
4
+ :related_id => record.id}, :method => :put %>
@@ -0,0 +1,3 @@
1
+ <div class="blank_slate_container">
2
+ <span class="blank_slate flash"><%= blank_text %></span>
3
+ </div>
@@ -0,0 +1,58 @@
1
+ <div class="relationship-table" id="relationship-table-<%= relationship %>">
2
+ <h3><%= object.class.human_attribute_name(relationship) %></h3>
3
+ <%= render :partial => 'admin/shared/add_to_association', :locals => {
4
+ :relationship_class => relationship_class,
5
+ :object => object,
6
+ :relationship => relationship
7
+ } %>
8
+ <%- if collection.present? -%>
9
+ <table class="index_table">
10
+ <thead>
11
+ <tr>
12
+ <th>ID</th>
13
+ <% columns.each do |column| %>
14
+ <th><%= relationship_class.human_attribute_name(column) %></th>
15
+ <%- end -%>
16
+ <th>&nbsp;</th>
17
+ </tr>
18
+ </thead>
19
+ <tbody>
20
+ <%- collection.each do |record| -%>
21
+ <tr class="<%= cycle("odd", "even") -%>">
22
+ <td><%= record.id %></td>
23
+ <%- columns.each do |column| -%>
24
+ <%- if record.send(column).is_a?(ActiveRecord::Base) -%>
25
+ <td><%= display_name_for(record.send(column)) %></td>
26
+ <%- else -%>
27
+ <td><%= record.send(column) %></td>
28
+ <%- end -%>
29
+ <%- end -%>
30
+ <td class="actions">
31
+ <%- if resource_administrated?(record.class) -%>
32
+ <%= render :partial => 'admin/shared/association_collection_table_actions', :locals => {
33
+ :record => record,
34
+ :object => object,
35
+ :relationship => relationship
36
+ } %>
37
+ <%- else -%>
38
+ &nbsp;
39
+ <%- end -%>
40
+ </td>
41
+ </tr>
42
+ <%- end -%>
43
+ </tbody>
44
+ </table>
45
+ <div class="resource-table-footer">
46
+ <%= paginate collection, :remote => true, :params => {
47
+ :action => 'page_related', :relationship_name => relationship
48
+ } %>
49
+ <div class="pagination_information">
50
+ <%= raw page_entries_info(collection) %>
51
+ </div>
52
+ </div>
53
+ <%- else -%>
54
+ <%= render :partial => 'admin/shared/blank_slate', :locals => {
55
+ :blank_text => "There are no #{object.class.human_attribute_name(relationship)}"
56
+ } %>
57
+ <%- end -%>
58
+ </div>
@@ -0,0 +1,7 @@
1
+ <%= admin_form_for resource %>
2
+
3
+ <%- if resource.persisted? && active_admin_config.form_associations.present? -%>
4
+ <%- active_admin_config.form_associations.each do |association| -%>
5
+ <%= collection_relationship_manager(resource, association) %>
6
+ <%- end -%>
7
+ <%- end -%>
@@ -0,0 +1,7 @@
1
+ if Rails.application.config.aa_associations.autocomplete_models
2
+ models = Rails.application.config.aa_associations.autocomplete_models.join('|')
3
+ Rails.application.routes.draw do
4
+ match '/autocomplete/:model' => 'autocomplete#index', :model => /(#{models})/,
5
+ :defaults => { :format => 'json' }
6
+ end
7
+ end
@@ -0,0 +1,20 @@
1
+ require 'active_support'
2
+ require 'activeadmin'
3
+ require 'active_admin_associations/version'
4
+ require 'active_admin_associations/active_admin_extensions'
5
+ require 'formtastic/token_input_default_for_association'
6
+ require 'formtastic/inputs/token_input'
7
+
8
+ module ActiveAdminAssociations
9
+ extend ActiveSupport::Autoload
10
+
11
+ eager_autoload do
12
+ autoload :AssociationActions
13
+ autoload :FormConfigDSL
14
+ autoload :RedirectDestroyActions
15
+ autoload :Autocompleter
16
+ autoload :AssociationConfig
17
+ end
18
+ end
19
+
20
+ require 'active_admin_associations/engine'
@@ -0,0 +1,13 @@
1
+ module ActiveAdmin
2
+ class Resource
3
+ attr_accessor :form_columns
4
+ attr_accessor :form_associations
5
+ attr_accessor :active_association_form
6
+ end
7
+
8
+ class << self
9
+ def resources
10
+ application.namespaces.values.map{|n| n.resources.resources }.flatten.compact.map(&:resource_class)
11
+ end
12
+ end
13
+ end
@@ -0,0 +1,43 @@
1
+ module ActiveAdminAssociations
2
+ module AssociationActions
3
+ def association_actions
4
+ member_action :unrelate, :method => :put do
5
+ reflection = resource_class.reflect_on_association(params[:relationship_name].to_sym)
6
+ if reflection.collection?
7
+ related_record = reflection.klass.find(params[:related_id])
8
+ resource.send(params[:relationship_name]).delete(related_record)
9
+ else
10
+ resource.update_attribute("#{params[:relationship_name]}_id", nil)
11
+ end
12
+ flash[:notice] = "The recored has been unrelated."
13
+ redirect_to request.headers["Referer"].presence || admin_dashboard_url
14
+ end
15
+
16
+ member_action :relate, :method => :put do
17
+ reflection = resource_class.reflect_on_association(params[:relationship_name].to_sym)
18
+ if reflection.collection?
19
+ record_to_relate = reflection.klass.find(params[:related_id])
20
+ resource.send(params[:relationship_name]) << record_to_relate
21
+ else
22
+ resource.update_attribute("#{params[:relationship_name]}_id", record_to_relate)
23
+ end
24
+ flash[:notice] = "The recored has been related."
25
+ redirect_to request.headers["Referer"].presence || admin_dashboard_url
26
+ end
27
+
28
+ member_action :page_related, :method => :get do
29
+ relationship_name = params[:relationship_name].to_sym
30
+ association_config = active_admin_config.form_associations[relationship_name]
31
+ relationship_class = resource_class.reflect_on_association(relationship_name).klass
32
+ association_columns = association_config.fields.presence || relationship_class.content_columns
33
+ render :partial => 'admin/shared/collection_table', :locals => {
34
+ :object => resource,
35
+ :collection => resource.send(relationship_name).page(params[:page]),
36
+ :relationship => relationship_name,
37
+ :columns => association_columns,
38
+ :relationship_class => relationship_class
39
+ }
40
+ end
41
+ end
42
+ end
43
+ end
@@ -0,0 +1,50 @@
1
+ module ActiveAdminAssociations
2
+ class AssociationConfig
3
+ include Enumerable
4
+
5
+ class Association
6
+ attr_reader :name
7
+ attr_reader :fields
8
+
9
+ def initialize(name, fields = [], &block)
10
+ @name = name.to_sym
11
+ @fields = fields
12
+ instance_exec(&block) if block_given?
13
+ end
14
+
15
+ def field(name)
16
+ @fields << name
17
+ end
18
+
19
+ def fields(*names)
20
+ @fields += names
21
+ end
22
+ end
23
+
24
+ attr_reader :association_configs
25
+ delegate :each, :to => :association_configs
26
+
27
+ def initialize(&block)
28
+ @association_configs = []
29
+ instance_exec(&block)
30
+ end
31
+
32
+ def blank?
33
+ @association_configs.blank?
34
+ end
35
+
36
+ def [](association_name)
37
+ @association_configs.detect {|a| a.name == association_name.to_sym }
38
+ end
39
+
40
+ def association(name, fields = [], &block)
41
+ @association_configs << Association.new(name, fields, &block)
42
+ end
43
+
44
+ def associations(*names)
45
+ names.each do |name|
46
+ @association_configs << Association.new(name)
47
+ end
48
+ end
49
+ end
50
+ end
@@ -0,0 +1,64 @@
1
+ module ActiveAdminAssociations
2
+ module Autocompleter
3
+ extend ActiveSupport::Concern
4
+
5
+ module ClassMethods
6
+ def autocomplete(attribute, options = {})
7
+ class_attribute :autocomplete_attribute
8
+ class_attribute :autocomplete_options
9
+
10
+ self.autocomplete_attribute = attribute
11
+ self.autocomplete_options = options
12
+
13
+ extend AutocompleteMethods
14
+ end
15
+ end
16
+
17
+ module AutocompleteMethods
18
+ def autocomplete_results(query)
19
+ results = where("LOWER(#{table_name}.#{autocomplete_attribute}) LIKE ?", "#{query.downcase}%").
20
+ order("#{table_name}.#{autocomplete_attribute} ASC")
21
+ results.map do |record|
22
+ _autocomplete_format_result(record)
23
+ end
24
+ end
25
+
26
+ private
27
+
28
+ def _autocomplete_format_result(record)
29
+ if configured_autocomplete_result_formatter?
30
+ aa_associations_config.autocomplete_result_formatter.call(record,
31
+ autocomplete_attribute, autocomplete_options)
32
+ else
33
+ label = _format_autocomplete_label(record)
34
+ {"label" => label, # This plays nice with both jQuery UI autocomplete and jquery.tokeninput
35
+ "value" => record.send(autocomplete_attribute),
36
+ "id" => record.id}
37
+ end
38
+ end
39
+
40
+ def _format_autocomplete_label(record)
41
+ if autocomplete_options[:format_label].present?
42
+ if autocomplete_options[:format_label].is_a?(Symbol)
43
+ record.send(autocomplete_options[:format_label])
44
+ elsif autocomplete_options[:format_label].respond_to?(:call)
45
+ autocomplete_options[:format_label].call(record)
46
+ else
47
+ record.send(autocomplete_attribute)
48
+ end
49
+ else
50
+ record.send(autocomplete_attribute)
51
+ end
52
+ end
53
+
54
+ def configured_autocomplete_result_formatter?
55
+ aa_associations_config.autocomplete_result_formatter.present? &&
56
+ aa_associations_config.autocomplete_result_formatter.respond_to?(:call)
57
+ end
58
+
59
+ def aa_associations_config
60
+ Rails.application.config.aa_associations
61
+ end
62
+ end
63
+ end
64
+ end
@@ -0,0 +1,25 @@
1
+ module ActiveAdminAssociations
2
+ class Engine < Rails::Engine
3
+ config.aa_associations = ActiveSupport::OrderedOptions.new
4
+
5
+ initializer "active_admin_associations.load_extensions" do |app|
6
+ ActiveAdmin::BaseController.helper ActiveAdminAssociationsHelper
7
+ ActiveAdmin::ResourceDSL.send(:include, ActiveAdminAssociations::AssociationActions)
8
+ ActiveAdmin::ResourceDSL.send(:include, ActiveAdminAssociations::FormConfigDSL)
9
+
10
+ unless app.config.aa_associations.destroy_redirect == false
11
+ ActiveAdmin::BaseController.send(:include, ActiveAdminAssociations::RedirectDestroyActions)
12
+ end
13
+
14
+ unless app.config.aa_associations.autocomplete == false
15
+ ActiveSupport.on_load(:active_record) do
16
+ ActiveRecord::Base.send(:include, ActiveAdminAssociations::Autocompleter)
17
+ end
18
+
19
+ Formtastic::Helpers::InputHelper.send(:include, Formtastic::TokenInputDefaultForAssociation)
20
+ end
21
+
22
+ ActiveAdmin.application.register_stylesheet 'active_admin_associations'
23
+ end
24
+ end
25
+ end
@@ -0,0 +1,15 @@
1
+ module ActiveAdminAssociations
2
+ module FormConfigDSL
3
+ def active_association_form(&block)
4
+ config.active_association_form = block
5
+ end
6
+
7
+ def form_columns(*column_names)
8
+ config.form_columns = column_names.flatten
9
+ end
10
+
11
+ def form_associations(&block)
12
+ config.form_associations = AssociationConfig.new(&block)
13
+ end
14
+ end
15
+ end
@@ -0,0 +1,7 @@
1
+ module ActiveAdminAssociations
2
+ module RedirectDestroyActions
3
+ def destroy
4
+ destroy!{ request.headers["Referer"].presence || admin_dashboard_url }
5
+ end
6
+ end
7
+ end
@@ -0,0 +1,3 @@
1
+ module ActiveAdminAssociations
2
+ VERSION = "0.1.0"
3
+ end
@@ -0,0 +1,43 @@
1
+ module Formtastic
2
+ module Inputs
3
+ class TokenInput
4
+ include Base
5
+
6
+ def to_html
7
+ input_wrapping do
8
+ label_html <<
9
+ builder.hidden_field(input_name, input_html_options)
10
+ end
11
+ end
12
+
13
+ def input_html_options
14
+ super.merge({
15
+ :required => nil,
16
+ :autofocus => nil,
17
+ :class => 'token-input',
18
+ 'data-model-name' => reflection.klass.model_name.singular
19
+ }).tap do |html_options|
20
+ if record.present?
21
+ html_options["data-pre"] = prepopulated_value.to_json
22
+ end
23
+ end
24
+ end
25
+
26
+ def prepopulated_value
27
+ [{"value" => name_value, "id" => record.id}]
28
+ end
29
+
30
+ def name_method
31
+ builder.collection_label_methods.find { |m| record.respond_to?(m) }
32
+ end
33
+
34
+ def name_value
35
+ record.send(name_method)
36
+ end
37
+
38
+ def record
39
+ @object.send(method)
40
+ end
41
+ end
42
+ end
43
+ end
@@ -0,0 +1,19 @@
1
+ module Formtastic
2
+ module TokenInputDefaultForAssociation
3
+ extend ActiveSupport::Concern
4
+
5
+ included do
6
+ alias_method_chain :default_input_type, :token_default_for_association
7
+ end
8
+
9
+ def default_input_type_with_token_default_for_association(method, options = {})
10
+ if @object
11
+ reflection = reflection_for(method)
12
+ if reflection && reflection.klass.respond_to?(:autocomplete_attribute) && reflection.macro == :belongs_to
13
+ return :token
14
+ end
15
+ end
16
+ default_input_type_without_token_default_for_association(method, options)
17
+ end
18
+ end
19
+ end
@@ -0,0 +1,69 @@
1
+ require 'test_helper'
2
+
3
+ class ActiveAdminAssociationsTest < ActionDispatch::IntegrationTest
4
+ setup do
5
+ admin_login_as
6
+ @post = Factory(:post)
7
+ @tag = Factory(:tag)
8
+ @post.tags = [@tag]
9
+ end
10
+
11
+ context 'editing a post' do
12
+ setup do
13
+ visit "/admin/posts/#{@post.id}/edit"
14
+ end
15
+
16
+ should 'have correct inputs from form_columns config' do
17
+ assert page.has_selector?('form.post fieldset.inputs input#post_title'), 'no input for Post title'
18
+ assert page.has_selector?('form.post fieldset.inputs textarea#post_body'), 'no input for Post body'
19
+ assert page.has_selector?('form.post fieldset.inputs input#post_creator_id'), 'no input for Post creator'
20
+ end
21
+
22
+ should 'have correct inputs from active_association_form config' do
23
+ assert page.has_selector?('form.post fieldset#more-inputs input.my-date-picker#post_published_at'), 'no input for Post published_at'
24
+ assert page.has_selector?('form.post fieldset#more-inputs input#post_featured'), 'no input for Post featured'
25
+ end
26
+
27
+ should 'have correct token input for post creator' do
28
+ token_input = page.find('form.post fieldset.inputs input.token-input#post_creator_id')
29
+ assert_equal 'hidden', token_input["type"]
30
+ assert_equal 'user', token_input['data-model-name']
31
+ assert_equal '1', token_input['value']
32
+ assert_equal [{"value" => "Bill Tester", "id" => @post.creator_id}], MultiJson.decode(token_input['data-pre'])
33
+ end
34
+
35
+ should 'have a form to relate new tags' do
36
+ assert page.has_selector?('.relationship-table#relationship-table-tags form.relate-to-form')
37
+ assert page.has_selector?('#relationship-table-tags form.relate-to-form input[type="hidden"]#relationship_name')
38
+ assert_equal 'tags', page.find('#relationship-table-tags form.relate-to-form input#relationship_name')['value']
39
+ token_input = page.find('#relationship-table-tags form.relate-to-form input.token-input#related_id')
40
+ assert_equal 'tag', token_input['data-model-name']
41
+ end
42
+
43
+ should 'have a table of the related tags' do
44
+ assert page.has_xpath?('//div[@id="relationship-table-tags"]//table[@class="index_table"]/thead//th[text()="Name"]')
45
+ assert_equal @tag.name, page.find(:xpath, "//div[@id='relationship-table-tags']//table[@class='index_table']/tbody//tr[td[1][text()=#{@tag.id}]]//td[2]").text
46
+ edit_link = page.find(:xpath, "//div[@id='relationship-table-tags']//table[@class='index_table']/tbody//tr/td[3]/a[text()='Edit']")
47
+ assert_match %r{\A/admin/tags/\d+/edit}, edit_link['href']
48
+
49
+ assert page.has_xpath?("//div[@id='relationship-table-tags']//table[@class='index_table']/tbody//tr/td[3]/form//input[@type='submit'][@value='Unrelate']")
50
+ unrelate_form = page.find(:xpath, "//div[@id='relationship-table-tags']//table[@class='index_table']/tbody//tr/td[3]/form[@class='button_to']")
51
+ unrelate_url = unrelate_form['action']
52
+ assert_match %r{\A/admin/posts/#{@post.id}/unrelate}, unrelate_url
53
+ query_params = unrelate_url.split('?')[1].split('&')
54
+ assert_same_elements ["related_id=#{@tag.id}", 'relationship_name=tags'], query_params
55
+ end
56
+ end
57
+
58
+ context 'editing a tag' do
59
+ setup do
60
+ visit "/admin/tags/#{@tag.id}/edit"
61
+ end
62
+
63
+ should 'have a table of the related posts' do
64
+ assert page.has_xpath?('//div[@id="relationship-table-posts"]//table[@class="index_table"]/thead//th[text()="Title"]')
65
+ assert_equal @post.title, page.find(:xpath, "//div[@id='relationship-table-posts']//table[@class='index_table']/tbody//tr[td[1][text()=#{@post.id}]]//td[2]").text
66
+ assert_equal @post.creator.name, page.find(:xpath, "//div[@id='relationship-table-posts']//table[@class='index_table']/tbody//tr[td[1][text()=#{@post.id}]]//td[3]").text
67
+ end
68
+ end
69
+ end