`
java-admin
  • 浏览: 1382043 次
  • 性别: Icon_minigender_1
  • 来自: 陕西.西安
社区版块
存档分类
最新评论

使用Ruby DSL实现敏捷素材管理

 
阅读更多

使用Ruby DSL实现敏捷素材管理

http://www.infoq.com/cn/articles/Agile-Asset-Management

 

 

领域特定语言(Domain Specific Language,简称DSL)是一个面向语言的工具,用于解决某个特定领域的编程任务。DSL的一般语言特征和它所被用到的问题领域,关系是非常密切的,并且在一个非常高的抽象层面上起作用。Martin Fowler在他介绍DSL的文章中,将DSL划分为外部DSL内部DSL两类(参见原文链接)。外部DSL是一门需要编译或者解释运行的编程语言,而内部DSL则构建于一门通用编程语言(general-purpose programming language)之内。实际上,内部DSL对于其宿主通用编程语言来说,就是它的一套层次非常高的API。本文讲述了了在PLANET ARGON公司的一个开发项目中,用Ruby实现的一个内部DSL如何给项目带来巨大贡献。

问题的提出

我在PLANET ARGON公司近期的一个开发项目的目标是,使用Ruby on Rails构建一个一次性的内容管理系统,这个系统要支持18种语言,并且管理大约1000个的图像文件。这些文件中有许多都是专业摄影图片,每个文件大小都超过1MB。而另外的图片文件则是一些细碎的线条艺术,图片里面显示的就是许多不同的国旗。这些图片所具备的相同特点,就是它们都还不能直接投入产品使用。

我们的应用程序中包含一个Image模型,该模型使用我们内部编写的一个文件上传插件来持久化图片数据。我们已经使用了after_save钩子来管理图形转换操作,而这些钩子在我们的模型内部使用了RMagic图形操作API。这样看来应该就够了,但实际上我们的需求一直在不断被调整。有些图片需要进行四次变换才能投入使用;而其它图片只需要两次;其中一些图片必须被上传到一个第三方的贷款提供商那里;而又有些时候,我们把图片的尺寸弄错了。我们曾经一度编写了一个批处理脚本来上传这些原始图像,接着after_save钩子就可以重新处理这些图像了。便捷情况很快就凸现了出来,于是我们回调代码中就七荤八素地塞满条件判断语句。你可能已经猜到了,这个麻烦很快就成了往事。我们需要一个可以帮助我们以一种可持续发展的方式达到客户需求的工具。

解决方案

有一天,我们的客户又向我们的Basecamp发过来一个参考图片,以及他们如何在PhotoShop中创建这个图片的一系列指示。我抓狂了。我开始考虑使用一个更加优雅的方案来解决这个问题,我想起了Jason Watkins曾经跟我说起过,当他在电子游戏这一行工作的时候,他用过一类称为“素材编译器(asset compiler)”的工具。素材编译器其实是一个构建系统,将一系列媒体文件转换成用于电子游戏最终构建版用到的媒体文件。最近我用Rake自动化了不少重复性的任务(Rake是一个用纯Ruby来实现的构建系统),所以用Rake搞出一套素材编译器,对我来说很有吸引力。如果创建产品图像的职责可以委派给一个外部的工具,那么不管我们客户的需求在什么时候发生改变,我们的应用程序都无须进行变更。更有意思的是,这个工具可以使得我们在需求发生变化的时候,使用一条命令就重新构建出所有的产品级图像。

拿着Jim Weirich(Rake的作者)的RDoc任务库(task library),我开始上手干活了。RDoc任务库以模板的形式随Rake一起被分发。Jim为Rake创建的用法模式之一就是一个实例化的类,里面带着描述了需用于自动化构建的Rake任务集合的合适选项。比如说,这里就给出了一个范例,展示如何在Rakefile使用RDoc任务库为Rails应用及其使用的插件,还有整个Rails框架构建文档。

  Rake::RDocTask.new('my_docs') do |rdoc|
    # configuration
    rdoc.rdoc_dir = 'doc/my_docs'
    rdoc.main = "doc/README_FOR_APP"
    rdoc.title = 'Comprehensive Documentation'

    # app documentation
    rdoc.rdoc_files.include('doc/README_FOR_APP')
    rdoc.rdoc_files.include('app/**/*.rb')
    rdoc.rdoc_files.include('lib/**/*.rb')

    # plugins documentation
    rdoc.rdoc_files.include('vendor/plugins/*/lib/**/*.rb')

    # framework documentation
    # ...snip...
  end

按照这个范例在你的Rakefile中实例化一个Rake::RDocTask对象将会建立一系列Rake任务,用于创建、删除及重新构建你的文档。被Rake::RDocTask.new使用rdoc这个block参数,实际上就是正被实例化的Rake::RDocTask对象,而正被初始化的选项以及rdoc的属性则是用attr_accessors定义在Rake::RDocTask中的。

我开始将构建产品级图像的代码从我们的Image模型中抽取出来,放进一个Rake任务库中。在建立了一个操作起来类似于RDoc任务库的可工作任务库之后,我捣鼓出来的代码就像下面这样:

  ImageTask.new :bronze_thumbnail do |t|
    t.src_files   =  image_src 'images/*.jpg'
    t.build_path  =  build     'greyscale_thumbnail'
    t.remote_dirs << REMOTE_DIR[:greyscale_thumbnail]
    t.transformation do |img|
      # apply bronze image effect
      img = img.quantize 256, Magick::GRAYColorspace
      img = img.colorize 0.25, 0.25, 0.25, '#706000'
      # crop to thumbnail size
      ...snip...
    end
  end

上面的代码定义了以下Rake任务,用rake -T可以得到以下描述:

  rake assets:bronze_thumbnail:build                    # Build the bronze_thumbnail files
  rake assets:bronze_thumbnail:clobber                  # Remove bronze_thumbnail files
  rake assets:bronze_thumbnail:rebuild                  # Force a rebuild of the bronze_thumbnail files
  rake assets:build                                     # Build all assets
  rake assets:clobber                                   # Clobber all assets
  rake assets:rebuild                                   # Rebuild all assets

我意识到,如果描述这些图像变换的代码与客户给我们的PhotoShop指令非常相似的话,这个工具将变得极具灵活性——代码就成了为图形变化定义Rake任务的内部DSL了。

我为我的任务库创建了一些单元测试,以保证任务可以被正确地创建出来,并且以测试驱动重构迈开第一步。由于我创建的是一个内部DSL,它在我这个例子里是一个Ruby API,这套DSL的创建完全可以遵循测试驱动的实践。我开始将我单元测试中初始化任务库各个类的范例代码替换成我尚未实现的DSL的范例代码,并且修改了数次,直到我满意为止。

  define_image_transformation 'thumbnailize' do
    crop_to '62x62', :north
  end

  define_image_transformation 'bronze' do
    greyscale
    lighten
    #    r     g     b     tint
    tint 0.25, 0.25, 0.25, '#706000'
  end

  image_task 'bronze_thumbnail' do
    from images 'images/*.jpg'
    to   build 'greyscale_thumbnail'
    remote_dirs << REMOTE_DIR[:greyscale_thumbnail]
    transformation do
      bronze
      thumbnailize
    end
  end

我开始运行我的单元测试,然后看见一连串错误消息跳将出来。这是件好事——现在我知道了我要的是什么,还有我需要哪些步骤才能实现我的目标。接着,我重命名了几个方法(比如说,把src改成了from,然后创建了一些辅助方法(helper methods)来辅助初始化任务库的类。此外,我还将一个低层的RMagick代码包装进一个类库之中,类库使用了对设计师友好的方法命名。随后我创建了一个define_image_transformation,该方法通过聚合那些RMagick的包装方法,定义了针对应用程序的高级图形变换。通过把类似于Photoshop的图形工具可以做到的事情和我们的客户需要我们做到的事情区别对待,我就可以保证我的工具能被抽取出来,并且重用在今后的项目之中——高层的图形变换代码是存放在我应用程序中lib/tasks目录的,而低层的包装器代码则存放在vendor/asset_compiler目录之下,和任务库分开存放。

成果

目前,我们的lib/tasks/assets.rake文件包含对image_task的12个调用,包括上面展示的实例。尽管对于外行程序员(lay programmers)来说,这门语言不一定合适(Fowler在文章中说,外行程序员就是非专业的程序员,但他们可以使用简单的编程工具来自动化业务任务),但是它和我们客户的语言是同步的,而且优雅地将他们的想法和目标建模出来。

因为Ruby DSL就是Ruby的代码,所以我强烈建议要遵循我在这篇文章中描述的测试驱动过程:编写你希望使用的Ruby内部DSL的一个范例代码,然后编写为这个范例的期望结果编写测试用例,接下来还是编写代码,直到所有的测试都能通过为止。此外,在一开始时就要考虑你DSL的范围。由于我强调了图像转换功能和我们应用程序的需求之间的分离,在我们的DSL基础上构建出一个强大、可重用而又可扩展的框架就是一件自然而然的事情了。我认为这是一个正确的决定,因为我知道很快我又将把这套类库用于其它项目。如果你的需求更加特别,那么你就不需要多层的抽象了。如果你打算重用你的内部DSL,那么请确定没有任何针对应用程序的业务逻辑掺杂其中,而是提供一个机制来俘获业务逻辑。

最后,告诉你应当如何使用哪种语言创建出一门内部DSL的圣经,是不存在的。在这个主题上我所读到的所有文章中,绝大多数都认为你必须边做边学。留意观察客户和领域专家如何沟通他们的需求,并且专注于使用可执行的代码来捕捉这些需求。即便你无法将你的领域专家转变成一个外行程序员,他们应当可以在一次代码评审或者一个结对编程过程中明白清楚地看到他们的需求,而你的目标则应当是,在你得到他们需求的同时就马上捕捉下来。

查看英文原文:Agile Asset Management with Ruby DSLs

<script type="text/javascript"></script><script src="/scripts/forum.js?20110705" type="text/javascript"></script><script src="/dwr/interface/ForumNotifications.js" type="text/javascript"></script><script src="/scripts/jquery.timeago.js" type="text/javascript"></script><script src="/scripts/date.js" type="text/javascript"></script>

分享到:
评论

相关推荐

    squib, 用于Prototype卡游戏的ruby DSL.zip

    squib, 用于Prototype卡游戏的ruby DSL 点火 点火器是一个用于Prototype卡和电路板游戏的ruby DSL插件。 写一点 ruby,定义你的数据库,然后将你的游戏编译成一系列的print-and-play甚至 print-on-demand 。

    Ruby-RubyJMeter一个基于Ruby的DSL用于构建JMeter测试计划

    Ruby-JMeter是一个强大的工具,它将Ruby编程语言与Apache JMeter测试框架相结合,为性能测试和负载测试提供了灵活且易于使用的领域特定语言(DSL)。这个工具使得测试人员和开发者能够用Ruby编写JMeter测试计划,...

    ruby on rails最佳敏捷开发

    Rails的Gem包管理系统允许开发者方便地管理和使用第三方库,进一步加速开发进程。 相比于Java框架普遍存在的XML配置,Rails更倾向于使用YAML或纯Ruby代码进行配置,这使得配置文件更加简洁直观,符合Ruby语言的哲学...

    使用ruby解析awdb离线库

    使用ruby解析awdb离线库使用ruby解析awdb离线库使用ruby解析awdb离线库使用ruby解析awdb离线库使用ruby解析awdb离线库使用ruby解析awdb离线库使用ruby解析awdb离线库使用ruby解析awdb离线库使用ruby解析awdb离线库...

    Ruby on Rails敏捷开发最佳实践源代码

    Ruby on Rails,简称Rails,是基于Ruby语言的一个开源Web应用框架,它遵循MVC(Model-View-Controller)架构模式,旨在提升开发效率和代码可读性,鼓励使用敏捷开发方法。这个压缩包包含了“Ruby on Rails敏捷开发...

    ruby-使用ruby实现的排序算法-sorting.zip

    本资源"ruby-使用ruby实现的排序算法-sorting.zip"聚焦于如何使用Ruby实现不同的排序算法,这对于Ruby开发者来说是一项重要的技能。下面将详细讨论Ruby中的排序算法及其原理。 1. 内置排序方法 `sort` Ruby提供了...

    cfer, 工具箱和 ruby DSL,用于自动化使用 AWS CloudFormation的基础架构.zip

    cfer, 工具箱和 ruby DSL,用于自动化使用 AWS CloudFormation的基础架构 Cfer Cfer是一个用于管理CloudFormation模板的轻量级工具包。请阅读这里的Cfer 。支持Cfer是 pre-1.0 软件,可能包含 Bug 或者不完整的功能...

    Ruby-TensorStream用Ruby重新实现TensorFlow

    **Ruby-TensorStream:用Ruby重现实现TensorFlow** Ruby-TensorStream是一个开源项目,旨在为Ruby开发者提供一个类似于Google TensorFlow的深度学习框架。它的核心目标是让Ruby程序员能够利用TensorFlow的强大功能...

    orchparty:使用Ruby DSL编写您自己的业务流程配置,使您可以拥有混合,导入和变量

    使用Ruby DSL编写您自己的业务流程配置,使您可以拥有混合,导入和变量。 import "../logging.rb" application 'my-cool-app' do variables do var port : 8080 end service "api" do mix "logging.syslog" ...

    ruby_dsl_talk_companion:Aman King 的演讲“Simple Ruby DSL Techniques”的代码伴侣

    通过学习这个演讲的代码伴侣,开发者可以深入了解Ruby DSL的设计和实现,提高代码的可读性和可维护性,同时也能提升对Ruby元编程的理解。无论是为了简化复杂的配置文件,还是为了创建强大的框架,掌握Ruby DSL都是一...

    ruby-使用ruby实现的算法之冒泡排序.zip

    本资料包“ruby-使用ruby实现的算法之冒泡排序.zip”专注于讲解如何使用Ruby来实现经典的冒泡排序算法,这对于理解排序算法以及提升Ruby编程技能非常有帮助。 冒泡排序是一种基础且直观的排序算法,它通过重复遍历...

    ruby-snmp, SNMP ( 简单网络管理协议)的ruby 实现.zip

    ruby-snmp, SNMP ( 简单网络管理协议)的ruby 实现 用于 ruby的 SNMP库摘要这里库实现 SNMP ( 简单网络管理协议) 。 它在纯 ruby 中实现,因此不依赖于的外部库( 如 ) 。 你可以在 ruby 可以运行的任何地方运行这里库...

    ruby-使用ruby实现的算法之加密解密算法.zip

    本资源包"ruby-使用ruby实现的算法之加密解密算法.zip"显然包含了关于如何在Ruby中实现加密和解密算法的实例和代码。下面将详细介绍Ruby中常用的加密解密方法及相关知识点。 1. **基本概念** - **加密**:是将明文...

    ruby on rails 敏捷开发,3.1 pdf and epub format

    Ruby on Rails,简称Rails,是由David Heinemeier Hansson基于Ruby语言开发的一个开源Web应用程序框架,它遵循敏捷开发的理念,致力于提高开发效率和代码的可读性。在Rails 3.1版本中,引入了许多重要更新和改进,...

    通过Ruby语言实现简易的命令行待办事项(To-Do List)管理器

    压缩包文件代码是通过Ruby语言实现的一个简易的命令行待办事项(To-Do List)管理器。这个管理器将允许用户添加、查看和删除待办事项。 这个Ruby脚本定义了两个类:TodoList 和 Task。TodoList 类管理待办事项的列表...

    botstack, 使用 Ruby on Rails 实现快速聊天.zip

    botstack, 使用 Ruby on Rails 实现快速聊天 botstack 这是创建 FB Chatbots的基础项目。 它拥有一个状态机和用户管理,允许你使用模块添加功能。快速入门将所有逻辑放入 lib/bot 。 我们已经准备好一切为你的项目做...

    Ruby-rubyinstall安装RubyJRubyRubiniusMagLevorMRuby

    在Ruby的世界里,管理不同的Ruby实现(如MRI、JRuby、Rubinius、MagLev和MRuby)是非常重要的,这有助于开发者根据项目需求选择最适合的运行时环境。`ruby-install`就是这样一个工具,它允许用户方便地安装和管理...

    从 Java 到 Ruby_ 每一个管理者应该知道的事情

    5. **项目管理**:采用 Ruby 可能意味着使用敏捷开发方法,如 Scrum 或 Kanban。这将改变项目管理和协作的方式,可能涉及到不同的工具和流程。 6. **性能与稳定性**:虽然 Ruby 在开发速度上可能有优势,但在某些...

    如何用Ruby来实现页面性能测试

    标题中的“如何用Ruby来实现页面性能测试”指的是利用Ruby编程语言进行网页性能评估和监控的一种方法。在描述中提到,作者选择了Ruby而非QTP(QuickTest Professional)是因为Ruby具有优于QTP的独特优点,尤其在资源...

Global site tag (gtag.js) - Google Analytics