`
yangtao309
  • 浏览: 66789 次
  • 性别: Icon_minigender_1
  • 来自: 上海
社区版块
存档分类
最新评论

应用Rails+Ext开发企业级权限平台

阅读更多
  应用Rails+Ext开发企业级权限平台
一般说来做企业级权限平台都是java的天下,我也不得不认同java这方面的能力。本文介绍的Ext + Rails 开发的企业级权限平台也是在原先用java实现的一套改写而成,保证了基本功能的一致性,UI界面的一致性。这样就充分说明了富客户端框架(或者说RIA)的应用广泛性,能够和多种服务器端语言组合开发应用系统平台。
  该平台是基于角色的权限管理后台.集中了一般企业应用所必需包含的必须模块,包括用户维护、模块维护、角色管理、权限管理、数据字典以及统一的页面风格等.由于该开发平台的存在,大大提高了开发人员的开发效率,特别是单表操作,仅仅只需要在范例模块的基础之上进行少量修改即可达到目的.平台架构设计本着追求层次间的低耦合,层次明显分开,TDD开发模式原则进行.
下面有实现的图样~~ (貌似有蛮多 呵呵)

整个开发中没什么新颖的东西 就如同java一般从数据库中去数字组装成符合要求的json字符串输出渲染页面让Ext来显示数据和如何组织项目的结构。


数据库表结构db/schema.rb
# This file is auto-generated from the current state of the database. Instead of editing this file, 
# please use the migrations feature of ActiveRecord to incrementally modify your database, and
# then regenerate this schema definition.
#
# Note that this schema.rb definition is the authoritative source for your database schema. If you need
# to create the application database on another system, you should be using db:schema:load, not running
# all the migrations from scratch. The latter is a flawed and unsustainable approach (the more migrations
# you'll amass, the slower it'll run and the greater likelihood for issues).
#
# It's strongly recommended to check this file into your version control system.

ActiveRecord::Schema.define(:version => 15) do

  create_table "catalogs", :force => true do |t|
    t.string  "catalogname", :limit => 32
    t.text    "remark"
    t.integer "sortno"
    t.integer "parent_id"
  end

  create_table "dicts", :force => true do |t|
    t.string  "key",  :limit => 32, :default => "", :null => false
    t.string  "value", :limit => 32
    t.text    "remark"
    t.integer "sortno"
    t.integer "catalog_id"
  end

  create_table "groups", :force => true do |t|
    t.string  "createby",  :limit => 32
    t.string  "name",  :limit => 50, :default => "", :null => false
    t.text    "remark"
    t.integer "sortno"
    t.integer "parent_id"
  end

  create_table "logs", :force => true do |t|
    t.text     "log",   :default => "", :null => false
    t.string   "type",  :limit => 32, :default => "", :null => false
    t.string   "personloginname", :limit => 32
    t.string   "personname",      :limit => 32
    t.datetime "dt"
  end

  create_table "mods", :force => true do |t|
    t.text   "link"
    t.string "type", :limit => 32
    t.text   "icon"
  end

  create_table "operations", :force => true do |t|
    t.string  "sn",        :limit => 32
    t.string  "icon",      :limit => 32
    t.string  "tip",       :limit => 32
    t.boolean "show_text"
    t.boolean "admin_op"
  end

  create_table "people", :force => true do |t|
    t.string  "login_name",     :limit => 32
    t.string  "status",         :limit => 32
    t.string  "hased_password"
    t.string  "salt"
    t.string  "name",           :limit => 32
    t.string  "sex",            :limit => 10
    t.date    "birthday"
    t.integer "sortno"
    t.text    "config"
    t.string  "creator",        :limit => 32
    t.date    "createdt"
  end

  create_table "person2groups", :force => true do |t|
    t.integer "person_id"
    t.integer "group_id"
    t.boolean "isadmin"
    t.string  "indicator", :limit => 32
  end

  create_table "person2resources", :force => true do |t|
    t.integer "resource_id"
    t.integer "person_id"
    t.string  "indicator",   :limit => 32
  end

  create_table "person2roles", :force => true do |t|
    t.string  "indicator", :limit => 32
    t.integer "person_id"
    t.integer "role_id"
  end

  create_table "resources", :force => true do |t|
    t.string  "name",      :limit => 32
    t.integer "sortno"
    t.boolean "visiabled"
    t.text    "remark"
    t.integer "parent_id"
    t.integer "ph_id"
    t.string  "ph_type"
  end

  create_table "role2resources", :force => true do |t|
    t.integer "role_id"
    t.integer "resource_id"
    t.boolean "communicable"
    t.boolean "inherit"
  end

  create_table "roles", :force => true do |t|
    t.string  "creator",     :limit => 32
    t.boolean "inheritable"
    t.string  "name",        :limit => 32
    t.text    "remark"
    t.integer "sortno"
    t.integer "parent_id"
  end

  create_table "sessions", :force => true do |t|
    t.string   "session_id", :default => "", :null => false
    t.text     "data"
    t.datetime "created_at"
    t.datetime "updated_at"
  end

  add_index "sessions", ["session_id"], :name => "index_sessions_on_session_id"
  add_index "sessions", ["updated_at"], :name => "index_sessions_on_updated_at"

end


字典类型对象和字典对象一对多关系
model/catalog.rb
class Catalog < ActiveRecord::Base
  acts_as_tree :order => "catalogname"
  has_many :dicts, :dependent => :destroy

  def expanded
    true
  end
  
  alias_attribute :text, :catalogname 
  alias_attribute  :catalogName, :catalogname
  alias_attribute :sortNo, :sortno 
  
  #以下省略.....
end


字典对象
model/dict
class Dict < ActiveRecord::Base
  belongs_to :catalog

  alias_attribute :sortNo, :sortno 
end


组织对象,组织和人员是多对多关系.
model/group.rb
class Group < ActiveRecord::Base
  acts_as_tree :order => "name"
  has_many :person2groups, :dependent => :destroy
  has_many :people, :through => :person2groups
  
  alias_attribute :sortNo, :sortno
  alias_attribute :groupName, :name 
  
  #以下省略.....
end


日志对象
model/log.rb
class Log < ActiveRecord::Base
end


模块对象 模块对象和操作对象都属于资源对象
model/mod.rb
class Mod < ActiveRecord::Base
  has_one :resource ,:as => :ph

end


操作对象 模块对象和操作对象都属于资源对象
model/operation.rb
class Operation < ActiveRecord::Base
   has_one :resource ,:as => :ph
end


人员组织中间表model
model/person2group.rb
class Person2group < ActiveRecord::Base
  belongs_to :person
  belongs_to :group
  
  
  alias_attribute :admin, :isadmin

  #以下方法省略......
end


人员资源中间model
model/person2resource.rb
class Person2resource < ActiveRecord::Base
  belongs_to :person
  belongs_to :resource

  #以下方法省略......
end


人员角色中间model
model/person2role.rb
class Person2role < ActiveRecord::Base
  belongs_to :person
  belongs_to :role

  #以下方法省略......
end


人员对象 和组织 角色 资源等关联
model/person.rb
require 'digest/sha1'

class Person < ActiveRecord::Base
  has_many :person2groups, :dependent => :destroy
  has_many :groups, :through => :person2groups
  
  has_many :person2roles, :dependent => :destroy
  has_many :roles, :through => :person2roles
  
  has_many :person2resources, :dependent => :destroy
  has_many :resources, :through => :person2resources

  #以下方法省略......
end


资源对象 包含两种资源 操作和模块
model/resource.rb
class Resource < ActiveRecord::Base
  #2008-6-12资源属性相关-多关联
  belongs_to :ph, :polymorphic => true
  
  acts_as_tree :order => "name"
  
  has_many :person2resources, :dependent => :destroy
  has_many :people, :through => :person2resources
  
  has_many :role2resources, :dependent => :destroy
  has_many :roles, :through => :role2resources
  
  alias_attribute :sortNo, :sortno
  alias_attribute :text,  :name
  alias_attribute :resName, :name
  alias_attribute :childRes, :children
  
  RES_TYPE_OPERATION = "Operation"
  RES_TYPE_MODULE = "Mod"

  #以下方法省略......
end


角色和资源的关联的中间model
model/role2resource.rb
class Role2resource < ActiveRecord::Base
  belongs_to :role
  belongs_to :resource 
  
  alias_attribute :resourceId, :resource_id 
  
  #以下方法省略......
end


角色对象 和人员 资源关联
model/role.rb
class Role < ActiveRecord::Base
  acts_as_tree :order => "name"
  
  has_many :person2roles, :dependent => :destroy
  has_many :people ,:through => :person2roles
  
  has_many :role2resources, :dependent => :destroy
  has_many :resources, :through => :role2resources
  
  #下面是为了便于JSON化数据
  alias_attribute :sortNo, :sortno
  alias_attribute :text, :name
  alias_attribute :rolename, :name
  
  #以下方法省略......
end


下面是自己改写过的生成json的插件jsonifier
用于把对象和对象集合转换成符合EXT接受的单个对象的json格式和多个树形结构的json格式
module Jsonifier #:nodoc:
  module JsonEncoding
    def to_json(options = {})
      hashifier = JsonHashifier.new(self, options)
      hashifier.to_hash.to_json
    end

    class JsonHashifier #:nodoc:
      attr_reader :options
      
      def initialize(record, options = {})
        @record, @options = record, options.dup
        filter_attributes
      end

      # Outputs AR record instance method as a hash that can be easily
      # encoded as JSON.
      def to_hash
        hash = {}

        hash.merge!(simple_attributes) 
        hash.merge!(method_attributes)
        hash.merge!(astract_attributes)
        hash.merge!(checked_attributes)
        hash.merge!(association_attributes)
        hash.merge!(self_children_attributes)
        hash.merge!(parent_object_attributes)
        hash.merge!(contain_association_attributes)
        
        hash
      end

      # Returns 1st level attributes as a hash.
      def simple_attributes
        attribute_names = @record.attribute_names

        if options[:only]
          options.delete(:except)
          attribute_names = attribute_names & Array(options[:only]).collect(&:to_s)
        else
          options[:except] = Array(options[:except]) | Array(@record.class.inheritance_column)
          attribute_names = attribute_names - options[:except].collect(&:to_s)
        end

        attribute_names.reject! { |n| binary_attribute?(n) } # Don't JSON-ify binary fields!

        @record.attributes(:only => attribute_names)
      end

      # Returns 1st level methods as a hash.
      def method_attributes
        Array(options[:methods]).inject({}) do |method_attributes, name|
          method_attributes.merge!({ name.to_s => @record.send(name.to_s) }) if @record.respond_to?(name.to_s)
          method_attributes
        end
      end
      
      # Returns 1st level methods as a hash.
      #ext:=> options[:astract].is_a?(Hash)
      #:astract => {:id => :resource_id}
      def astract_attributes
        hash = {}
        if options[:astract] && options[:astract].is_a?(Hash)
          options[:astract].keys.each do |key|
              name = options[:astract][key]
              hash.merge!({ key.to_s => @record.send(name.to_s) }) if @record.respond_to?(name.to_s)
              hash
          end
        end
        hash
      end
      
      # Returns 1st level methods as a hash.
      #ext:=> options[:checked].is_a?(Hash)
      #:checked => obj 
      #but the obj.is_a?(Hash)
      #using for role_is_checked and group_is_checked
      #2008-06-30
      def checked_attributes
        hash = {}
        if options[:checked] && options[:checked].is_a?(Hash)
          if options[:checked].values.include?(@record.send(:id))
            hash.merge!(:checked => true) 
          else
            hash.merge!(:checked => false) 
          end
          hash
        end
        hash
      end
      
      #2008-06-13 by ytok
      # Returns 1st level methods as a hash.
      def filter_attributes
        filter_object = options[:filter]
        if filter_object && filter_object.is_a?(Hash)
           filter_object.keys.each do |key|
            value = filter_object[key]
            if value.is_a?(Hash)
              value.keys.each do |class_name|
                add_method = value[class_name]
                if @record.instance_of?(key.class)
                  if @record.ph.instance_of?(class_name.class)
                    options[:methods].concat(add_method)
                  end
                end
              end
            end
          end
        end
      end
      
      
      # Returns 1st level associations as a hash. Recursively "hashifies"
      # associations so that nth level associations are converted to JSON as well.
      def association_attributes
        hash = {}

        if include_associations = options.delete(:include)
          base_only_or_except = { :except => options[:except],
                                  :only => options[:only] }

          include_has_options = include_associations.is_a?(Hash)

          for association in include_has_options ? include_associations.keys : Array(include_associations)
            association_options = include_has_options ? include_associations[association] : base_only_or_except

            opts = options.merge(association_options)
            
            case @record.class.reflect_on_association(association).macro
            when :has_many, :has_and_belongs_to_many
              records = @record.send(association).to_a
              unless records.empty?
                hash[association] = records.collect { |r| JsonHashifier.new(r, opts).to_hash }
              end
            when :has_one, :belongs_to
              if record = @record.send(association)
                hash[association] = JsonHashifier.new(record, opts).to_hash
              end
            end
          end

          options[:include] = include_associations
        end

        hash
      end
      
      #2008-06-07 by ytok
      # Returns more 1st level associations as a hash. Recursively "hashifies"
      # associations so that nth level associations are converted to JSON as well.
      def self_children_attributes
        hash = {}
        
        if include_associations = options[:self]
          
          base_only_or_except = { :except => options[:except],
                                  :only => options[:only] }

          include_has_options = include_associations.is_a?(Hash)

          for association in include_has_options ? include_associations.keys : Array(include_associations)
            association_options = include_has_options ? include_associations[association] : base_only_or_except

            opts = options.merge(association_options)

            case @record.class.reflect_on_association(association).macro
            when :has_many, :has_and_belongs_to_many
              records = @record.send(association).to_a
              unless records.empty?
                hash[association] = records.collect { |r| JsonHashifier.new(r, opts).to_hash }
              else
                hash[association] = Array.new
              end
            when :has_one, :belongs_to
              if record = @record.send(association)
                hash[association] = JsonHashifier.new(record, opts).to_hash
              end
            end
          end

          options[:self] = include_associations
        end
        
        hash
      end

      #2008-06-09 by ytok
      # Returns more 1st level associations as a hash. Recursively "hashifies"
      # associations so that nth level associations are converted to JSON as well.
      def parent_object_attributes
        hash = {}
        
        if include_associations = options[:parent_object]
          
          base_only_or_except = { :except => options[:except],
                                  :only => options[:only] }

          include_has_options = include_associations.is_a?(Hash) 
         
          association = include_has_options ? include_associations.keys : Array.new 
          association.each do |associ|
            association_options = include_has_options ? base_only_or_except : include_associations[associ]

            opts = options.merge(association_options)

            case @record.class.reflect_on_association(associ).macro
            when :has_many, :has_and_belongs_to_many
              records = @record.send(associ).to_a
              unless records.empty?
                hash[include_associations[associ]] = records.collect { |r| JsonHashifier.new(r, opts).to_hash }
              else
                hash[include_associations[associ]] = Array.new
              end
            when :has_one, :belongs_to
              if record = @record.send(associ)
                hash[include_associations[associ]] = JsonHashifier.new(record, opts).to_hash
              else
                hash[include_associations[associ]] = Array.new
              end
            end
          end

          options[:parent_object] = include_associations
        end
        
        hash
      end
      
      #2008-06-10 by ytok
      # Returns more 1st level associations as a hash. Recursively "hashifies"
      # associations so that nth level associations are converted to JSON as well.
      def contain_association_attributes
        hash = {}
        
        if include_associations = options[:contains]
          
          if include_associations.is_a?(Hash)
            include_associations.keys.each do |contain_key|
              contain_value = include_associations[contain_key]
              attr_name = contain_key.to_sym
              
              hash = component_association_transact(attr_name, contain_value)
            end
          else
            attr_name = include_associations.to_sym
            hash = component_association_transact(attr_name, nil)
          end
          
        end
        hash
      end
      
      
      protected

        def binary_attribute?(name)
          !@record.class.serialized_attributes.has_key?(name) && @record.class.columns_hash[name].type == :binary
        end
        
        #2008-06-13 by ytok
        #核心处理关联关系部分
        def component_association_transact(attr_name, attr_value = nil) 
          hash = {}
          
          include_associations = options[:contains]
          base_only_or_except = { :except => options[:except],
                                  :only => options[:only] } 
          association_options = base_only_or_except
          
          #处理待过滤属性
          dispose_filter_attribute
          opts = options.merge(association_options)
          
          unless attr_value
            hash = dispose_association_attribute(attr_name, opts, attr_name, nil)      
            
          else
            if attr_value.is_a?(Hash) 
              attr_value.keys.each do |associations_method|
                ass_value = attr_value[associations_method]
                
                #nothing to do
              end
            elsif attr_value.is_a?(Symbol)
               hash = dispose_association_attribute(attr_value, opts, attr_name, nil)    
               
            else
               hash = dispose_association_attribute(attr_name, opts, attr_name, attr_value)  
               
            end
          end
          options[:contains] = include_associations
          
          hash
        end
        
        #2008-06-13 by ytok
        #处理关联关系核心
        def dispose_association_attribute(association, opts, association_value, astract_value = nil)
          hash = {} 
          
            case @record.class.reflect_on_association(association).macro
            when :has_many, :has_and_belongs_to_many
              records = @record.send(association).to_a
              unless records.empty?
                hash[association_value] = records.collect { |r| 
                  unless astract_value && r.ph.instance_of?(astract_value.class)
                     JsonHashifier.new(r, opts).to_hash  
                  end
                }
                
                #处理掉空元素
                temp = Array.new
                hash[association_value].each do |item|
                  unless !item
                    temp << item
                  end
                end
                
                hash[association_value] = temp
              else
                hash[association_value] = Array.new
              end
            when :has_one, :belongs_to
              if record = @record.send(association)
                unless astract_value && record.ph.instance_of?(astract_value.class)
                  hash[association_value] = JsonHashifier.new(record, opts).to_hash
                else
                  hash[association_value] = Array.new
                end
              else
                hash[association_value] = Array.new
              end
             end
          
          hash
        end
        
        
        #处理待过滤属性
        def dispose_filter_attribute
          filter_attr_name = Array.new
          filter_object = options[:filter]
          if filter_object && filter_object.is_a?(Hash)
            filter_object.keys.each do |key|
              filter_object[key].values.each do |value|
                value.each do |item|
                  filter_attr_name << item
                end
              end
            end
          end
          
          filter_attr_name.each do |item|
            options[:methods].delete(item)
          end
        end
        
    end
  end
end

ActiveRecord::Base.send(:include, Jsonifier::JsonEncoding)


ps======================================== 下面后续的说明其整个项目组织结构和开发中遇到的问题
  • 大小: 80.7 KB
  • 大小: 82.5 KB
  • 大小: 91.5 KB
  • 大小: 105.7 KB
  • 大小: 94.6 KB
  • 大小: 91.9 KB
  • 大小: 89.7 KB
  • 大小: 99.4 KB
  • 大小: 107.1 KB
  • 大小: 77 KB
  • 大小: 80.4 KB
  • 大小: 81.4 KB
分享到:
评论

相关推荐

    Web开发敏捷之道--应用Rails进行敏捷Web开发 之 Depot代码。

    标题中的“Web开发敏捷之道--应用Rails进行敏捷Web开发 之 Depot代码”表明这是一个关于使用Ruby on Rails框架进行敏捷Web开发的示例项目,名为Depot。Ruby on Rails(简称Rails)是一个开源的Web应用程序框架,它...

    Ruby+Rails+社交+教程

    这个“Ruby+Rails+社交+教程”显然旨在引导开发者如何利用Rails的灵活性和强大功能构建一个完整的社交平台。以下是教程可能涵盖的一些核心知识点: 1. **Ruby基础知识**:首先,你需要对Ruby编程语言有基本的理解,...

    Web开发敏捷之道-应用Rails进行敏捷Web开发 pdf

    《Web开发敏捷之道——应用Rails进行敏捷Web开发》是一本深度探讨如何利用Ruby on Rails框架进行高效、敏捷的Web应用程序开发的专业书籍。该书涵盖了从初学者到高级开发者所需的各种知识,旨在帮助读者掌握敏捷开发...

    rails+grape+swagger+devise+capistrano 简单融合示例

    总结来说,这个"rails+grape+swagger+devise+capistrano"的简单融合示例展示了一个完整的、功能齐全的API项目架构。Rails作为基础框架,Grape负责API的构建,Swagger用于API的文档化,Devise处理用户认证,而...

    Ruby+Rails+社交+教程3

    本教程“Ruby+Rails+社交+教程3”旨在帮助开发者掌握如何利用Ruby的强大功能和Rails的优雅设计来构建一个具有用户交互性的社交平台。 首先,让我们深入了解一下Ruby。Ruby是一种面向对象的编程语言,以其简洁、易读...

    Ruby+Rails+社交+进阶教程5

    在本“Ruby+Rails+社交+进阶教程5”中,我们将深入探讨如何利用Ruby on Rails框架构建一个功能丰富的社交网络平台。Ruby on Rails(简称Rails)是一个基于Ruby语言的开源Web应用程序框架,它遵循MVC(模型-视图-控制...

    Web开发敏捷之道-应用Rails进行敏捷Web开发(第3版).pdf

    整体而言,全书既有直观的实例,又有深入的分析,同时还涵盖了web应用开发中各方面的相关知识,堪称一部内容全面而又深入浅出的佳作。 编辑推荐 《Web开发敏捷之道:应用Rails进行敏捷Web开发(第3版)》:Ruby on ...

    使用Aptana+Rails开发Rails Web应用(中文)

    在开发Web应用时,Ruby on Rails(简称Rails)框架因其高效、简洁的代码风格和强大的社区支持而备受青睐。Aptana是一款强大的集成开发环境(IDE),尤其适用于Rails项目的开发,它提供了丰富的特性来提升开发效率。...

    Web开发敏捷之道应用Rails进行敏捷Web开发(第3版)

    资源名称:Web开发敏捷之道 应用Rails进行敏捷Web开发(第3版)内容简介:全书主要分为两大部分。在“构建应用程序”部分中,读者将看到一个完整的“在线购书网站”示例。在随后的“Rails框架”部分中,作者深入介绍...

    应用Rails进行敏捷Web开发中文第三版

    《应用Rails进行敏捷Web开发》中文第三版是针对Ruby on Rails框架的一本详尽指南,主要聚焦于Rails 2.2.2版本。Ruby on Rails(简称Rails)是一款基于Ruby编程语言的开源Web应用程序框架,它遵循“Don't Repeat ...

    Agile+Web+Development+with+Rails+(4th+Ed....pdf

    Agile+Web+Development+with+Rails+(4th+Ed....pdf

    用Ext Scaffold插件打造Rails的Ext风格

    标题 "用Ext Scaffold插件打造Rails...通过学习这篇文档,开发者能够掌握如何在Rails应用中集成Ext JS,利用其强大的UI组件和Ext Scaffold插件快速构建功能丰富的前端界面,从而提升开发效率并提供更高质量的用户体验。

    Ruby+on+Rails快速Web应用开发实战.pdf

    总结以上内容,Ruby on Rails快速Web应用开发实战的文档详细介绍了Ruby编程语言和Rails框架的安装、配置以及使用。它涵盖了从Rails的历史和版本发展到如何在不同操作系统上安装Rails,再到如何使用Rails进行Web应用...

    Rails中应用Ext.tree:以中国的省市地区三级联动选择为例

    这篇博客文章“Rails中应用Ext.tree:以中国的省市地区三级联动选择为例”提供了一个实用的示例,教我们如何利用Ext.js库中的Tree组件来实现这种功能。 首先,让我们了解Rails和Ext.js的基本概念。Rails是基于Ruby...

    在RHEL上安裝設置ROR(nginx+passenger+ruby+rails+oracle+netzke)

    7. **Netzke**: Netzke是一个用于创建Rails应用的JavaScript组件库,提供前后端分离的开发方式。安装`netzke-basepack` gem,然后在Rails应用中集成Netzke的组件和配置。 8. **环境变量与权限**: 确保所有必要的...

    rails_docker_template:用于Rails应用程序或Rails + Webpacker应用程序开发的Docker模板

    docker rails模板用于Rails应用程序或Rails + Webpacker应用程序开发的Docker模板。用于开发该模板使用在Docker的上执行bundle install 。 更改Gemfile时无需重新构建Docker映像,因为捆绑的gems已缓存在Docker ...

    Web开发敏捷之道--应用Rails进行敏捷Web开发(第2版中文版).part15.rar

    Web开发敏捷之道--应用Rails进行敏捷Web开发(第2版中文版).part15.rar

    应用Rails进行REST开发

    在Rails应用中,资源通常由控制器(controller)和模型(model)组成,对应着数据库中的记录。 **1.1 什么是REST?** 在REST架构中,URL标识了一个资源,而不是一个特定的控制器或动作。例如,项目资源可以通过`/...

    使用Rails开发Facebook平台应用

    ### 使用Rails开发Facebook平台应用 #### 一、引言与背景 随着社交媒体的迅速发展,Facebook作为全球最大的社交网络之一,为企业和个人提供了无限的机会来创建和推广应用程序。Rails(Ruby on Rails)作为一种流行...

Global site tag (gtag.js) - Google Analytics